Copyover for Smaug - aka Hotboot
--------------------------------

Original code by Erwin Andreasen for Rom muds.
Smaug ports by various authors.
Cleanup and revisions for this particular port by Samson of Alsherok

Terms of Use
------------

1. You may use this snippet in your code provided that any included
comment headers in the code are left intact. You may add your own, but
do not take mine out.

2. This snippet may not be posted for redistribution on any site
without obtaining prior written consent from the Alsherok team.

3. ( optional ) Register with the forums at http://forums.alsherok.net
Registration is not required to make use of the snippet, but since I no
longer provide email support for any of the code I release, forum posts
are your only avenue for direct support. This may seem overly stupid,
but you can blame the continuing abuse I suffer from spammers for this.
Don't post stuff to TMC or TMS asking about my code. I'm highly unlikely
to ever notice it there on the rare ocassions I skim posts in either place.

If forum registration doesn't appeal to you, then you can try to get ahold
of me via IMC on the code channel.

If you can't agree to these terms, don't use this code, and don't expect
me to help if something breaks while installing it. Harsh? Hardly. I'm
tired of people who come crawling to whine and complain when they haven't
bothered to comply with the terms first.

What this code does
-------------------

The usual copyover code which allows a mud to be rebooted without having to
drop the socket connections to the players. This particular port has been
cleaned up some and has the proper alterations to allow it to work with
several of the snippets I have on my site. It also preserves the locations of
all loaded mobs and objects in the game at the time, so it will seem like just
a bit of lag for the players. Mobs will even still be wounded.
The command has also simply been renamed because "copyover" just sounds funny to me :)

Installation Instructions
-------------------------

1. Add hotboot.c and hotboot.h to your source code directory. Then under your dist
dir, make a hotboot dir.

2. Open mud.h and find the following code:

/*
 * Connected state for a channel.
 */
typedef enum
{
  CON_PLAYING,		CON_GET_NAME,		CON_GET_OLD_PASSWORD,
  CON_CONFIRM_NEW_NAME,	CON_GET_NEW_PASSWORD,	CON_CONFIRM_NEW_PASSWORD,
  CON_GET_NEW_SEX,	CON_GET_NEW_CLASS,	CON_READ_MOTD,
  CON_GET_NEW_RACE,	CON_GET_EMULATION,	CON_EDITING,
  CON_GET_WANT_RIPANSI,	CON_TITLE,		CON_PRESS_ENTER,
  CON_WAIT_1,		CON_WAIT_2,		CON_WAIT_3,
  CON_ACCEPTED,         CON_GET_PKILL,		CON_READ_IMOTD
} connection_types;

   Change that code to read as follows - and be sure you preserve any extra constates
   you may have added yourself:

/*
 * Connected state for a channel.
 */
typedef enum
{
  CON_GET_NAME = -99,
  CON_GET_OLD_PASSWORD,   CON_CONFIRM_NEW_NAME,
  CON_GET_NEW_PASSWORD,	CON_CONFIRM_NEW_PASSWORD,
  CON_GET_NEW_SEX,	CON_GET_NEW_CLASS,	CON_READ_MOTD,
  CON_GET_NEW_RACE,	CON_GET_EMULATION,
  CON_GET_WANT_RIPANSI,	CON_TITLE,		CON_PRESS_ENTER,
  CON_WAIT_1,		CON_WAIT_2,		CON_WAIT_3,
  CON_ACCEPTED,         CON_GET_PKILL,		CON_READ_IMOTD,

/* Uncomment this if using Samson's Reroll code */
/* CON_ROLL_STATS, */

  CON_COPYOVER_RECOVER, CON_PLAYING = 0,

/* Uncomment this if using Samson's delete code */
/* CON_DELETE, */

/* Uncomment this if using Mudworld's Oasis OLC port */
/* CON_OEDIT,		 CON_MEDIT,			CON_REDIT, */

/* Uncomment this section if using Samson's Shell Code */
/* CON_FORKED, CON_IAFORKED, */

  CON_EDITING
} connection_types;

   Then locate the following code:

/*
 * Structure for extended bitvectors -- Thoric
 */
struct extended_bitvector
{
    unsigned int		bits[XBI]; /* Needs to be unsigned to compile in Redhat 6 - Samson */
};

   Directly below that, add the following:

