pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
/*****************************************************************************
 ** Project     : pDirt (Aber IV MUD Daemon)
 ** Module      : recover.c
 ** Description : A completely rewritten reboot/update system. Quite stable,
 **               fast and complete. It will store virtually all changes to
 **               the original start state of a mud and store it to be reloaded
 **               after a reboot or crash.
 ** Author      : Peter Eussen (Marty)
 ** Copyright   : (c) 1997 Eradicated Worlds
 ** Date        : 1 October 1997
 ** Version     : 0.1
 ***************************************************************************/
#define RECOVER_C
#include "kernel.h"
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "quest.h"
#include "quests.h"
#include "sendsys.h"
#include "log.h"
#include "bprintf.h"
#include "uaf.h"
#include "mail.h"
#include "mud.h"
#include "writer.h"
#include "main.h"
#include "editor.h"
#include "rooms.h"
#include "mobile.h"
#include "special.h"
#include "fight.h"
#include "bootstrap.h"
#include "commands.h"
#include "tables.h"
#include "objsys.h"
#include "zones.h"
#include "recover.h"

#define MAX_ZONE_LEN			50

typedef struct _Players {
   char    name[PNAME_LEN+1];
   char    host[MAXHOSTNAMELEN];
   char    realhost[MAXHOSTNAMELEN];
   char    awaymsg[SETIN_MAX+1];
   time_t  last_cmd;
   time_t  logged_on;
   time_t  last_command;
   time_t  last_on;
   int     fd;
   long    loc;
   int     vis;
   int     slot;
   Boolean linkdead;
   Boolean iamon;
   int     invct;
   int     drunkct;
} PlayerRec;

typedef struct _Objects {
   long    id;
   int     dam;
   int     armor;
   OFLAGS  oflags;
   int     state;
   int     loc;
   int     cflags;
   int     vis;
   int     size;
   int     value;
} ObjectRec;

typedef struct _Mobiles {
   long	    id;
   int      loc;
   int      armor;
   int      dam;
   int      speed;
   MFLAGS   mflags;
   PFLAGS   pflags;
   SFLAGS   sflags;
   int      score;
   int      strength;
   int      fighting;
   int      agg;
} MobileRec;

typedef struct _World {
   int	    port;
   int      master_sock;
   int      dnsfd;
   int      syslogfd;

   int      reboots;
   int      resets;
   int      crashes;

   time_t   startup;
   time_t   reset_time;
   time_t   last_backup;
   time_t   last_healall;
   
   int      plrct;
   int      mobct;
   int      objct;
   int      zonct;

   Boolean  full_recover;
   int      tempzones;

   char     season;
   char     modifier;
   char     daytime;
   char     daycount;
   char     light;
   char     temp;
   unsigned int countdown;

   int      qdsize;
/*   int      qlsize;
   int      pdsize;*/

   Boolean  qdone[96];
   /*nt      qlist[96][PNAME_LEN+1];*/
} WorldRec; 


int dump_players(char *base);
int dump_mobiles(char *base);
int dump_objects(char *base);
int dump_zones(char *base);
int load_players(char *base);
void recover_error(char *str,char *fn, int line);
int recover_mobiles(char *base);
int recover_objects(char *base);
int recover_zones(char *base);

extern int dnsfd;

