#include #include "db.h" #include "externs.h" #include "strings.h" #include "property.h" /* We have to guard against recycle() calling enter_room() and thus running a program which recycles something unexpectedly. So MUF RECYCLE operations go through muf_recycle instead, which queues nested recycles up until earlier calls are done. */ typedef struct pending PENDING; struct pending { PENDING *link; dbref player; dbref thing; } ; static PENDING *pending = 0; static int recycling = 0; void muf_recycle(dbref player, dbref thing) { if (recycling) { PENDING *t; t = malloc(sizeof(PENDING)); t->link = pending; pending = t; t->player = player; t->thing = thing; return; } recycle(player,thing,0); } /* * Return value: -1, 0, or 1. * -1 means "lock replaced with updated lock" * 0 means "lock becomes trivially false" * 1 means "lock becomes trivially true" * If 0 or 1 is returned, the *b will have been set to 0 (=TRUE_BOOLEXP). * * There is a problem here: if, say, #6 is locked to #7, and #8 is * recycled, then #6 will be on #7's backlocks list, even though it * will not in fact be locked to #7 any longer. This can cause * trouble if #6's lock becomes TRUE_BOOLEXP as a result, and #7 is * then recycled, which will call us to clean up #6's lock ('cause * #7's backlocks list includes #6). *b will be a nil pointer in this * circumstance, so we check and just punt if we're called with a nil * pointer. This is not the right fix, but it avoids coredumping. */ static int recycle_backlocks(dbref d, struct boolexp **b) { struct boolexp *temp; int left; int right; if (*b == TRUE_BOOLEXP) return(1); switch ((*b)->type) { case BOOLEXP_AND: left = recycle_backlocks(d,&(*b)->u.dyad.sub1); right = recycle_backlocks(d,&(*b)->u.dyad.sub2); if (left != -1) { if ((right == -1) && (left == 1)) { temp = (*b)->u.dyad.sub2; free(*b); *b = temp; return(-1); } free_boolexp(*b); *b = 0; return(left&&right); } if (! right) { free_boolexp(*b); *b = 0; return(0); } if (right == 1) { temp = (*b)->u.dyad.sub1; free(*b); *b = temp; return(-1); } break; case BOOLEXP_OR: left = recycle_backlocks(d,&(*b)->u.dyad.sub1); right = recycle_backlocks(d,&(*b)->u.dyad.sub2); if (left != -1) { if (right == -1) { if (left == 1) { free_boolexp(*b); *b = 0; return(1); } temp = (*b)->u.dyad.sub2; free(*b); *b = temp; return(-1); } free(*b); *b = 0; return(left||right); } if (right != -1) { if (! right) { temp = (*b)->u.dyad.sub1; free(*b); *b = temp; return(-1); } free_boolexp(*b); *b = 0; return(1); } break; case BOOLEXP_NOT: left = recycle_backlocks(d,&(*b)->u.monad); if (left != -1) { free(*b); *b = 0; return(!left); } break; case BOOLEXP_OBJ: if ((*b)->u.obj.thing == d) { free((*b)->u.obj.param); free(*b); *b = 0; return(0); } break; case BOOLEXP_PROP: break; } return(-1); } void recycle(dbref player, dbref thing, int ignorekeep) { char buf[BUFFER_LEN]; dbref first; dbref rest; struct dbref_list *list; struct dbref_list *temp; int i; int j; PENDING *tpend; if (! ignorekeep) { switch (Typeof(thing)) { case TYPE_EXIT: case TYPE_ROOM: case TYPE_THING: case TYPE_PROGRAM: if (FLAGS(thing) & KEEP) panic("recycling Keep object"); break; } } recycling ++; while (1) { for (list=DBFETCH(thing)->backlocks;list;list=list->next) { rest = list->object; if (! recycle_backlocks(thing,&DBFETCH(rest)->key)) { struct boolexp *tbx; tbx = malloc(sizeof(struct boolexp)); tbx->type = BOOLEXP_OBJ; tbx->u.obj.thing = rest; tbx->u.obj.param = 0; DBSTORE(rest,key,tbx); } } for (list=DBFETCH(thing)->backlinks;list;list=list->next) { rest = list->object; if (rest == HOME) rest = DBFETCH(OWNER(thing))->sp.player.home; switch (Typeof(rest)) { case TYPE_ROOM: DBFETCH(rest)->sp.room.dropto = NOTHING; break; case TYPE_THING: if (DBFETCH(player)->sp.player.home == thing) { DBSTORE(player,sp.player.home,PLAYER_START); } else { DBSTORE(rest,sp.thing.home,DBFETCH(player)->sp.player.home); } add_backlink(rest,DBFETCH(rest)->sp.thing.home); break; case TYPE_EXIT: for (i=j=0;isp.exit.ndest;i++) { if (DBFETCH(rest)->sp.exit.dest[i] != thing) { DBFETCH(rest)->sp.exit.dest[j++] = DBFETCH(rest)->sp.exit.dest[i]; } } if (j < DBFETCH(rest)->sp.exit.ndest) { if (! Wizard(player)) add_pennies(player,LINK_COST,"rec #%d backlink",(int)thing); DBSTORE(rest,sp.exit.ndest,j); } break; case TYPE_PLAYER: DBSTORE(rest,sp.player.home,PLAYER_START); add_backlink(rest,DBFETCH(rest)->sp.player.home); break; } } for (list=sort_dbref_list(DBFETCH(thing)->backprops);list;list=list->next) { if (list->next && (list->object == list->next->object)) continue; fry_dbref_props(DBFETCH(list->object)->properties,thing); } switch (Typeof(thing)) { case TYPE_ROOM: notify_except(player,thing,NOTHING,"You feel a wrenching sensation..."); remove_backlinks(thing,DBFETCH(thing)->sp.room.dropto); if (!Wizard(OWNER(thing))) { add_pennies(OWNER(thing),ROOM_COST,"rec #%d room",(int)thing); } for (first=DBFETCH(thing)->sp.room.exits;first!=NOTHING;first=rest) { rest = DBFETCH(first)->next; if ( (DBFETCH(first)->location == NOTHING) || (DBFETCH(first)->location == thing) ) { recycle(player,first,1); } } /* do players last, in case a player is homed to a subroom */ for (first=DBFETCH(thing)->contents;first!=NOTHING;first=rest) { rest = DBFETCH(first)->next; if (Typeof(first) != TYPE_PLAYER) moveto(first,HOME); } for (first=DBFETCH(thing)->contents;first!=NOTHING;first=rest) { rest = DBFETCH(first)->next; enter_room(first,HOME,DBFETCH(thing)->location); } break; case TYPE_THING: remove_backlinks(thing,DBFETCH(thing)->sp.thing.home); if (!Wizard(OWNER(thing))) { add_pennies(OWNER(thing),DBFETCH(thing)->sp.thing.value,"rec #%d thing",(int)thing); } for (first=DBFETCH(thing)->sp.thing.actions;first!=NOTHING;first=rest) { rest = DBFETCH(first)->next; if ( (DBFETCH(first)->location == NOTHING) || (DBFETCH(first)->location == thing) ) { recycle(player,first,1); } } break; case TYPE_EXIT: for (i=0;isp.exit.ndest;i++) { remove_backlinks(thing,DBFETCH(thing)->sp.exit.dest[i]); } remove_backlocks_parse(thing,DBFETCH(thing)->key); free_boolexp(DBFETCH(thing)->key); DBSTORE(thing,key,TRUE_BOOLEXP); if (! Wizard(OWNER(thing))) { add_pennies(OWNER(thing),EXIT_COST,"rec #%d exit",(int)thing); } if ((DBFETCH(thing)->sp.exit.ndest != 0) && !Wizard(OWNER(thing))) { add_pennies(OWNER(thing),LINK_COST,"rec #%d link",(int)thing); } break; case TYPE_PROGRAM: for (list=DBFETCH(thing)->sp.program.proglocks;list;list=temp) { temp = list->next; sprintf(buf,"This program has been recycled by %s:",unparse_object(list->object,player)); strcat(buf,unparse_object(list->object,thing)); notify(list->object,buf); switch (Typeof(list->object)) { case TYPE_PLAYER: switch (DBFETCH(list->object)->sp.player.proglock_type) { case PROGLOCK_READ: prog_abort(list->object); break; case PROGLOCK_WRITE: FLAGS(list->object) &= ~INTERACTIVE; DBSTORE(list->object,sp.player.curr_prog,NOTHING); DBDIRTY(list->object); remove_proglock(list->object,thing); } break; case TYPE_DAEMON: sprintf(buf,"This program has been recycled by %s:",unparse_object(list->object,player)); strcat(buf,unparse_object(list->object,thing)); notify(list->object,buf); if (list->object == player) { panic("Daemon tried to recycle its own program"); } remove_daemon(list->object); break; } } sprintf(buf,"muf/%d.m",(int)thing); unlink(buf); break; } remove_backlocks_parse(thing,DBFETCH(thing)->key); free_boolexp(DBFETCH(thing)->key); DBSTORE(thing,key,TRUE_BOOLEXP); moveto(thing,NOTHING); db_free_object(thing); db_clear_object(thing); DBSTORE(thing,name,dup_string("")); DBSTORE(thing,description,store_str("")); FLAGS(thing) = TYPE_GARBAGE; remove_ownerlist(thing); DBFETCH(OWNER(thing))->sp.player.dbrefs_owned --; DBDIRTY(OWNER(thing)); OWNER(thing) = NOTHING; DBSTORE(thing,nextowned,NOTHING); DBSTORE(thing,serial,DBFETCH(thing)->serial+1); add_to_recyclable(thing); DBDIRTY(thing); if (!pending || (recycling > 1)) break; tpend = pending; pending = tpend->link; player = tpend->player; thing = tpend->thing; free(tpend); } recycling --; }