// predicates.cpp // // $Id: predicates.cpp,v 1.20 2000/09/17 21:31:20 sdennis Exp $ // #include "copyright.h" #include "autoconf.h" #include "config.h" #include "externs.h" #include <signal.h> #include "mudconf.h" #include "db.h" #include "interface.h" #include "match.h" #include "command.h" #include "alloc.h" #include "attrs.h" #include "powers.h" #include "ansi.h" #include "htab.h" extern int FDECL(do_command, (DESC *, char *, int)); extern void NDECL(dump_database); char * DCL_CDECL tprintf(const char *fmt,...) { static char buff[LBUF_SIZE]; va_list ap; va_start(ap, fmt); Tiny_vsnprintf(buff, LBUF_SIZE, fmt, ap); va_end(ap); return buff; } void DCL_CDECL safe_tprintf_str(char *str, char **bp, char *fmt,...) { va_list ap; va_start(ap, fmt); int nAvailable = LBUF_SIZE - (*bp - str); int len = Tiny_vsnprintf(*bp, nAvailable, fmt, ap); va_end(ap); *bp += len; } /* * --------------------------------------------------------------------------- * * insert_first, remove_first: Insert or remove objects from lists. */ dbref insert_first(dbref head, dbref thing) { s_Next(thing, head); return thing; } dbref remove_first(dbref head, dbref thing) { dbref prev; if (head == thing) return (Next(thing)); DOLIST(prev, head) { if (Next(prev) == thing) { s_Next(prev, Next(thing)); return head; } } return head; } /* * --------------------------------------------------------------------------- * * reverse_list: Reverse the order of members in a list. */ dbref reverse_list(dbref list) { dbref newlist, rest; newlist = NOTHING; while (list != NOTHING) { rest = Next(list); s_Next(list, newlist); newlist = list; list = rest; } return newlist; } /* * --------------------------------------------------------------------------- * * member - indicate if thing is in list */ int member(dbref thing, dbref list) { DOLIST(list, list) { if (list == thing) return 1; } return 0; } /* * --------------------------------------------------------------------------- * * is_integer, is_number: see if string contains just a number. */ int is_integer(char *str, int *pDigits) { int nDigits = 0; if (pDigits) { *pDigits = 0; } // Leading spaces. // while (Tiny_IsSpace[(unsigned char)*str]) str++; // Leading minus // if (*str == '-') { str++; // Just a minus by itself isn't an integer. // if (!*str) return 0; } // Need at least 1 integer // if (!Tiny_IsDigit[(unsigned char)*str]) { return 0; } // The number (int) // while (Tiny_IsDigit[(unsigned char)*str]) { nDigits++; str++; } if (pDigits) { *pDigits = nDigits; } // Trailing Spaces. // while (Tiny_IsSpace[(unsigned char)*str]) str++; return (*str ? 0 : 1); } int is_number(char *str) { int got_one; // Leading spaces. // while (Tiny_IsSpace[(unsigned char)*str]) str++; // Leading minus // if (*str == '-') { str++; // But not if just a minus // if (!*str) return 0; } // Need at least one digit. // got_one = 0; if (Tiny_IsDigit[(unsigned char)*str]) { got_one = 1; } // The number (int) // while (Tiny_IsDigit[(unsigned char)*str]) { str++; } // Decimal point. // if (*str == '.') str++; // Need at least one digit // if (Tiny_IsDigit[(unsigned char)*str]) { got_one = 1; } // The number (fract) // while (Tiny_IsDigit[(unsigned char)*str]) { str++; } // Trailing spaces. // while (Tiny_IsSpace[(unsigned char)*str]) str++; return ((*str || !got_one) ? 0 : 1); } #ifndef STANDALONE int could_doit(dbref player, dbref thing, int locknum) { char *key; dbref aowner; int aflags, doit; /* * no if nonplayer trys to get key */ if (!isPlayer(player) && Key(thing)) { return 0; } if (Pass_Locks(player)) return 1; key = atr_get(thing, locknum, &aowner, &aflags); doit = eval_boolexp_atr(player, thing, thing, key); free_lbuf(key); return doit; } int can_see(dbref player, dbref thing, int can_see_loc) { /* * Don't show if all the following apply: * Sleeping players should * * * * * * not be seen. * The thing is a disconnected player. * The * player * is * * * not a puppet. */ if (mudconf.dark_sleepers && isPlayer(thing) && !Connected(thing) && !Puppet(thing)) { return 0; } /* * You don't see yourself or exits */ if ((player == thing) || isExit(thing)) { return 0; } /* * If loc is not dark, you see it if it's not dark or you control it. * * * * * If loc is dark, you see it if you control it. Seeing your * * own * * * dark objects is controlled by mudconf.see_own_dark. * * In * dark * * locations, you also see things that are LIGHT and * !DARK. */ if (can_see_loc) { return (!Dark(thing) || (mudconf.see_own_dark && MyopicExam(player, thing))); } else { return ((Light(thing) && !Dark(thing)) || (mudconf.see_own_dark && MyopicExam(player, thing))); } } static int pay_quota(dbref who, int cost) { dbref aowner; int quota, aflags; char buf[20], *quota_str; /* * If no cost, succeed */ if (cost <= 0) return 1; /* * determine quota */ quota = Tiny_atol(quota_str = atr_get(Owner(who), A_RQUOTA, &aowner, &aflags)); free_lbuf(quota_str); /* * enough to build? Wizards always have enough. */ quota -= cost; if ((quota < 0) && !Free_Quota(who) && !Free_Quota(Owner(who))) return 0; // Dock the quota. // Tiny_ltoa(quota, buf); atr_add_raw(Owner(who), A_RQUOTA, buf); return 1; } int canpayfees(dbref player, dbref who, int pennies, int quota) { if (!Wizard(who) && !Wizard(Owner(who)) && !Free_Money(who) && !Free_Money(Owner(who)) && (Pennies(Owner(who)) < pennies)) { if (player == who) { notify(player, tprintf("Sorry, you don't have enough %s.", mudconf.many_coins)); } else { notify(player, tprintf("Sorry, that player doesn't have enough %s.", mudconf.many_coins)); } return 0; } if (mudconf.quotas) { if (!pay_quota(who, quota)) { if (player == who) { notify(player, "Sorry, your building contract has run out."); } else { notify(player, "Sorry, that player's building contract has run out."); } return 0; } } payfor(who, pennies); return 1; } int payfor(dbref who, int cost) { dbref tmp; if (Wizard(who) || Wizard(Owner(who)) || Free_Money(who) || Free_Money(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return 1; } who = Owner(who); if ((tmp = Pennies(who)) >= cost) { s_Pennies(who, tmp - cost); return 1; } return 0; } #endif /* * STANDALONE */ void add_quota(dbref who, int payment) { dbref aowner; int aflags; char buf[20], *quota; quota = atr_get(who, A_RQUOTA, &aowner, &aflags); Tiny_ltoa(Tiny_atol(quota) + payment, buf); free_lbuf(quota); atr_add_raw(who, A_RQUOTA, buf); } void giveto(dbref who, int pennies) { if (Wizard(who) || Wizard(Owner(who)) || Free_Money(who) || Free_Money(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return; } who = Owner(who); s_Pennies(who, Pennies(who) + pennies); } // The following function validates that the object names (which will be // used for things, exits, and rooms, but not for players) and generates // a canonical form of that name (with optimized ANSI). // char *MakeCanonicalObjectName(const char *pName, int *pnName, BOOL *pbValid) { static char Buf[MBUF_SIZE]; *pnName = 0; *pbValid = FALSE; if (!pName) { return NULL; } // Build up what the real name would be. If we pass all the // checks, this is what we will return as a result. // int nVisualWidth; int nBuf = ANSI_TruncateToField(pName, sizeof(Buf), Buf, MBUF_SIZE, &nVisualWidth, 0); // Disallow pure ANSI names. There must be at least -something- // visible. // if (nVisualWidth <= 0) { return NULL; } // Get the stripped version (Visible parts without color info). // unsigned int nStripped; char *pStripped = strip_ansi(Buf, &nStripped); // Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE // as the first character, or SPACE as the last character // if ( strchr("*!#", *pStripped) || Tiny_IsSpace[(unsigned char)pStripped[0]] || Tiny_IsSpace[(unsigned char)pStripped[nStripped-1]]) { return NULL; } // Only printable characters besides ARG_DELIMITER, AND_TOKEN, // and OR_TOKEN are allowed. // for (unsigned int i = 0; i < nStripped; i++) { if (!Tiny_IsObjectNameCharacter[(unsigned char)pStripped[i]]) { return NULL; } } // Special names are specifically dis-allowed. // if ( (nStripped == 2 && memcmp("me", pStripped, 2) == 0) || (nStripped == 4 && ( memcmp("home", pStripped, 4) == 0 || memcmp("here", pStripped, 4) == 0))) { return NULL; } *pnName = nBuf; *pbValid = TRUE; return Buf; } // The following function validates the player name. ANSI is not // allowed in player names. However, a player name must satisfy // the requirements of a regular name as well. // BOOL ValidatePlayerName(const char *pName) { if (!pName) { return FALSE; } unsigned int nName = strlen(pName); // Verify that name is not empty, but not too long, either. // if (nName <= 0 || PLAYER_NAME_LIMIT <= nName) { return FALSE; } // Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE // as the first character, or SPACE as the last character // if ( strchr("*!#", *pName) || Tiny_IsSpace[(unsigned char)pName[0]] || Tiny_IsSpace[(unsigned char)pName[nName-1]]) { return FALSE; } #ifndef STANDALONE if (mudconf.name_spaces) { Tiny_IsPlayerNameCharacter[(unsigned char)' '] = 1; } else { Tiny_IsPlayerNameCharacter[(unsigned char)' '] = 0; } #endif // Only printable characters besides ARG_DELIMITER, AND_TOKEN, // and OR_TOKEN are allowed. // for (unsigned int i = 0; i < nName; i++) { if (!Tiny_IsObjectNameCharacter[(unsigned char)pName[i]]) { return FALSE; } } // Special names are specifically dis-allowed. // if ( (nName == 2 && memcmp("me", pName, 2) == 0) || (nName == 4 && ( memcmp("home", pName, 4) == 0 || memcmp("here", pName, 4) == 0))) { return FALSE; } return TRUE; } int ok_password(const char *password, dbref player) { const char *scan; int num_upper = 0; int num_special = 0; int num_lower = 0; if (*password == '\0') { #ifndef STANDALONE notify_quiet(player, "Null passwords are not allowed."); #endif return 0; } for (scan = password; *scan; scan++) { if ( !Tiny_IsPrint[(unsigned char)*scan] || Tiny_IsSpace[(unsigned char)*scan]) { #ifndef STANDALONE notify_quiet(player, "Illegal character in password."); #endif return 0; } if (Tiny_IsUpper[(unsigned char)*scan]) num_upper++; else if (Tiny_IsLower[(unsigned char)*scan]) num_lower++; else if ((*scan != '\'') && (*scan != '-')) num_special++; } // Needed. Change it if you like, but be sure yours is the same. // if ((strlen(password) == 13) && (password[0] == 'X') && (password[1] == 'X')) { #ifndef STANDALONE notify_quiet(player, "Please choose another password."); #endif return 0; } #ifndef STANDALONE if (mudconf.safer_passwords) { if (num_upper < 1) { notify_quiet(player, "The password must contain at least one capital letter."); return 0; } if (num_lower < 1) { notify_quiet(player, "The password must contain at least one lowercase letter."); return 0; } if (num_special < 1) { notify_quiet(player, "The password must contain at least one number or a symbol other than the apostrophe or dash."); return 0; } } #endif // STANDALONE return 1; } #ifndef STANDALONE /* * --------------------------------------------------------------------------- * * handle_ears: Generate the 'grows ears' and 'loses ears' messages. */ void handle_ears(dbref thing, int could_hear, int can_hear) { char *buff, *bp; int gender; static const char *poss[5] = {"", "its", "her", "his", "their"}; if (!could_hear && can_hear) { buff = alloc_lbuf("handle_ears.grow"); StringCopy(buff, Name(thing)); if (isExit(thing)) { for (bp = buff; *bp && (*bp != ';'); bp++) ; *bp = '\0'; } gender = get_gender(thing); notify_check(thing, thing, tprintf("%s grow%s ears and can now hear.", buff, (gender == 4) ? "" : "s"), (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV)); free_lbuf(buff); } else if (could_hear && !can_hear) { buff = alloc_lbuf("handle_ears.lose"); StringCopy(buff, Name(thing)); if (isExit(thing)) { for (bp = buff; *bp && (*bp != ';'); bp++) ; *bp = '\0'; } gender = get_gender(thing); notify_check(thing, thing, tprintf("%s lose%s %s ears and become%s deaf.", buff, (gender == 4) ? "" : "s", poss[gender], (gender == 4) ? "" : "s"), (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV)); free_lbuf(buff); } } /* * for lack of better place the @switch code is here */ void do_switch(dbref player, dbref cause, int key, char *expr, char *args[], int nargs, char *cargs[], int ncargs) { int a, any; char *buff, *bp, *str; if (!expr || (nargs <= 0)) return; if (key == SWITCH_DEFAULT) { if (mudconf.switch_df_all) key = SWITCH_ANY; else key = SWITCH_ONE; } /* * now try a wild card match of buff with stuff in coms */ any = 0; buff = bp = alloc_lbuf("do_switch"); for (a = 0; (a < (nargs - 1)) && args[a] && args[a + 1]; a += 2) { bp = buff; str = args[a]; TinyExec(buff, &bp, 0, player, cause, EV_FCHECK | EV_EVAL | EV_TOP, &str, cargs, ncargs); *bp = '\0'; if (wild_match(buff, expr)) { wait_que(player, cause, 0, NOTHING, 0, args[a + 1], cargs, ncargs, mudstate.global_regs); if (key == SWITCH_ONE) { free_lbuf(buff); return; } any = 1; } } free_lbuf(buff); if ((a < nargs) && !any && args[a]) wait_que(player, cause, 0, NOTHING, 0, args[a], cargs, ncargs, mudstate.global_regs); } void do_addcommand(dbref player, dbref cause, int key, char *name, char *command) { CMDENT *old, *cmd; ADDENT *add, *nextp; dbref thing; int atr; if (!*name) { notify(player, "Sorry."); return; } if (!parse_attrib(player, command, &thing, &atr) || (atr == NOTHING)) { notify(player, "No such attribute."); return; } // Let's make this case insensitive... // _strlwr(name); old = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { /* If it's already found in the hash table, and it's being added using the same object and attribute... */ for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next) { if ((nextp->thing == thing) && (nextp->atr == atr)) { notify(player, tprintf("%s already added.", name)); return; } } // Else tack it on to the existing entry... // add = (ADDENT *)MEMALLOC(sizeof(ADDENT)); ISOUTOFMEMORY(add); add->thing = thing; add->atr = atr; add->name = StringClone(name); add->next = old->addent; old->addent = add; } else { if (old) { /* Delete the old built-in and rename it __name */ hashdeleteLEN(name, strlen(name), &mudstate.command_htab); } cmd = (CMDENT *)MEMALLOC(sizeof(CMDENT)); ISOUTOFMEMORY(cmd); cmd->cmdname = StringClone(name); cmd->switches = NULL; cmd->perms = 0; cmd->extra = 0; if (old && (old->callseq & CS_LEADIN)) { cmd->callseq = CS_ADDED|CS_ONE_ARG|CS_LEADIN; } else { cmd->callseq = CS_ADDED|CS_ONE_ARG; } add = (ADDENT *)MEMALLOC(sizeof(ADDENT)); ISOUTOFMEMORY(add); add->thing = thing; add->atr = atr; add->name = StringClone(name); add->next = NULL; cmd->addent = add; hashaddLEN(name, strlen(name), (int *)cmd, &mudstate.command_htab); if (old) { // Fix any aliases of this command. // hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); char *p = tprintf("__%s", name); hashaddLEN(p, strlen(p), (int *)old, &mudstate.command_htab); } } /* We reset the one letter commands here so you can overload them */ set_prefix_cmds(); notify(player, tprintf("%s added.", name)); } void do_listcommands(dbref player, dbref cause, int key, char *name) { CMDENT *old; ADDENT *nextp; int didit = 0; char *keyname; // Let's make this case insensitive... // _strlwr(name); if (*name) { old = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { /* If it's already found in the hash table, and it's being added using the same object and attribute... */ for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next) { notify(player, tprintf("%s: #%d/%s", nextp->name, nextp->thing, ((ATTR *)atr_num(nextp->atr))->name)); } } else { notify(player, tprintf("%s not found in command table.",name)); } return; } else { int nKeyLength; for (keyname = hash_firstkey(&mudstate.command_htab, &nKeyLength); keyname != NULL; keyname = hash_nextkey(&mudstate.command_htab, &nKeyLength)) { old = (CMDENT *)hashfindLEN(keyname, nKeyLength, &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next) { if (strcmp(keyname, nextp->name)) continue; notify(player, tprintf("%s: #%d/%s", nextp->name, nextp->thing, ((ATTR *)atr_num(nextp->atr))->name)); didit = 1; } } } } if (!didit) notify(player, "No added commands found in command table."); } void do_delcommand(dbref player, dbref cause, int key, char *name, char *command) { CMDENT *old, *cmd; ADDENT *prev = NULL, *nextp; dbref thing = NOTHING; int atr = NOTHING; if (!*name) { notify(player, "Sorry."); return; } if (*command) { if (!parse_attrib(player, command, &thing, &atr) || (atr == NOTHING)) { notify(player, "No such attribute."); return; } } // Let's make this case insensitive... // _strlwr(name); old = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { char *p__Name = tprintf("__%s", name); unsigned int n__Name = strlen(p__Name); unsigned int nName = strlen(name); if (!*command) { for (prev = (ADDENT *)old->handler; prev != NULL; prev = nextp) { nextp = prev->next; /* Delete it! */ MEMFREE(prev->name); MEMFREE(prev); } hashdeleteLEN(name, nName, &mudstate.command_htab); if ((cmd = (CMDENT *)hashfindLEN(p__Name, n__Name, &mudstate.command_htab)) != NULL) { hashdeleteLEN(p__Name, n__Name, &mudstate.command_htab); hashaddLEN(name, nName, (int *)cmd, &mudstate.command_htab); hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); } MEMFREE(old); set_prefix_cmds(); notify(player, "Done."); return; } else { for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next) { if ((nextp->thing == thing) && (nextp->atr == atr)) { /* Delete it! */ MEMFREE(nextp->name); if (!prev) { if (!nextp->next) { hashdeleteLEN(name, nName, &mudstate.command_htab); if ((cmd = (CMDENT *)hashfindLEN(p__Name, n__Name, &mudstate.command_htab)) != NULL) { hashdeleteLEN(p__Name, n__Name, &mudstate.command_htab); hashaddLEN(name, nName, (int *)cmd, &mudstate.command_htab); hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); } MEMFREE(old); } else { old->addent = nextp->next; MEMFREE(nextp); } } else { prev->next = nextp->next; MEMFREE(nextp); } set_prefix_cmds(); notify(player, "Done."); return; } prev = nextp; } notify(player, "Command not found in command table."); } } else { notify(player, "Command not found in command table."); } } /* * @prog 'glues' a user's input to a command. Once executed, the first string * input from any of the doers's logged in descriptors, will go into * A_PROGMSG, which can be substituted in <command> with %0. Commands already * queued by the doer will be processed normally. */ void handle_prog(DESC *d, char *message) { DESC *all; char *cmd; dbref aowner; int aflags, i; /* * Allow the player to pipe a command while in interactive mode. */ if (*message == '|') { do_command(d, message + 1, 1); // Use telnet protocol's GOAHEAD command to show prompt // if (d->program_data != NULL) { queue_string(d, tprintf("%s>%s \377\371", ANSI_HILITE, ANSI_NORMAL)); } return; } cmd = atr_get(d->player, A_PROGCMD, &aowner, &aflags); wait_que(d->program_data->wait_cause, d->player, 0, NOTHING, 0, cmd, (char **)&message, 1, (char **)d->program_data->wait_regs); /* First, set 'all' to a descriptor we find for this player */ all = (DESC *)hashfindLEN(&(d->player), sizeof(d->player), &mudstate.desc_htab) ; if (all && all->program_data) { for (i = 0; i < MAX_GLOBAL_REGS; i++) { if (all->program_data->wait_regs[i]) { free_lbuf(all->program_data->wait_regs[i]); all->program_data->wait_regs[i] = NULL; } } MEMFREE(all->program_data); all->program_data = NULL; // Set info for all player descriptors to NULL // DESC_ITER_PLAYER(d->player, all) all->program_data = NULL; } atr_clr(d->player, A_PROGCMD); free_lbuf(cmd); } void do_quitprog(dbref player, dbref cause, int key, char *name) { DESC *d; dbref doer; int i, isprog = 0; if (*name) { doer = match_thing(player, name); } else { doer = player; } if (!(Prog(player) || Prog(Owner(player))) && (player != doer)) { notify(player, "Permission denied."); return; } if (!isPlayer(doer) || !Good_obj(doer)) { notify(player, "That is not a player."); return; } if (!Connected(doer)) { notify(player, "That player is not connected."); return; } DESC_ITER_PLAYER(doer, d) { if (d->program_data != NULL) { isprog = 1; } } if (!isprog) { notify(player, "Player is not in an @program."); return; } d = (DESC *)hashfindLEN(&doer, sizeof(doer), &mudstate.desc_htab) ; if (d && d->program_data) { for (i = 0; i < MAX_GLOBAL_REGS; i++) { if (d->program_data->wait_regs[i]) { free_lbuf(d->program_data->wait_regs[i]); d->program_data->wait_regs[i] = NULL; } } MEMFREE(d->program_data); d->program_data = NULL; /* Set info for all player descriptors to NULL */ DESC_ITER_PLAYER(doer, d) d->program_data = NULL; } atr_clr(doer, A_PROGCMD); notify(player, "@program cleared."); notify(doer, "Your @program has been terminated."); } void do_prog(dbref player, dbref cause, int key, char *name, char *command) { DESC *d; PROG *program; int i, atr, aflags; dbref doer, thing, aowner; ATTR *ap; char *attrib, *msg; if (!name || !*name) { notify(player, "No players specified."); return; } doer = match_thing(player, name); if (!(Prog(player) || Prog(Owner(player))) && (player != doer)) { notify(player, NOPERM_MESSAGE); return; } if (!isPlayer(doer) || !Good_obj(doer)) { notify(player, "That is not a player."); return; } if (!Connected(doer)) { notify(player, "That player is not connected."); return; } msg = command; attrib = parse_to(&msg, ':', 1); if (msg && *msg) { notify(doer, msg); } parse_attrib(player, attrib, &thing, &atr); if (atr != NOTHING) { char *pBuffer = atr_get(thing, atr, &aowner, &aflags); if (*pBuffer) { ap = atr_num(atr); if ( God(player) || ( !God(thing) && See_attr(player, thing, ap, aowner, aflags) && (Wizard(player) || (aowner == Owner(player))))) { atr_add_raw(doer, A_PROGCMD, pBuffer); } else { notify(player, NOPERM_MESSAGE); free_lbuf(pBuffer); return; } free_lbuf(pBuffer); } else { notify(player, "Attribute not present on object."); return; } } else { notify(player, "No such attribute."); return; } // Check to see if the cause already has an @prog input pending. // DESC_ITER_PLAYER(doer, d) { if (d->program_data != NULL) { notify(player, "Input already pending."); return; } } program = (PROG *)MEMALLOC(sizeof(PROG)); ISOUTOFMEMORY(program); program->wait_cause = player; for (i = 0; i < MAX_GLOBAL_REGS; i++) { program->wait_regs[i] = alloc_lbuf("prog_regs"); memcpy(program->wait_regs[i], mudstate.global_regs[i], mudstate.glob_reg_len[i]+1); } // Now, start waiting. // DESC_ITER_PLAYER(doer, d) { d->program_data = program; // Use telnet protocol's GOAHEAD command to show prompt. // queue_string(d, tprintf("%s>%s \377\371", ANSI_HILITE, ANSI_NORMAL)); } } /* * --------------------------------------------------------------------------- * * do_restart: Restarts the game. */ void do_restart(dbref player, dbref cause, int key) { #ifndef WIN32 if (mudstate.dumping) { notify(player, "Dumping. Please try again later."); return; } #endif // WIN32 raw_broadcast(0, "Game: Restart by %s, please wait.", Name(Owner(player))); STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT"); log_text((char *)"Restart by "); log_name(player); ENDLOG; SYNC; dump_database_internal(DUMP_I_RESTART); CLOSE; #ifdef WIN32 // WIN32 WSACleanup(); exit(12345678); #else // WIN32 extern int slave_pid; extern SOCKET slave_socket; shutdown(slave_socket, SD_BOTH); close(slave_socket); slave_socket = INVALID_SOCKET; if (slave_pid > 0) { kill(slave_pid, SIGKILL); } slave_pid = 0; dump_restart_db(); #ifdef GAME_DOOFERMUX execl("bin/netmux", mudconf.mud_name, mudconf.config_file, NULL); #else execl("bin/netmux", "netmux", mudconf.config_file, NULL); #endif // GAME_DOOFERMUX #endif // !WIN32 } /* * --------------------------------------------------------------------------- * * do_comment: Implement the @@ (comment) command. Very cpu-intensive :-) */ void do_comment(dbref player, dbref cause, int key) { } static dbref promote_dflt(dbref old, dbref new0) { switch (new0) { case NOPERM: return NOPERM; case AMBIGUOUS: if (old == NOPERM) return old; else return new0; } if ((old == NOPERM) || (old == AMBIGUOUS)) return old; return NOTHING; } dbref match_possessed(dbref player, dbref thing, char *target, dbref dflt, int check_enter) { dbref result, result1; int control; char *buff, *start, *place, *s1, *d1, *temp; /* * First, check normally */ if (Good_obj(dflt)) return dflt; /* * Didn't find it directly. Recursively do a contents check */ start = target; while (*target) { /* * Fail if no ' characters */ place = target; target = (char *)strchr(place, '\''); if ((target == NULL) || !*target) return dflt; /* * If string started with a ', skip past it */ if (place == target) { target++; continue; } /* * If next character is not an s or a space, skip past */ temp = target++; if (!*target) return dflt; if ((*target != 's') && (*target != 'S') && (*target != ' ')) continue; /* * If character was not a space make sure the following * * * * * * character is a space. */ if (*target != ' ') { target++; if (!*target) return dflt; if (*target != ' ') continue; } /* * Copy the container name to a new buffer so we can * * * * * terminate it. */ buff = alloc_lbuf("is_posess"); for (s1 = start, d1 = buff; *s1 && (s1 < temp); *d1++ = (*s1++)) ; *d1 = '\0'; /* * Look for the container here and in our inventory. Skip * * * * * past if we can't find it. */ init_match(thing, buff, NOTYPE); if (player == thing) { match_neighbor(); match_possession(); } else { match_possession(); } result1 = match_result(); free_lbuf(buff); if (!Good_obj(result1)) { dflt = promote_dflt(dflt, result1); continue; } /* * If we don't control it and it is either dark or opaque, * * * * * skip past. */ control = Controls(player, result1); if ((Dark(result1) || Opaque(result1)) && !control) { dflt = promote_dflt(dflt, NOTHING); continue; } /* * Validate object has the ENTER bit set, if requested */ if ((check_enter) && !Enter_ok(result1) && !control) { dflt = promote_dflt(dflt, NOPERM); continue; } /* * Look for the object in the container */ init_match(result1, target, NOTYPE); match_possession(); result = match_result(); result = match_possessed(player, result1, target, result, check_enter); if (Good_obj(result)) return result; dflt = promote_dflt(dflt, result); } return dflt; } /* * --------------------------------------------------------------------------- * * parse_range: break up <what>,<low>,<high> syntax */ void parse_range(char **name, dbref *low_bound, dbref *high_bound) { char *buff1, *buff2; buff1 = *name; if (buff1 && *buff1) *name = parse_to(&buff1, ',', EV_STRIP_TS); if (buff1 && *buff1) { buff2 = parse_to(&buff1, ',', EV_STRIP_TS); if (buff1 && *buff1) { while (Tiny_IsSpace[(unsigned char)*buff1]) buff1++; if (*buff1 == NUMBER_TOKEN) buff1++; *high_bound = Tiny_atol(buff1); if (*high_bound >= mudstate.db_top) *high_bound = mudstate.db_top - 1; } else { *high_bound = mudstate.db_top - 1; } while (Tiny_IsSpace[(unsigned char)*buff2]) buff2++; if (*buff2 == NUMBER_TOKEN) buff2++; *low_bound = Tiny_atol(buff2); if (*low_bound < 0) *low_bound = 0; } else { *low_bound = 0; *high_bound = mudstate.db_top - 1; } } int parse_thing_slash(dbref player, char *thing, char **after, dbref *it) { char *str; /* * get name up to / */ for (str = thing; *str && (*str != '/'); str++) ; /* * If no / in string, return failure */ if (!*str) { *after = NULL; *it = NOTHING; return 0; } *str++ = '\0'; *after = str; /* * Look for the object */ init_match(player, thing, NOTYPE); match_everything(MAT_EXIT_PARENTS); *it = match_result(); /* * Return status of search */ return (Good_obj(*it)); } extern NAMETAB lock_sw[]; int get_obj_and_lock(dbref player, char *what, dbref *it, ATTR **attr, char *errmsg, char **bufc) { char *str, *tbuf; int anum; tbuf = alloc_lbuf("get_obj_and_lock"); StringCopy(tbuf, what); if (parse_thing_slash(player, tbuf, &str, it)) { /* * <obj>/<lock> syntax, use the named lock */ anum = search_nametab(player, lock_sw, str); if (anum < 0) { free_lbuf(tbuf); safe_str("#-1 LOCK NOT FOUND", errmsg, bufc); return 0; } } else { /* * Not <obj>/<lock>, do a normal get of the default lock */ *it = match_thing(player, what); if (!Good_obj(*it)) { free_lbuf(tbuf); safe_str("#-1 NOT FOUND", errmsg, bufc); return 0; } anum = A_LOCK; } /* * Get the attribute definition, fail if not found */ free_lbuf(tbuf); *attr = atr_num(anum); if (!(*attr)) { safe_str("#-1 LOCK NOT FOUND", errmsg, bufc); return 0; } return 1; } #endif /* * STANDALONE */ /* * --------------------------------------------------------------------------- * * where_is: Returns place where obj is linked into a list. * * ie. location for players/things, source for exits, NOTHING for rooms. */ dbref where_is(dbref what) { dbref loc; if (!Good_obj(what)) return NOTHING; switch (Typeof(what)) { case TYPE_PLAYER: case TYPE_THING: loc = Location(what); break; case TYPE_EXIT: loc = Exits(what); break; default: loc = NOTHING; break; } return loc; } /* * --------------------------------------------------------------------------- * * where_room: Return room containing player, or NOTHING if no room or * * recursion exceeded. If player is a room, returns itself. */ dbref where_room(dbref what) { int count; for (count = mudconf.ntfy_nest_lim; count > 0; count--) { if (!Good_obj(what)) break; if (isRoom(what)) return what; if (!Has_location(what)) break; what = Location(what); } return NOTHING; } int locatable(dbref player, dbref it, dbref cause) { dbref loc_it, room_it; int findable_room; /* * No sense if trying to locate a bad object */ if (!Good_obj(it)) return 0; loc_it = where_is(it); /* * Succeed if we can examine the target, if we are the target, * if * * * * * * we can examine the location, if a wizard caused the lookup, * * or * * * if the target caused the lookup. */ if (Examinable(player, it) || Find_Unfindable(player) || (loc_it == player) || ((loc_it != NOTHING) && (Examinable(player, loc_it) || loc_it == where_is(player))) || Wizard(cause) || (it == cause)) return 1; room_it = where_room(it); if (Good_obj(room_it)) findable_room = !Hideout(room_it); else findable_room = 1; /* * Succeed if we control the containing room or if the target is * * * * * findable and the containing room is not unfindable. */ if (((room_it != NOTHING) && Examinable(player, room_it)) || Find_Unfindable(player) || (Findable(it) && findable_room)) return 1; /* * We can't do it. */ return 0; } /* * --------------------------------------------------------------------------- * * nearby: Check if thing is nearby player (in inventory, in same room, or * * IS the room. */ int nearby(dbref player, dbref thing) { int thing_loc, player_loc; if (!Good_obj(player) || !Good_obj(thing)) return 0; thing_loc = where_is(thing); if (thing_loc == player) return 1; player_loc = where_is(player); if ((thing_loc == player_loc) || (thing == player_loc)) return 1; return 0; } /* * --------------------------------------------------------------------------- * * exit_visible, exit_displayable: Is exit visible? */ int exit_visible(dbref exit, dbref player, int key) { #if defined(WOD_REALMS) && !defined(STANDALONE) int iRealmDirective = DoThingToThingVisibility(player, exit, ACTION_IS_STATIONARY); if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective) { return 0; } #endif // Exam exit's location // if (key & VE_LOC_XAM) return 1; // Exam exit // if (Examinable(player, exit)) return 1; // Exit is light // if (Light(exit)) return 1; // Dark location or base // if (key & (VE_LOC_DARK | VE_BASE_DARK)) return 0; // Dark exit // if (Dark(exit)) return 0; // Default // return 1; } // Exit visible to look // int exit_displayable(dbref exit, dbref player, int key) { // Dark exit // if (Dark(exit)) return 0; #if defined(WOD_REALMS) && !defined(STANDALONE) int iRealmDirective = DoThingToThingVisibility(player, exit, ACTION_IS_STATIONARY); if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective) { return 0; } #endif // Light exit // if (Light(exit)) return 1; // Dark location or base. // if (key & (VE_LOC_DARK | VE_BASE_DARK)) return 0; // Default // return 1; } /* * --------------------------------------------------------------------------- * * next_exit: return next exit that is ok to see. */ dbref next_exit(dbref player, dbref this0, int exam_here) { if (isRoom(this0)) return NOTHING; if (isExit(this0) && exam_here) return this0; while ((this0 != NOTHING) && Dark(this0) && !Light(this0) && !Examinable(player, this0)) this0 = Next(this0); return this0; } #ifndef STANDALONE /* * --------------------------------------------------------------------------- * * did_it: Have player do something to/with thing */ void did_it(dbref player, dbref thing, int what, const char *def, int owhat, const char *odef, int awhat, char *args[], int nargs) { char *d, *buff, *act, *charges, *bp, *str; dbref loc, aowner; int num, aflags; char *preserve[MAX_GLOBAL_REGS]; int preserve_len[MAX_GLOBAL_REGS]; // If we need to call exec() from within this function, we first save // the state of the global registers, in order to avoid munging them // inappropriately. Do note that the restoration to their original // values occurs BEFORE the execution of the @a-attribute. Therefore, // any changing of setq() values done in the @-attribute and @o-attribute // will NOT be passed on. This prevents odd behaviors that result from // odd @verbs and so forth (the idea is to preserve the caller's control // of the global register values). // int need_pres = 0; // message to player. // if (what > 0) { d = atr_pget(thing, what, &aowner, &aflags); if (*d) { need_pres = 1; save_global_regs("did_it_save", preserve, preserve_len); buff = bp = alloc_lbuf("did_it.1"); str = d; TinyExec(buff, &bp, 0, thing, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs); *bp = '\0'; if (what == A_HTDESC) { safe_str("\r\n", buff, &bp); *bp = '\0'; notify_html(player, buff); } else notify(player, buff); free_lbuf(buff); } else if (def) { notify(player, def); } free_lbuf(d); } else if ((what < 0) && def) { notify(player, def); } /* * message to neighbors */ if ((owhat > 0) && Has_location(player) && Good_obj(loc = Location(player))) { d = atr_pget(thing, owhat, &aowner, &aflags); if (*d) { if (!need_pres) { need_pres = 1; save_global_regs("did_it_save", preserve, preserve_len); } buff = bp = alloc_lbuf("did_it.2"); str = d; TinyExec(buff, &bp, 0, thing, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs); *bp = '\0'; if (*buff) { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), buff)); } free_lbuf(buff); } else if (odef) { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef)); } free_lbuf(d); } else if ((owhat < 0) && odef && Has_location(player) && Good_obj(loc = Location(player))) { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef)); } // If we preserved the state of the global registers, restore them. // if (need_pres) { restore_global_regs("did_it_restore", preserve, preserve_len); } // do the action attribute. // if (awhat > 0) { if (*(act = atr_pget(thing, awhat, &aowner, &aflags))) { charges = atr_pget(thing, A_CHARGES, &aowner, &aflags); if (*charges) { num = Tiny_atol(charges); if (num > 0) { buff = alloc_sbuf("did_it.charges"); Tiny_ltoa(num-1, buff); atr_add_raw(thing, A_CHARGES, buff); free_sbuf(buff); } else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags))) { free_lbuf(act); act = buff; } else { free_lbuf(act); free_lbuf(buff); free_lbuf(charges); return; } } free_lbuf(charges); wait_que(thing, player, 0, NOTHING, 0, act, args, nargs, mudstate.global_regs); } free_lbuf(act); } } /* * --------------------------------------------------------------------------- * * do_verb: Command interface to did_it. */ void do_verb(dbref player, dbref cause, int key, char *victim_str, char *args[], int nargs) { dbref actor, victim; dbref aowner = NOTHING; int what, owhat, awhat, nxargs, restriction; int aflags = NOTHING; ATTR *ap; const char *whatd, *owhatd; char *xargs[10]; // Look for the victim. // if (!victim_str || !*victim_str) { notify(player, "Nothing to do."); return; } // Get the victim. // init_match(player, victim_str, NOTYPE); match_everything(MAT_EXIT_PARENTS); victim = noisy_match_result(); if (!Good_obj(victim)) { return; } // Get the actor. Default is my cause. // if ((nargs >= 1) && args[0] && *args[0]) { init_match(player, args[0], NOTYPE); match_everything(MAT_EXIT_PARENTS); actor = noisy_match_result(); if (!Good_obj(actor)) { return; } } else { actor = cause; } // Check permissions. There are two possibilities: // // 1. Player controls both victim and actor. In this case, // victim runs his action list. // // 2. Player controls actor. In this case victim does not run // his action list and any attributes that player cannot read // from victim are defaulted. // if (!controls(player, actor)) { notify_quiet(player, "Permission denied,"); return; } restriction = !controls(player, victim); what = -1; owhat = -1; awhat = -1; whatd = NULL; owhatd = NULL; nxargs = 0; // Get invoker message attribute. // if (nargs >= 2) { ap = atr_str(args[1]); if (ap && (ap->number > 0)) { what = ap->number; } } // Get invoker message default. // if ((nargs >= 3) && args[2] && *args[2]) { whatd = args[2]; } // Get others message attribute. // if (nargs >= 4) { ap = atr_str(args[3]); if (ap && (ap->number > 0)) { owhat = ap->number; } } // Get others message default. // if ((nargs >= 5) && args[4] && *args[4]) { owhatd = args[4]; } // Get action attribute. // if (nargs >= 6) { ap = atr_str(args[5]); if (ap) { awhat = ap->number; } } // Get arguments. // if (nargs >= 7) { parse_arglist(victim, actor, args[6], '\0', EV_STRIP_LS | EV_STRIP_TS, xargs, 10, (char **)NULL, 0, &nxargs); } // If player doesn't control both, enforce visibility restrictions. // if (restriction) { ap = NULL; if (what != -1) { atr_get_info(victim, what, &aowner, &aflags); ap = atr_num(what); } if (!ap || !Read_attr(player, victim, ap, aowner, aflags) || ((ap->number == A_DESC) && !mudconf.read_rem_desc && !Examinable(player, victim) && !nearby(player, victim))) { what = -1; } ap = NULL; if (owhat != -1) { atr_get_info(victim, owhat, &aowner, &aflags); ap = atr_num(owhat); } if (!ap || !Read_attr(player, victim, ap, aowner, aflags) || ((ap->number == A_DESC) && !mudconf.read_rem_desc && !Examinable(player, victim) && !nearby(player, victim))) { owhat = -1; } awhat = 0; } // Go do it. // did_it(actor, victim, what, whatd, owhat, owhatd, awhat, xargs, nxargs); // Free user args. // for (int i = 0; i < nxargs; i++) { free_lbuf(xargs[i]); } } #endif // STANDALONE // -------------------------------------------------------------------------- // OutOfMemory: handle an out of memory condition. // BOOL OutOfMemory(const char *SourceFile, unsigned int LineNo) { Log.printf("%s(%u): Out of memory.\n", SourceFile, LineNo); Log.Flush(); #ifdef STANDALONE abort(); #else // STANDALONE if (mudstate.initializing) { abort(); } else { do_restart(1,1,0); } #endif // STANDALONE return TRUE; } // -------------------------------------------------------------------------- // AssertionFailed: A logical assertion has failed. // BOOL AssertionFailed(const char *SourceFile, unsigned int LineNo) { Log.printf("%s(%u): Assertion failed.\n", SourceFile, LineNo); Log.Flush(); #ifdef STANDALONE abort(); #else // STANDALONE if (mudstate.initializing) { abort(); } else { do_restart(1,1,0); } #endif // STANDALONE return FALSE; }