int load_recovery(pid_t pidnum,Boolean *was_update)
{   FILE *fp;
    char basefn[255];
    char fn[255];
    WorldRec w;
    extern fd_set sockets_fds, buffer_fds;
    extern int syslogfd;
 
    sprintf(basefn,"%s/REBOOT/%d",DATA_DIR,pidnum);
    sprintf(fn,"%s.info",basefn);
 
    if ((fp = fopen(fn,"r")) == NULL)
    {   recover_error("load_recovery()",__FILE__,__LINE__);
        exit(2);
    }
    else
    {   extern int port_number;
	extern int main_socket;
	extern CALENDAR calendar;

	if (fread(&w,sizeof(WorldRec),1,fp) != 1)
        {  recover_error("load_recovery",__FILE__,__LINE__);
           exit(3);
        }

        if (bootstrap() < 0)
        {   recover_error("bootstrap() in load_recovery()",__FILE__,__LINE__);
            exit(4);
        }

	FD_ZERO(&sockets_fds);
	FD_ZERO(&buffer_fds);
	FD_SET(w.master_sock,&sockets_fds);

        if (recover_zones(basefn) != w.zonct)
        {   recover_error("recover_zones() in load_recovery()",__FILE__,__LINE__);
            exit(3);
        }
        boot_reset();

        numreboots = w.reboots;
	numresets = w.resets;
	numcrashes = w.crashes;

	last_startup = w.startup;
	last_reset = w.reset_time;
	last_backup = w.last_backup;
	last_healall = w.last_healall;
	
	port_number = w.port;
	main_socket = w.master_sock;
        dnsfd = w.dnsfd;
        syslogfd = w.syslogfd;

	calendar.season = w.season;
	calendar.modifier = w.modifier;
	calendar.daytime = w.daytime;
	calendar.daycount = w.daycount;
	calendar.light = w.light;
	calendar.temp = w.temp;
	calendar.countdown = w.countdown;

	bzero(questsdone,w.qdsize * sizeof(Boolean));
/*	bzero(QuestDoneBy,w.qlsize * (sizeof(char) * (PNAME_LEN +1)));*/


        if (load_players(basefn) != w.plrct)
        {   mudlog("RECOVER: Amount of players in file does not correspond with info.");
            exit(3);
        }
       
        if ((*was_update = w.full_recover))
        {   bcopy(w.qdone,questsdone,sizeof(Boolean) * w.qdsize);
/*            bcopy(w.qlist,QuestDoneBy,w.qlsize * (sizeof(char) * (PNAME_LEN+1)));*/
            
            if (recover_mobiles(basefn) != w.mobct)
            {   mudlog("RECOVER: Amount of mobiles in file does not correspond with info.");
                exit(3);
            }
            if (recover_objects(basefn) != w.objct)
            {   mudlog("RECOVER: Amount of objects in file does not correspond with info.");
                exit(3);
            }
        }
    }
    mudlog("RECOVER: %s finished",*was_update ? "Update" : "Reboot");
    fclose(fp);
    remove(fn);
    return 0;
}

int dump_recovery(Boolean full)
{   FILE *fp;
    char fn2[255];
    char fn[255];
    WorldRec w;
    sprintf(fn,"%s/REBOOT/%d",DATA_DIR, getpid());

    sprintf(fn2,"%s.info",fn);

    if ((fp = fopen(fn2,"w")) == NULL)
        return -1;
    else
    {   extern int port_number;
        extern int main_socket;
        extern int syslogfd;
	extern CALENDAR calendar;

	w.qdsize = LAST_QUEST;
/*        w.pdsize = ARRAYSIZE(PuzzlesDone);*/

        bzero(w.qdone,sizeof(w.qdone));
    /*    bzero(w.qlist,sizeof(w.qlist));
        bzero(w.pdone,sizeof(w.pdone));*/

        if (full)
        {   w.mobct = dump_mobiles(fn);
            w.objct = dump_objects(fn);
            bcopy(questsdone,w.qdone,sizeof(w.qdone));
            /*bcopy(QuestDoneBy,w.qlist,sizeof(w.qlist));*/
        }

        w.zonct = dump_zones(fn);
        w.plrct = dump_players(fn);

        w.full_recover = full;
        w.port = port_number;
        w.master_sock = main_socket;
        w.dnsfd = dnsfd;
        w.syslogfd = syslogfd;

        w.reboots = numreboots;
        w.resets = numresets;
        w.crashes = numcrashes;

        w.startup = last_startup;
        w.reset_time = last_reset;
        w.last_backup = last_backup;
        w.last_healall = last_healall;

	w.season = calendar.season;
	w.modifier = calendar.modifier;
	w.daytime = calendar.daytime;
	w.daycount = calendar.daycount;
	w.light = calendar.light;
	w.temp = calendar.temp;
	w.countdown = calendar.countdown;

        if (fwrite((void *)&w,sizeof(WorldRec),1,fp) == -1)
	{  recover_error("dump_recovery()",__FILE__,__LINE__);
           exit(1);
        }
        fclose(fp); 
    }
    return 0;
}

