/* destroy.c */
/* $Id: destroy.c,v 1.8 1993/09/18 19:03:56 nils Exp $ */
#include <stdio.h>
#include <ctype.h>
#include "db.h"
#include "config.h"
#include "externs.h"
#ifdef DESTROY
dbref first_free=NOTHING;
static void dbmark P((dbref));
static void dbunmark P((void));
static void dbmark1 P((void));
static void dbunmark1 P((void));
static void dbmark2 P((void));
static void mark_float P((void));
/* things must be completely dead before using this.. use do_empty first. */
static void free_object(obj)
dbref obj;
{
db[obj].next=first_free;
first_free=obj;
}
#define CHECK_REF(thing) if((thing)<-3 || (thing)>=db_top || ((thing)>=-1 && IS_GONE(thing)))
/*#define CHECK_REF(a) if ((((a)>-1) && (db[a].flags & GOING)) || (a>=db_top)\
|| (a<-3))*/
/* check for free list corruption */
#define NOT_OK(thing)\
((db[thing].location!=NOTHING) || ((db[thing].owner!=1) && (db[thing].owner != GOD)) ||\
((db[thing].flags & ~ACCESSED)!=(TYPE_THING | GOING)))
/* return a cleaned up object off the free list or NOTHING */
dbref free_get()
{
dbref newobj;
if (first_free==NOTHING)
return(NOTHING);
newobj=first_free;
first_free=db[first_free].next;
/* Make sure this object really should be in free list */
if (NOT_OK(newobj))
{
static nrecur=0;
if (nrecur++==20)
{
first_free=NOTHING;
report();
log_error("Removed free list and continued");
return(NOTHING);
}
report();
log_error(tprintf("Object #%d shouldn't free, fixing free list",newobj));
fix_free_list();
nrecur--;
return(free_get());
}
/* free object name */
SET(db[newobj].name,NULL);
return(newobj);
}
int object_cost(thing)
dbref thing;
{
switch(Typeof(thing)) {
case TYPE_THING:
return OBJECT_DEPOSIT(Pennies(thing));
case TYPE_ROOM:
return ROOM_COST;
case TYPE_EXIT:
if (db[thing].link != NOTHING)
return EXIT_COST;
else
return EXIT_COST + LINK_COST;
case TYPE_PLAYER:
return 1000;
default:
log_error(tprintf("Illegal object type: %d, object_cost", Typeof(thing)));
return 5000;
}
}
/* go through and rebuild the free list */
void fix_free_list()
{
dbref thing;
char *ch;
first_free=NOTHING;
/* destroy all rooms+make sure everything else is really dead */
for(thing=0;thing<db_top;thing++)
if (IS_DOOMED(thing)) {
if ((atol(ch=atr_get(thing,A_DOOMSDAY)) < time(NULL)) && (atol(ch) > 0))
do_empty(thing);
} else
/* if something other than room make sure it is located in NOTHING
otherwise undelete it, needed incase @tel used on object */
if (NOT_OK(thing))
db[thing].flags&=~GOING;
first_free=NOTHING;
/* check for references to destroyed objects */
for(thing=db_top-1;thing>=0;thing--)
/* if object is alive make sure it doesn't refer to any dead objects */
if (!IS_GONE(thing)) {
CHECK_REF(db[thing].exits)
switch(Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
case TYPE_ROOM: { /* yuck probably corrupted set to nothing */
log_error(tprintf("Dead exit in exit list (first) for room #%d: %d",thing, db[thing].exits));
report();
db[thing].exits=NOTHING;
}
}
CHECK_REF(db[thing].zone)
switch(Typeof(thing)) {
case TYPE_ROOM:
log_error(tprintf("Zone for #%d is #%d! setting it to the global zone.", thing, db[thing].zone));
db[thing].zone = db[0].zone;
break;
}
CHECK_REF(db[thing].link)
switch(Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
db[thing].link=PLAYER_START;
break;
case TYPE_EXIT:
case TYPE_ROOM:
db[thing].link=NOTHING;
break;
}
CHECK_REF(db[thing].location)
switch(Typeof(thing)) {
case TYPE_PLAYER: /* this case shouldn't happen but just incase */
case TYPE_THING:
db[thing].location=NOTHING;
moveto(thing,PLAYER_START);
break;
case TYPE_EXIT:
db[thing].location=NOTHING;
destroy_obj(thing, atoi(BAD_OBJECT_DOOMSDAY));
break;
case TYPE_ROOM:
db[thing].location = thing; /* rooms are in themselves */
break;
}
if (((db[thing].next<0) || (db[thing].next>=db_top)) && (db[thing].next!=NOTHING)) {
log_error(tprintf("Invalid next pointer from object %s(%d)",db[thing].name,
thing));
report();
db[thing].next=NOTHING;
}
if ((db[thing].owner<0) || (db[thing].owner>=db_top) || Typeof(db[thing].owner) != TYPE_PLAYER) {
log_error(tprintf("Invalid object owner %s(%d): %d",db[thing].name,thing, db[thing].owner));
report();
db[thing].owner=GOD;
}
} else
/* if object is dead stick in free list */
free_object(thing);
/* mark all rooms that can be reached from limbo */
dbmark(PLAYER_START);
mark_float();
dbmark2();
/* look through list and inform any player with an unconnected room */
dbunmark();
}
/* Check data base for disconnected rooms */
static void dbmark(loc)
dbref loc;
{
dbref thing;
if ((loc<0) || (loc>=db_top) || (db[loc].flags & MARKED) ||
(Typeof(loc)!=TYPE_ROOM))
return;
db[loc].flags|=MARKED;
/* recursively trace */
for(thing=Exits(loc);thing!=NOTHING;thing=db[thing].next)
dbmark(db[thing].link);
}
static void dbmark2()
{
dbref loc;
for (loc = 0; loc < db_top; loc++)
if (Typeof(loc) == TYPE_PLAYER || Typeof(loc) == TYPE_THING) {
if (db[loc].link != NOTHING)
dbmark(db[loc].link);
if(db[loc].location != NOTHING)
dbmark(db[loc].location);
}
/* if (Typeof(loc) == TYPE_THING && Exits(loc) != NOTHING)
for (thing = Exits(loc); thing != NOTHING; thing = db[thing].next)
dbmark(db[thing].link); i don't get this. -koosh */
}
static void dbunmark()
{
dbref loc;
int ndisrooms=0;
for(loc=0;loc<db_top;loc++)
if (db[loc].flags & MARKED)
db[loc].flags&=~MARKED;
else
if (Typeof(loc)==TYPE_ROOM) {
ndisrooms++;
dest_info(NOTHING,loc);
}
com_send(DBINFO_CHAN,tprintf("[%s] * There are %d disconnected rooms.",DBINFO_CHAN,ndisrooms));
}
/* Check data base for disconnected objects */
static void dbmark1()
{
dbref thing;
dbref loc;
for(loc=0;loc<db_top;loc++)
if (Typeof(loc)!=TYPE_EXIT) {
for(thing=db[loc].contents;thing!=NOTHING;thing=db[thing].next) {
if ((db[thing].location!=loc) || (Typeof(thing)==TYPE_EXIT)) {
log_error(tprintf("Contents of object %d corrupt at object %d cleared",
loc,thing));
db[loc].contents=NOTHING;
break;
}
db[thing].flags|=MARKED;
}
for (thing=db[loc].exits; thing!=NOTHING; thing=db[thing].next) {
if ((db[thing].location != loc) || (Typeof(thing)!=TYPE_EXIT)) {
log_error(tprintf("Exits of object %d corrupt at object %d. cleared.", loc, thing));
db[loc].exits = NOTHING;
break;
}
db[thing].flags|=MARKED;
}
}
}
static void dbunmark1()
{
dbref loc;
for(loc=0;loc<db_top;loc++)
if (db[loc].flags & MARKED)
db[loc].flags&=~MARKED;
else
if (!IS_GONE(loc))
if (((Typeof(loc)==TYPE_PLAYER) || (Typeof(loc)==TYPE_THING))) {
log_error(tprintf("DBCK: Moved object %d",loc));
if (db[loc].location>0 && db[loc].location<db_top && Typeof(db[loc].location) != TYPE_EXIT)
moveto(loc, db[loc].location);
else
moveto (loc, 0);
} else if (Typeof(loc) == TYPE_EXIT) {
log_error(tprintf("DBCK: moved exit %d", loc));
if (db[loc].location>0 && db[loc].location<db_top && Typeof(db[loc].location) != TYPE_EXIT)
moveto(loc, db[loc].location);
else
moveto(loc, 0);
}
}
static void calc_memstats()
{
int i;
int j=0;
for (i=0; i<db_top; i++)
j += mem_usage(i);
com_send(DBINFO_CHAN,tprintf("[%s] * There are %d bytes being used in memory, total.",DBINFO_CHAN,j));
}
void do_dbck(player)
dbref player;
{
extern dbref speaker;
dbref i;
speaker = GOD;
for (i=0; i<db_top; i++) {
int m;
dbref j;
for (j=db[i].exits, m=0; j != NOTHING; j=db[j].next, m++)
if (m>1000) db[j].next = NOTHING;
for (j=db[i].contents, m=0; j!=NOTHING; j=db[j].next, m++)
if (m>1000) db[j].next = NOTHING;
}
if(!has_pow(player,NOTHING,POW_DB)) {
notify(player,"@dbck is a restricted command.");
return;
}
fix_free_list();
dbmark1();
dbunmark1();
calc_memstats();
}
/* send contents of destroyed object home+destroy exits */
/* all objects must be moved to nothing or otherwise unlinked first */
void do_empty(thing)
dbref thing;
{
static int nrecur=0;
int i;
ATRDEF *k, *next;
if (nrecur++>20) { /* if run away recursion return */
report();
log_error("Runaway recursion in do_empty");
nrecur--;
return;
}
if (Typeof(thing)!=TYPE_ROOM)
moveto(thing, NOTHING);
for (k=db[thing].atrdefs; k; k=next) {
next = k->next;
if (0==--k->a.refcount) {
free(k->a.name);
free(k);
}
}
db[thing].atrdefs = NULL;
switch(Typeof(thing)) {
case TYPE_THING:
case TYPE_PLAYER:
moveto (thing, NOTHING);
case TYPE_ROOM: { /* if room destroy all exits out of it */
dbref first;
dbref rest;
/* before we kill it tell people what is happening */
if (Typeof(thing)==TYPE_ROOM)
dest_info(thing, NOTHING);
/* return owners deposit */
db[thing].zone = NOTHING;
first=Exits(thing);
/* Clear all exits out of exit list */
while(first!=NOTHING) {
rest=db[first].next;
if (Typeof(first)==TYPE_EXIT)
do_empty(first);
first=rest;
}
first = db[thing].contents;
/* send all objects to nowhere */
DOLIST(rest, first) {
if (db[rest].link == thing) {
db[rest].link = db[db[rest].owner].link;
if (db[rest].link == thing)
db[rest].link = 0;
}
}
/* now send them home */
while(first != NOTHING) {
rest = db[first].next;
/* if home is in thing set it to limbo */
moveto (first, HOME);
first = rest;
}
}
break;
}
/* refund owner */
if(!(db[db[thing].owner].flags&QUIET))
notify(db[thing].owner,tprintf
("You get back your %d credit deposit for %s.",
object_cost(thing),
unparse_object(db[thing].owner,thing)));
giveto(db[thing].owner, object_cost(thing));
add_quota(db[thing].owner, 1);
/* chomp chomp */
atr_free(thing);
db[thing].list=NULL;
if(db[thing].pows) {
free(db[thing].pows);
db[thing].pows = 0;
}
/* don't eat name otherwise examine will crash */
s_Pennies(thing,0);
db[thing].owner=GOD;
db[thing].flags=GOING | TYPE_THING; /* toad it */
db[thing].location=NOTHING;
db[thing].link=NOTHING;
for (i=0; db[thing].children && db[thing].children[i]!=NOTHING; i++)
REMOVE_FIRST_L(db[db[thing].children[i]].parents, thing);
if (db[thing].children)
free(db[thing].children);
db[thing].children = NULL;
for (i=0; db[thing].parents && db[thing].parents[i]!=NOTHING; i++)
REMOVE_FIRST_L(db[db[thing].parents[i]].children, thing);
if (db[thing].parents)
free(db[thing].parents);
db[thing].parents = NULL;
do_halt(thing,"");
free_object(thing);
nrecur--;
}
void do_undestroy (player, arg1)
dbref player;
char * arg1;
{
dbref object;
object=match_controlled(player,arg1,POW_EXAMINE);
if (object == NOTHING)
return;
if (!(db[object].flags & GOING)) {
notify(player, tprintf("%s is not scheduled for destruction",
unparse_object(player, object)));
return;
}
db[object].flags &= ~GOING;
if (atol(atr_get(object, A_DOOMSDAY)) > 0) {
atr_add(object, A_DOOMSDAY, "");
notify(player, tprintf("%s has been saved from destruction.",
unparse_object(player, object)));
} else
notify(player, tprintf("%s is protected, and the GOING flag shouldn't \
have been set in the first place so what on earth happened?",
unparse_object(player, object)));
}
void zero_free_list()
{
first_free = NOTHING;
}
static int gstate=0;
static struct object *o;
static int thing;
void do_check(player, arg1)
dbref player;
char *arg1;
{
dbref obj;
if (!power(player,POW_SECURITY)) {
notify (player, "Permission denied.");
return;
}
obj = match_controlled (player, arg1, POW_MODIFY);
if (obj == NOTHING) return;
thing = obj;
gstate = 1;
notify(player, "Okay, i set the garbage point.");
}
/* garbage collect the database */
void do_incremental()
{
int j;
int a;
switch(gstate) {
case 0: /* pre collection need to age things first */
/*fprintf(stderr,"Ageing\n");*/
#ifdef DO_AGE
do_age();
#endif
/*(fprintf(stderr,"agedone\n");*/
gstate=1;
thing=0;
break;
case 1: /* into the copying stage */
o = &(db[thing]);
for(a=0;(a<GARBAGE_CHUNK) && (thing<db_top);a++,o++,thing++) {
char buff[1024];
extern char ccom[];
int i;
sprintf(ccom,"object #%d\n",thing);
if (thing == db_top) {
gstate = 0;
break;
}
strcpy(buff,o->name);
SET(o->name,buff);
atr_collect(thing);
if (!IS_GONE(thing)) {
ALIST *atr, *nxt;
again1:
for (i=0; db[thing].parents && db[thing].parents[i]!=NOTHING; i++) {
CHECK_REF(db[thing].parents[i]) {
log_error(tprintf ("Bad #%d in parent list on #%d.",db[thing].parents[i], thing));
REMOVE_FIRST_L (db[thing].parents, db[thing].parents[i]);
goto again1;
}
for (j=0; db[db[thing].parents[i]].children &&
db[db[thing].parents[i]].children[j] != NOTHING; j++)
if (db[db[thing].parents[i]].children[j] == thing) {
j = -1;
break;
}
if (j != -1) {
log_error(tprintf("Wrong #%d in parent list on #%d.",db[thing].parents[i], thing));
REMOVE_FIRST_L (db[thing].parents, db[thing].parents[i]);
goto again1;
}
}
again2:
for (i=0; db[thing].children && db[thing].children[i]!=NOTHING; i++) {
CHECK_REF(db[thing].children[i]) {
log_error(tprintf ("Bad #%d in children list on #%d.",db[thing].children[i], thing));
REMOVE_FIRST_L (db[thing].children, db[thing].children[i]);
goto again2; /* bad programming style, but it's easiest. */
}
for (j=0; db[db[thing].children[i]].parents &&
db[db[thing].children[i]].parents[j] != NOTHING; j++)
if (db[db[thing].children[i]].parents[j] == thing) {
j = -1;
break;
}
if (j != -1) {
log_error(tprintf("Wrong #%d in children list on #%d.",db[thing].children[i], thing));
REMOVE_FIRST_L (db[thing].children, db[thing].children[i]);
goto again1;
}
}
for (atr = db[thing].list; atr; atr=nxt) {
nxt = AL_NEXT(atr);
if (AL_TYPE(atr) && AL_TYPE(atr)->obj!=NOTHING
&& !is_a(thing,AL_TYPE(atr)->obj))
atr_add (thing, AL_TYPE(atr), "");
}
{
dbref zon;
for (dozonetemp=0, zon=get_zone_first(thing); zon!=NOTHING; zon=get_zone_next(zon), dozonetemp++)
if (dozonetemp>15) { /* ack. inf loop. */
log_error(tprintf("%s's zone %s is infinite.",unparse_object_a(1,thing), unparse_object_a(1, zon)));
db[zon].zone = db[0].zone;
db[db[0].zone].zone = NOTHING;
}
}
CHECK_REF(db[thing].exits)
switch(Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
case TYPE_ROOM: { /* yuck probably corrupted set to nothing */
log_error(tprintf("Dead exit in exit list (first) for room #%d: %d",thing, db[thing].exits));
report();
db[thing].exits=NOTHING;
}
}
CHECK_REF(db[thing].zone)
switch(Typeof(thing)) {
case TYPE_ROOM:
log_error(tprintf("Zone for #%d is #%d! setting it to the global zone.", thing, db[thing].zone));
db[thing].zone = db[0].zone;
break;
}
CHECK_REF(db[thing].link)
switch(Typeof(thing)) {
case TYPE_PLAYER:
case TYPE_THING:
db[thing].link=PLAYER_START;
break;
case TYPE_EXIT:
case TYPE_ROOM:
db[thing].link=NOTHING;
break;
}
CHECK_REF(db[thing].location)
switch(Typeof(thing)) {
case TYPE_PLAYER: /* this case shouldn't happen but just incase */
case TYPE_THING:
db[thing].location=NOTHING;
moveto(thing,PLAYER_START);
break;
case TYPE_EXIT:
db[thing].location=NOTHING;
destroy_obj(thing, atoi(BAD_OBJECT_DOOMSDAY));
break;
case TYPE_ROOM:
db[thing].location = thing; /* rooms are in themselves */
break;
}
if (((db[thing].next<0) || (db[thing].next>=db_top)) && (db[thing].next!=NOTHING)) {
log_error(tprintf("Invalid next pointer from object %s(%d)",db[thing].name,
thing));
report();
db[thing].next=NOTHING;
}
if ((db[thing].owner<0) || (db[thing].owner>=db_top) || Typeof(db[thing].owner) != TYPE_PLAYER) {
log_error(tprintf("Invalid object owner %s(%d): %d",db[thing].name,thing, db[thing].owner));
report();
db[thing].owner=GOD;
}
}
}
/* if complete go to state 0 */
if (thing==db_top)
gstate=0;
break;
}
}
/* Find and mark all floating rooms. */
static void mark_float()
{
dbref loc;
for(loc=0;loc<db_top;loc++)
if (IS(loc,TYPE_ROOM,ROOM_FLOATING)) dbmark(loc);
}
void do_upfront(player, arg1, arg2)
dbref player;
char *arg1;
char *arg2;
{
dbref object;
if (!power(player,POW_DB)) {
notify(player,"Restricted command.");
return;
}
if ((object=match_thing(player,arg1)) == NOTHING)
return;
for(object=first_free;object!=NOTHING && db[object].next!=thing;
object=db[object].next);
if(object==NOTHING) {
notify(player,"That object does not exist in the free list.");
return;
}
first_free = object;
db[object].next = NOTHING; /* let it reconstruct it */
notify(player,"Done.");
if (*arg2) parse_que(player,arg2,player);
}
#endif /* DESTROY */