muse1.7b4/
muse1.7b4/config/
muse1.7b4/doc/
muse1.7b4/run/
muse1.7b4/run/db/
muse1.7b4/src/
muse1.7b4/src/db/
muse1.7b4/src/files/
muse1.7b4/src/io/
muse1.7b4/src/prog/
muse1.7b4/src/util/
/* 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 */