int dump_players(char *base)
{   FILE *fp;
    char fn[255];
    int  i,ct = 0;
    PlayerRec p;

    sprintf(fn,"%s.players",base);

    if ((fp = fopen(fn,"w")) == NULL)
       return -1;
    else
    {   for (i = 0;i < max_players; i++) 
        {   if (players[i].iamon)
            {  strcpy(p.name,pname(i));
               strcpy(p.host,players[i].hostname);
               strcpy(p.realhost,players[i].realhostname);
               strcpy(p.awaymsg,players[i].awaymsg);
               p.last_cmd = players[i].last_cmd;
               p.logged_on = players[i].logged_on;
               p.last_command = players[i].last_command;
               p.last_on = players[i].p_last_on;
               p.fd = players[i].fil_des;
               p.loc = loc_id(ploc(i));
               p.vis = pvis(i);
               p.slot = i;
               p.linkdead = players[i].linkdead;
               p.iamon = players[i].iamon;

               /* Clear out all editors etc */
               if (players[i].inmailer)
               {  int temp = mynum;
                  setup_globals(i);
                  abort_mail();
                  setup_globals(temp);
               }
               terminate_all_writers(i);
               save_player(i,True);

               if (fwrite((void *)&p,sizeof(PlayerRec),1,fp) == -1)
	       {  recover_error("player_dump()",__FILE__,__LINE__);
                  exit(1);
               }
               ct++;
            }
            else if (players[i].inp_handler != NULL)
            {   setup_globals(i);
                bprintf("\n\r"MUDNAME" is about to reboot and can therefore can not service you.\n"
                        "Please try to login in a few minutes. Thanks for your cooperation.\n");
                bflush();
                if (cur_player->stream != NULL)
                   fclose(cur_player->stream);
                close(cur_player->fil_des);
                mudlog("RECOVER: Disconnecting connection from host %s",cur_player->hostname);
            }
        }
        terminate_editors();
        fclose(fp);
    }
    return ct;
}

int dump_mobiles(char *base)
{   char fn[255];
    FILE *fp;
    int  i=0,ct = 0;
    MobileRec m;

    sprintf(fn,"%s.NPC",base);
    if ((fp = fopen(fn,"w")) == NULL)
       return -1;
    else
    {   for (i = max_players; i < numchars; i++)
        {   if (loc_id(ploc(i)) != ploc_reset(i) ||
                parmor(i) != parmor_reset(i) ||
                pdam(i) != pdam_reset(i) ||
                pspeed(i) != pspeed_reset(i) ||
                pflags(i).b1 != pflags_reset(i).b1 ||
                pflags(i).b2 != pflags_reset(i).b2 ||
                pflags(i).b3 != pflags_reset(i).b3 ||
                mflags(i).h != mflags_reset(i).l ||
                mflags(i).l != mflags_reset(i).h ||
                sflags(i).b1 != sflags_reset(i).b1 ||
                sflags(i).b2 != sflags_reset(i).b2 ||
                sflags(i).b3 != sflags_reset(i).b3 ||
                pscore(i) != 0 ||
                pstr(i) != pstr_reset(i) ||
                pfighting(i) != -1 ||
                pagg(i) != pagg_reset(i))
           {    m.id = pnum(i);
                m.loc = loc_id(ploc(i));
                m.armor = parmor(i);
                m.dam = pdam(i);
                m.speed = pspeed(i);
                m.pflags = pflags(i);
                m.mflags = mflags(i);
                m.sflags = sflags(i);
                m.score = pscore(i);
                m.strength = pstr(i);
                m.fighting = pfighting(i);
                m.agg = pagg(i);

                if (fwrite((void *)&m,sizeof(MobileRec),1,fp) == -1)
	        {  recover_error("dump_mobiles()",__FILE__,__LINE__);
                   exit(1);
                }
                ct++;
           }                
        }
        fclose(fp);
    }
    return ct;
}