#include "hotboot.h"

   Then locate the following line of code:

void	boot_db		args( ( void ) );

   Change it to read as follows:

void	boot_db		args( ( bool fCopyOver ) );

   Then locate the OBJ_DATA struct and add the following to the end:

    int		room_vnum; /* hotboot tracker */

   Then locate the CHAR_DATA struct and add the following to the end:

    int		home_vnum;  /* hotboot tracker */

   Then locate the PC_DATA struct and add the following to the end:

    bool		hotboot; /* hotboot tracker */

   Then locate this line in the save.c section:

bool	load_char_obj	args( ( DESCRIPTOR_DATA *d, char *name, bool preload ) );

   Change it to read as follows:

bool	load_char_obj	args( ( DESCRIPTOR_DATA *d, char *name, bool preload, bool copyover ) );

   Then locate:

void    fwrite_obj args( ( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest, sh_int os_type ) );

   Change it to read as follows:

void    fwrite_obj args( ( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest, short os_type, bool hotboot ) );

3. Open comm.c and locate the following in function main:

#ifdef WIN32
  int mainthread( int argc, char **argv )
#else
  int main( int argc, char **argv )
#endif
{
    struct timeval now_time;
    char hostn[128];

   Directly below that, add the following:

   bool fCopyOver = FALSE;

   Then further down in main, locate the following:

    /*
     * Get the port number.
     */
    port = 4000;
    if ( argc > 1 )
    {
	if ( !is_number( argv[1] ) )
	{
	    fprintf( stderr, "Usage: %s [port #]\n", argv[0] );
	    exit( 1 );
	}
	else if ( ( port = atoi( argv[1] ) ) <= 1024 )
	{
	    fprintf( stderr, "Port number must be above 1024.\n" );
	    exit( 1 );
	}
    }

   Change it to read as follows:

   /*
    * Get the port number.
    */
   port = 4000;
   if ( argc > 1 )
   {
	if ( !is_number( argv[1] ) )
	{
	    fprintf( stderr, "Usage: %s [port #]\n", argv[0] );
	    exit( 1 );
	}
	else if ( ( port = atoi( argv[1] ) ) <= 1024 )
	{
	    fprintf( stderr, "Port number must be above 1024.\n" );
	    exit( 1 );
	}

      if (argv[2] && argv[2][0])
      {
         fCopyOver = TRUE;
         control = atoi( argv[3] );
         control2 = atoi( argv[4] );
         conclient = atoi( argv[5] );
         conjava = atoi( argv[6] );
      }
      else
         fCopyOver = FALSE;
   }

   Then locate the following a bit further down:

    log_string("Booting Database");
    boot_db( );
    log_string("Initializing socket");
    control  = init_socket( port   );
    control2 = init_socket( port+1 );
    conclient= init_socket( port+10);
    conjava  = init_socket( port+20);

   Change that to read as:

    log_string("Booting Database");
    boot_db( fCopyOver );
    log_string("Initializing socket"); 
    if( !fCopyOver ) /* We have already the port if copyover'ed */
    {
	control  = init_socket( port   );
	control2 = init_socket( port+1 );
	conclient= init_socket( port+10);
	conjava  = init_socket( port+20);
    }

   Then locate the call to game_loop() and directly ABOVE that, add the following:

      if( fCopyOver )
      {
         log_string( "Initiating hotboot recovery." );
         hotboot_recover();
      }

4. Open db.c and locate function boot_db.

   Find this:

/*
 * Big mama top level function.
 */
void boot_db( void )

   Change it to read as:

/*
 * Big mama top level function.
 */
void boot_db( bool fCopyOver )

   Then locate the following segment:

	log_string( "Resetting areas..." );
	area_update( );

   Change it to read as follows:

      if( fCopyOver )
	{
	   log_string( "Loading world state..." );
	   load_world( supermob );
	}

	log_string( "Resetting areas..." );
	area_update( );

   Locate function create_mobile and find the following line:

    mob->act			= pMobIndex->act;

   Below that line, add the following:

    mob->home_vnum		= -1;

5. Open handler.c, function char_to_room and find the following line:

    ch->in_room		= pRoomIndex;

   Add the following lines directly beneath that:

    if( ch->home_vnum < 1 )
       ch->home_vnum = ch->in_room->vnum;

   Then locate function obj_to_room and find the following segment:

    obj->in_room				= pRoomIndex;
    obj->carried_by				= NULL;
    obj->in_obj					= NULL;

   Add the following line directly beneath that:

    obj->room_vnum			= pRoomIndex->vnum; /* hotboot tracker */

6. Open save.c, and find:

void    fread_char      args( ( CHAR_DATA *ch, FILE *fp, bool preload) );

   Change that to:

void    fread_char      args( ( CHAR_DATA *ch, FILE *fp, bool preload, bool copyover) );

Locate function fwrite_obj and find this line:

void fwrite_obj( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest, sh_int os_type )

   Change it to read as follows:

void fwrite_obj( CHAR_DATA *ch, OBJ_DATA *obj, FILE *fp, int iNest, short os_type, bool hotboot )

   Then locate this segment:

    /*
     * Slick recursion to write lists backwards,
     *   so loading them will load in forwards order.
     */
    if ( obj->prev_content && os_type != OS_CORPSE )
	if( os_type == OS_CARRY )
	   fwrite_obj( ch, obj->prev_content, fp, iNest, OS_CARRY );

    /*
     * Castrate storage characters.
     * Catch deleted objects                                    -Thoric
     * Do NOT save prototype items!				-Thoric
     */
    if ( (ch && ch->level < obj->level)
    || ( obj->item_type == ITEM_KEY && !IS_OBJ_STAT(obj, ITEM_CLANOBJECT ) )
    ||   obj_extracted(obj) 
    ||   IS_OBJ_STAT( obj, ITEM_PROTOTYPE ) )
       return;

   Change it to read as follows:

    /*
     * Slick recursion to write lists backwards,
     *   so loading them will load in forwards order.
     */
    if ( obj->prev_content && os_type != OS_CORPSE )
	if( os_type == OS_CARRY )
	   fwrite_obj( ch, obj->prev_content, fp, iNest, OS_CARRY, hotboot );

    /*
     * Castrate storage characters.
     * Catch deleted objects                                    -Thoric
     * Do NOT save prototype items!				-Thoric
     */
    if( !hotboot )
    {
       if ( (ch && ch->level < obj->level)
       || ( obj->item_type == ITEM_KEY && !IS_OBJ_STAT(obj, ITEM_CLANOBJECT ) )
       ||   obj_extracted(obj) 
       ||   IS_OBJ_STAT( obj, ITEM_PROTOTYPE ) )
       return;
    }

   Then locate this segment:

    /* Corpse saving. -- Altrag */
    fprintf( fp, (os_type == OS_CORPSE ? "#CORPSE\n" : "#OBJECT\n") );

   Directly ABOVE that, add this:

    /* DO NOT save corpses lying on the ground as a hotboot item, they already saved elsewhere! - Samson */
    if( hotboot && obj->item_type == ITEM_CORPSE_PC )
	return;

   Then locate this segment:

    if ( os_type == OS_CORPSE && obj->in_room )
      fprintf( fp, "Room         %d\n",   obj->in_room->vnum         );

   Change it to read as follows:

    if ( ( os_type == OS_CORPSE || hotboot ) && obj->in_room )
    {
      fprintf( fp, "Room         %d\n",   obj->in_room->vnum         );
	fprintf( fp, "Rvnum	   %d\n",   obj->room_vnum		   );
    }

   Then locate this segment:

    fprintf( fp, "End\n\n" );

    if ( obj->first_content )
	fwrite_obj( ch, obj->last_content, fp, iNest + 1, OS_CARRY );

   Change it to read as follows:

    fprintf( fp, "End\n\n" );

    if ( obj->first_content )
	fwrite_obj( ch, obj->last_content, fp, iNest + 1, OS_CARRY, hotboot );

   Then locate function save_char_obj and find the following:

	if ( ch->first_carrying )
	  fwrite_obj( ch, ch->last_carrying, fp, 0, OS_CARRY );

   Change it to read as follows:

	if ( ch->first_carrying )
	  fwrite_obj( ch, ch->last_carrying, fp, 0, OS_CARRY, ch->pcdata->hotboot );

   Then locate function load_char_obj and find the following:

/*
 * Load a char and inventory into a new ch structure.
 */
bool load_char_obj( DESCRIPTOR_DATA *d, char *name, bool preload )

   Change it to read as follows:

/*
 * Load a char and inventory into a new ch structure.
 */
bool load_char_obj( DESCRIPTOR_DATA *d, char *name, bool preload, bool copyover )

   Then further down in function load_char_obj find the following:

    ch->pcdata->first_ignored           = NULL;    /* Ignore list */
    ch->pcdata->last_ignored            = NULL;
    ch->pcdata->tell_history            = NULL; /* imm only lasttell cmnd */
    ch->pcdata->lt_index                = 0;    /* last tell index */
    ch->morph                       = NULL;

   Directly below that, add the following:

    ch->pcdata->hotboot                 = FALSE; /* Never changed except when PC is saved during hotboot save */

   Then locate the following further down:

	    word = fread_word( fp );
	    if ( !strcmp( word, "PLAYER" ) )
	    {
		fread_char ( ch, fp, preload );
		if ( preload )
		  break;
	    }

   Change it to read as follows:

	    word = fread_word( fp );
	    if ( !strcmp( word, "PLAYER" ) )
	    {
		fread_char ( ch, fp, preload, copyover );
		if ( preload )
		  break;
	    }

   Then locate function fread_obj and find the following:

       if ( ch )
	room = ch->in_room;

   Change that to read as:

   if ( ch )
   {
	room = ch->in_room;
	if( ch->tempnum == -9999 )
	   file_ver = 0;
   }

   Then locate the following:

	case 'R':
	    KEY( "Room", room, get_room_index(fread_number(fp)) );

   Directly beneath that, add the following:

	    KEY( "Rvnum", obj->room_vnum,	fread_number(fp));

   Then locate function fread_char and find the following:

void fread_char( CHAR_DATA *ch, FILE *fp, bool preload )
{
    char buf[MAX_STRING_LENGTH];

   Change that to read as follows:

void fread_char( CHAR_DATA *ch, FILE *fp, bool preload, bool copyover )
{
    char buf[MAX_STRING_LENGTH];

   Then further down, locate the following:

	    if ( !strcmp( word, "Site" ) )
	    {
		if ( !preload )
		  ch_printf( ch, "Last connected from: %s\r\n", fread_word( fp ) );
		else
		  fread_to_eol( fp );
		fMatch = TRUE;
		if ( preload )
		  word = "End";
		else
		  break;
	    }

   Change it to read as follows:

	    if ( !strcmp( word, "Site" ) )
	    {
		if ( !preload && !copyover )
		  ch_printf( ch, "Last connected from: %s\r\n", fread_word( fp ) );
		else
		  fread_to_eol( fp );
		fMatch = TRUE;
		if ( preload )
		  word = "End";
		else
		  break;
	    }

7. Search the rest of your code for any other calls to fwrite_obj and load_char_obj.
   Add a FALSE to the arguments for the calls.

8. Add the appropriate entries to tables.c for do_hotboot

9. Make clean, recompile.

10. After rebooting, make a 'hotboot' command using cedit, and set this to the level you want.

If there are any problems with this installation, feel free to post your
question to the forums at http://forums.alsherok.net

This code has been installed and tested on Smaug 1.6 FUSS, which is a bugfixed
and cleaned up version of the base Smaug 1.4a code. The Smaug FUSS Project is
maintained on servers which run the Redhat and Fedora family of Linux. Limited
testing has also been done on the Cygwin package under WindowsXP SP1 and SP2.
Users of BSD, MSVC, MSVC++, or Macintosh platforms are on their own as The
Smaug FUSS Project does not have access to these development environments for testing.
The Smaug FUSS Project can be found at: http://www.smaugfuss.org

No guarantees are made that this code will be compatible with your codebase and any
modifications you may have made to it. No warranty of any kind is expressed or implied
by the use of this code, and we are not responsible for any damages which may result
from the application of this snippet to your codebase.

Adventure beckons in the lands of mystique....
Samson, Implementor of Alsherok
http://www.alsherok.net
telnet://alsherok.net:5500

IMC2 contact: Samson@Alsherok