int dump_objects(char *base)
{  char fn[255];
   FILE *fp;
   int  i,ct = 0;
   ObjectRec o;

   sprintf(fn,"%s.objects",base);
   if ((fp = fopen(fn,"w")) == NULL)
       return -1;
   else
   {  for (i = 0; i < numobs; i++)
      {   if (odamage(i) != odamage_reset(i) ||
              oarmor(i) != oarmor_reset(i) ||
              obits(i).b1 != obits_reset(i).b1 ||
              obits(i).b2 != obits_reset(i).b2 ||
              obits(i).b3 != obits_reset(i).b3 ||
              state(i) != state_reset(i) ||
              oloc(i) != oloc_reset(i) ||
              ocarrf(i) != ocarrf_reset(i) ||
              ovis(i) != ovis_reset(i) ||
              osize(i) != osize_reset(i) ||
              obaseval(i) != ovalue_reset(i))
          {   o.id = obj_id(i);
              o.dam = odamage(i);
              o.armor = oarmor(i);
              o.oflags.b1 = obits(i).b1;
              o.oflags.b2 = obits(i).b2;
              o.oflags.b3 = obits(i).b3;
              o.state = state(i);
              o.loc = oloc(i);
              o.cflags = ocarrf(i);
              o.vis = ovis(i);
              o.size = osize(i);
              o.value = obaseval(i);
              if (fwrite((void *)&o,sizeof(ObjectRec),1,fp) == -1)
	      {  recover_error("dump_objects()",__FILE__,__LINE__);
                 exit(1);
              }
              ct++;
          }
           
      }
      fclose(fp);
   }
   return ct;
}

int dump_zones(char *base)
{   char fn[255];
    FILE *fp;
    char z[MAX_ZONE_LEN];
    int  ct = 0, i;

    sprintf(fn,"%s.zones",base);

    if ((fp = fopen(fn,"w")) == NULL)
       return -1;
    else
    {   for (i = num_const_zon; i < numzon; i++)
        {   if (!ztemporary(i))
            {    strncpy((char *)z,zname(i),MAX_ZONE_LEN-1);
                 ct++;
                 fwrite((void *)z,MAX_ZONE_LEN,1,fp);
            }
        } 
    }
    fclose(fp);
    return ct;
}

int recover_zones(char *base)
{   char fn[255];
    FILE *fp;
    int  ct = 0;
    char z[MAX_ZONE_LEN];

    sprintf(fn,"%s.zones",base);
    if ((fp = fopen(fn,"r")) == NULL)
       return -1;
    else
    {   fread((void *)z,MAX_ZONE_LEN,1,fp);

        while (!feof(fp))
        {   ct++;
            if (load_zone(z,NULL,NULL,NULL,NULL,NULL,NULL) == -1)
            {   mudlog("RECOVER: zone %s not found.\n",z);
            }
            fread((void *)z,MAX_ZONE_LEN,1,fp);
        }
    }
    fclose(fp);
    remove(fn);
    return ct;
}

int load_players(char *base)
{   FILE *fp;
    char fn[255];
    int  i,ct = 0,loc = 0;
    PlayerRec p;
    extern int width;
    extern fd_set sockets_fds;

    sprintf(fn,"%s.players",base);

    if ((fp = fopen(fn,"r")) == NULL)
    {   recover_error("load_players()",__FILE__,__LINE__);
        return -1;
    }
    else
    {   for (i=0; i < max_players; i++)
    	   initialize_slot(i);
    	   
        fread(&p,sizeof(PlayerRec),1,fp);
        
        while (!feof(fp))
        {   ct++;
            i = p.slot;
            
            setup_globals(i);
            getuafinfo(p.name);
            fetchprmpt(mynum);
            
            strcpy(pname(i), p.name);
            strcpy(players[i].hostname,p.host);
            strcpy(players[i].realhostname,p.realhost);
            strcpy(players[i].awaymsg,p.awaymsg);
            players[i].last_cmd = p.last_cmd;
            players[i].logged_on = p.logged_on;
            players[i].last_command = p.last_command;
            players[i].p_last_on = p.last_on;
            players[i].iamon = p.iamon;
            players[i].linkdead = p.linkdead;
            players[i].fil_des = p.fd;
            loc = find_loc_by_id(p.loc);
            
            if (exists(loc))
            	setploc(i,loc);
            else if (exists((loc = find_loc_by_id(phome(i)))) )
                setploc(i,loc);
            else 
            	setploc(i,get_rand_start());
            	
            cur_player->isawiz = (OPERATOR(p.name) || privileged_user(p.name));
            setpvis(i,p.vis);
            setpwpn(i,-1);
            setphelping(i,-1);
            setpfighting(i,-1);
            setpsitting(i,0);

            push_input_handler(get_command);
            
            if (!p.linkdead && p.fd >= 0)
            {  if (cur_player->fil_des >= width)
                  width = cur_player->fil_des;
               FD_SET(p.fd,&sockets_fds);
               cur_player->sock_buffer_p = cur_player->sock_buffer_end = cur_player->sock_buffer;
               cur_player->stream = fdopen(p.fd,"w");
               if (cur_player->stream == NULL)
               {   initialize_slot(i);
                   mudlog("RECOVER: Could not initialise slot for %s.",p.name);
               }
               else
                  fprintf(cur_player->stream,"You feel yourself floating in an infinite void, as you wait for the world\n"
                          "to recover from the disaster.\n");
            }
            
            if (cur_player->stream != NULL)
            {   setplayerfunc(i);
                get_command(NULL);
            }
            fread(&p,sizeof(PlayerRec),1,fp);
        }
    }
    fclose(fp);
    remove(fn);
    return ct;
}

int recover_mobiles(char *base)
{   char fn[255];
    FILE *fp;
    int  ct = 0,loc=0;
    MobileRec m;

    sprintf(fn,"%s.NPC",base);
    if ((fp = fopen(fn,"r")) == NULL)
       return -1;
    else
    {   fread(&m,sizeof(MobileRec),1,fp);
    
        while (!feof(fp))
        {   ct++;
           
            m.id += max_players; 
            loc = find_loc_by_id(m.loc);
            
            if (exists(loc))
            	setploc(m.id,loc);
            else
                setploc(m.id,dead_loc);
            
            setparmor(m.id,m.armor);
            setpdam(m.id,m.dam);
            setpspeed(m.id,m.speed);
            pscore(m.id) = m.score;
            setpstr(m.id,m.strength);
            setpfighting(m.id,m.fighting);
            setpagg(m.id,m.agg);
            pflags(m.id) = m.pflags;
            mflags(m.id) = m.mflags;
            sflags(m.id) = m.sflags;
            fread(&m,sizeof(MobileRec),1,fp);
        }
    }
    fclose(fp);
    remove(fn);
    return ct;
}

int recover_objects(char *base)
{  char fn[255];
   FILE *fp;
   int  ob,ct = 0;
   ObjectRec o;

   sprintf(fn,"%s.objects",base);
   if ((fp = fopen(fn,"r")) == NULL)
       return -1;
   else
   {  fread(&o,sizeof(ObjectRec),1,fp);
   
      while (!feof(fp))
      {   ct++;
      
          ob = find_object_by_id(o.id);
         
          if (ob >= 0) 
          {  osetdamage(ob,o.dam);
             osetarmor(ob,o.armor);
             obits(ob) = o.oflags;
             state(ob) = o.state;
             setoloc(ob,o.loc,o.cflags);
             osetvis(ob,o.vis);
             osetsize(ob,o.size);
             osetbaseval(ob,o.value);
          }
          fread(&o,sizeof(ObjectRec),1,fp);
      } 
   }
   fclose(fp);
   remove(fn);
   return ct;
}

void recover_error(char *fun,char *fn, int line)
{   sprintf(globalbuf,"Recover Failed: %s [%s:%d]",fun,fn,line);
    progerror(globalbuf);
    exit(1);
}