Login
User Name:

Password:



Register
Forgot your password?
Vote for Us!
auth_update crash
Dec 23, 2017, 10:15 pm
By Remcon
check_tumble
Dec 18, 2017, 7:21 pm
By Remcon
parse description bug
Dec 15, 2017, 10:08 pm
By Remcon
Couple bugs
Dec 12, 2017, 5:42 pm
By Remcon
Bug in disarm( )
Nov 12, 2017, 6:54 pm
By GatewaySysop
LoP 1.46
Author: Remcon
Submitted by: Remcon
LOP 1.45
Author: Remcon
Submitted by: Remcon
LOP Heroes Edition
Author: Vladaar
Submitted by: Vladaar
Heroes sound extras
Author: Vladaar
Submitted by: Vladaar
6Dragons 4.3
Author: Vladaar
Submitted by: Vladaar
Users Online
CommonCrawl, Yahoo!, Sogou

Members: 0
Guests: 12
Stats
Files
Topics
Posts
Members
Newest Member
478
3,708
19,242
612
Jacki72H
Today's Birthdays
There are no member birthdays today.
Related Links
» SmaugMuds.org » General » Smaug Snippets » Hotboot/Copyover
Forum Rules | Mark all | Recent Posts

Hotboot/Copyover
< Newer Topic :: Older Topic > Under Windows w/Cygwin's dll...

Pages:<< prev 1 next >>
Post is unread #1 Jul 12, 2003, 8:39 pm
Go to the top of the page
Go to the bottom of the page

GatewaySysop
Conjurer
GroupMembers
Posts367
JoinedMar 7, 2005

Just a general question here. I've seen probably three different kinds of hotboot/copyover snippets out there and I'm just curious if there's been anyone who's had this working under Windows (I've got 98 for now) when using the cygwin1.dll file, versus running under the Cygwin bash shell itself.

I seem to recall reading in more than one place that there were issues getting hotboot/copyover/etc. to work with this (or w/Cygwin at all?). It seemed like a rather extensive install so I thought I might want to find out about this before I tried throwing it in.

Any info would be appreciated. :)
       
Post is unread #2 Jul 13, 2003, 12:24 pm
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,639
JoinedJan 1, 2002

As I recall the copyover code will not work in Windows 9x/ME with Cygwin. You'd need to be running 2000 or XP before that would do you any good.
       
Post is unread #3 Jul 13, 2003, 3:08 pm
Go to the top of the page
Go to the bottom of the page

GatewaySysop
Conjurer
GroupMembers
Posts367
JoinedMar 7, 2005

Oddly enough, I was playing with that distro of 1.4a revision 4 that I got from the IMMU site, because it said that it included the copyover snippet, and it worked under Win98. :blink:

Strange no? This is running standalone with just the Cygwin1.dll and not under Cygwin's shell or anything. Does anyone know what particular snippet was used in that release so that I might give it a try?
       
Post is unread #4 Jul 13, 2003, 7:49 pm
Go to the top of the page
Go to the bottom of the page

GatewaySysop
Conjurer
GroupMembers
Posts367
JoinedMar 7, 2005

Egads!

I took a look into the aforementioned 1.4a revision 4 release of SMAUG from the IMMU site and happened upon what appeared to be a variation of the copyover-7 code that I had found on a few snippet sites. Working with the current release of FUSS, the instructions for installing the copyover-7 snippet AND the source for 1.4a revision 4 I went through one file at a time and patched things in. I used the instructions for the snippet as a guide and found all the parts of the snippet, although some were changed, in the revision 4 SMAUG code and lopped them in.

In the end I compiled and only got a single error, which was due to a single declaration that I missed. For the most part I installed this code exactly as it was found in the IMMU revision 4 release, really just using the aged copyover instructions as a guide for finding all the bits of the code.

End result? A clean compile with 0 errors and what appears to be a functioning copyover command in FUSS. B)

Now, I'm using this standalone with just the cygwin1.dll file, compiled under Cygwin and then run from the /area folder where I just have the .dll dropped in with a copy of the .exe file. Again this is on Win98.

I have tried this using local connections (127.0.0.1) and a remote connection simultaneously and they were both preserved during the copyover. It seems to be working properly from what I can tell. Never the less I've made extensive backups just in case, but so far so good.

Is there anything I should do to check that it is in fact working as it appears to be? Some way to perhaps validate it or possible bugs I can check for?

If this turns out to be viable (I have my doubts, seems too good to be true at the moment), I'd be happy to write up what I did to get this working in FUSS.

Input is appreciated!
       
Post is unread #5 Jul 13, 2003, 9:49 pm
Go to the top of the page
Go to the bottom of the page

Samson
Black Hand
GroupAdministrators
Posts3,639
JoinedJan 1, 2002

At it's core, all of the copyover/hotboot snippets use the same code to handle the process of stopping the original process and swapping in the new one. I have no idea what DLL file you're talking about, but if it works, cool. All I recall is that when I had 98, copyover didn't work in the Cygwin shell. It has worked in XP though, and I'm assuming 2000 since it's the same kernel. If there was some special thing that got lost along the way then that would be valuable information.

As for adding it to FUSS, I'm maintaining that as a bugfix distro and nothing more. Copyover isn't a bugfix, so it won't be included. But I'm sure the various maintainers of copyover snippets would appreciate seeing your findings anyway. :)
       
Post is unread #6 Jul 14, 2003, 12:32 am   Last edited Sep 7, 2004, 10:17 am by gatewaysysop
Go to the top of the page
Go to the bottom of the page

GatewaySysop
Conjurer
GroupMembers
Posts367
JoinedMar 7, 2005

Hmm. Well the cygwin1.dll is the dll file I was talking about throwing into the /area subdirectory of SMAUG along with a copy of the executable file. It's always worked for to me to do this, rather than have to run it under cygwin's shell.

Anywho, for those interested I'm going to see if this will let me post a really lengthy, hastily written but hopefully complete account of the changes that I've made. No guarantees of course, as this is just some hodge podge mucking about that seems to have produced results for me. Who knows, I could have done something horribly wrong in the process, so if that's the case by all means point it out. :(

Anyway, here goes:

What I've done to get copyover working (apparently) in FUSS: 

MUD.H

find: 

/*
* Old-style Bit manipulation macros
*
* The bit passed is the actual value of the bit (Use the BV## defines)
*/ 
#define IS_SET(flag, bit) ((flag) & (bit))
#define SET_BIT(var, bit) ((var) |= (bit))
#define REMOVE_BIT(var, bit) ((var) &= ~(bit))
#define TOGGLE_BIT(var, bit) ((var) ^= (bit))

and add:

#define CH(descriptor)  ((d)->original ? (d)->original : (d)->character)

then find: 

#define PLANE_FILE SYSTEM_DIR "planes.dat"   /* For planes   */

beneath add: 

#define COPYOVER_FILE   SYSTEM_DIR "copyover.dat" /* for warm reboots */
#define EXE_FILE        "../src/smaug"            /* executable path */

then find: 

/*
* 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 it to:

/*
* 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,
 CON_COPYOVER_RECOVER, CON_PLAYING = 0, CON_EDITING

} connection_types;

then find:

/* db.c */
void show_file args( ( CHAR_DATA *ch, char *filename ) );
char * str_dup  args( ( char const *str ) );
void boot_db  args( ( void ) );
void area_update args( ( void ) );

and change to:

/* db.c */
void show_file args( ( CHAR_DATA *ch, char *filename ) );
char * str_dup  args( ( char const *str ) );
void boot_db  args( ( bool fCopyOver ) );
void area_update args( ( void ) );

then find (in the section for comm.c):

void ch_printf_color args( ( CHAR_DATA *ch, char *fmt, ... ) );

and add directly beneath that line:

void    log_printf      args( (char *fmt, ...) ); 

then find: 

DECLARE_DO_FUN( do_cook  ;);

beneath that, add:

DECLARE_DO_FUN( do_copyover     );


------------------------------------------
In DB.C  

find: 

/*
* External booting function
*/
void load_corpses args( ( void ) );
void renumber_put_resets args( ( AREA_DATA *pArea ) );
void load_colors args( ( void ) );

add:

void    copyover_recover ();

then find: 

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

change the above line to: 

void boot_db( bool fCopyOver )

then find: 

/* Morphs MUST be loaded after class and race tables are set up --Shaddai */
       log_string ("Loading Morphs";);
       load_morphs( );
       log_string ("Loading Colors";);
       load_colors( );
       MOBtrigger = TRUE;
      

   }

and change to: 

/* Morphs MUST be loaded after class and race tables are set up --Shaddai */
       log_string ("Loading Morphs";);
       load_morphs( );
       log_string ("Loading Colors";);
       load_colors( );
       MOBtrigger = TRUE;
       if(fCopyOver)
       {
         log_string( "Running copyover_recover.";);
         copyover_recover();
       }

   }

----------------------------------------------------

COMM.C

find: 

struct timeval now_time;
   char hostn[128];

change to:

struct timeval now_time;
   bool fCopyOver = FALSE;
   char hostn[128];

then find: 

   /*
    * 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 );
}
   }

and replace with: 

   /*
    * 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]);

#ifdef I3
   I3_socket = atoi( argv[4] );
#endif

       }
       else
              fCopyOver = FALSE;


   }


then find: 

#endif /* WIN32 */

   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);

#ifdef I3
  /* Initialize and connect to Intermud-3 */
  I3_main( FALSE, port, FALSE );
#endif

replace with: 

#endif /* WIN32 */

#ifdef I3
  /* Initialize and connect to Intermud-3 */
  I3_main( FALSE, port, fCopyOver );
#endif

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


find:

   log_string( log_buf );

   game_loop( );
   
   closesocket( control  ;);
   closesocket( control2 );
   closesocket( conclient);
   closesocket( conjava  ;);


and change to:

   log_string( log_buf );
   
   game_loop( );

   closesocket( control  ;);

/*
   closesocket( control2 );
   closesocket( conclient);
   closesocket( conjava  ;);
*/

then find: 

void bailout(void)
{
   echo_to_all( AT_IMMORT, "MUD shutting down by system operator NOW!!", ECHOTAR_ALL );
   shutdown_mud( "MUD shutdown by system operator" );
   log_string ("MUD shutdown by system operator";);
   Sleep (5000);               /* give "echo_to_all" time to display */
   mud_down = TRUE;            /* This will cause game_loop to exit */
   service_shut_down = TRUE;   /* This will cause characters to be saved */
   fflush(stderr);
   return;
} 
#endif

and directly beneath, add: 

/* Recover from a copyover - load players */
void copyover_recover ()
{
       DESCRIPTOR_DATA *d;
       FILE *fp;
       char name [100];
       char host[MAX_STRING_LENGTH];
       int desc;
       bool fOld;

       log_printf ("Copyover recovery initiated";);

       fp = fopen (COPYOVER_FILE, "r";);

       if (!fp) /* there are some descriptors open which will hang forever then ? */
       {
               perror ("copyover_recover:fopen";);
               log_printf ("Copyover file not found. Exitting.\n\r";);
               exit (1);
       }

       unlink (COPYOVER_FILE); /* In case something crashes - doesn't prevent reading */

       for (;;)
       {
               fscanf (fp, "%d %s %s\n", &desc, name, host);
               if (desc == -1)
                       break;

               /* Write something, and check if it goes error-free */
               if (!write_to_descriptor (desc, "\n\rRestoring from copyover...\n\r",0))
               {
                       close (desc); /* nope */
                       continue;
               }

 CREATE( d, DESCRIPTOR_DATA, 1 );
               init_descriptor (d,desc); /* set up various stuff */

               d->host = str_dup (host);
 LINK( d, first_descriptor, last_descriptor, next, prev );
               d->connected = CON_COPYOVER_RECOVER; /* -15, so close_socket frees the char */


               /* Now, find the pfile */

               fOld = load_char_obj (d, name, FALSE);

               if (!fOld) /* Player file not found?! */
               {
                       write_to_descriptor (desc, "\n\rSomehow, your character was lost in the copyover. Sorry.\n\r", 0);
                       close_socket (d, FALSE);
               }
               else /* ok! */
               {
                       write_to_descriptor (desc, "\n\rCopyover recovery complete.\n\r",0);

                       /* Just In Case */
                       if (!d->character->in_room)
                               d->character->in_room = get_room_index (ROOM_VNUM_TEMPLE);

                       /* Insert in the char_list */
  LINK( d->character, first_char, last_char, next, prev );

                       char_to_room (d->character, d->character->in_room);
                       do_look (d->character, "";);
                       act( AT_ACTION, "$n materializes!", d->character, NULL, NULL, TO_ROOM);
                       d->connected = CON_PLAYING;
               }
       }
}


then find: 

void new_descriptor( int new_desc )

directly above, add: 

void init_descriptor( DESCRIPTOR_DATA *dnew, int desc)
{
   dnew->next         = NULL;
   dnew->descriptor   = desc;
   dnew->connected    = CON_GET_NAME;
   dnew->outsize      = 2000;
   dnew->idle         = 0;
   dnew->lines        = 0;
   dnew->scrlen       = 24;
   dnew->user         = STRALLOC("unknown";);
   dnew->newstate     = 0;
   dnew->prevcolor    = 0x07;

   CREATE( dnew->outbuf, char, dnew->outsize );
}


then find the entire function, void new_descriptor, and replace with:

void new_descriptor( int new_desc )
{
   char buf[MAX_STRING_LENGTH];
   DESCRIPTOR_DATA *dnew;
   struct sockaddr_in sock;
   struct hostent *from;
   int desc;
   int size;
   char bugbuf[MAX_STRING_LENGTH];
#ifdef WIN32
   unsigned long arg = 1;
#endif

   size = sizeof(sock);
   if ( check_bad_desc( new_desc ) )
   {
     set_alarm( 0 );
     return;
   }
   set_alarm( 20 );
   alarm_section = "new_descriptor::accept";
   if ( ( desc = accept( new_desc, (struct sockaddr *) &sock, &size) ) < 0 )
   {
perror( "New_descriptor: accept" );
sprintf(bugbuf, "[*****] BUG: New_descriptor: accept";);
log_string_plus( bugbuf, LOG_COMM, sysdata.log_level );
set_alarm( 0 );
return;
   }
   if ( check_bad_desc( new_desc ) )
   {
     set_alarm( 0 );
     return;
   }
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

   set_alarm( 20 );
   alarm_section = "new_descriptor: after accept";

#ifdef WIN32
   if ( ioctlsocket(desc, FIONBIO, &arg) == -1 )
#else
   if ( fcntl( desc, F_SETFL, FNDELAY ) == -1 )
#endif
   {
perror( "New_descriptor: fcntl: FNDELAY" );
set_alarm( 0 );
return;
   }
   if ( check_bad_desc( new_desc ) )
     return;

   CREATE( dnew, DESCRIPTOR_DATA, 1 );
/*
   dnew->next  = NULL;
   dnew->descriptor = desc;
   dnew->connected = CON_GET_NAME;
   dnew->outsize = 2000;
   dnew->idle  = 0;
   dnew->lines  = 0;
   dnew->scrlen = 24;
   dnew->port  = ntohs( sock.sin_port );
   dnew->user   = STRALLOC("(unknown)";);
   dnew->newstate = 0;
   dnew->prevcolor = 0x07;

   CREATE( dnew->outbuf, char, dnew->outsize );
*/
   init_descriptor(dnew, desc );
   dnew->port = ntohs(sock.sin_port);

   strcpy( buf, inet_ntoa( sock.sin_addr ) );
   sprintf( log_buf, "Sock.sinaddr:  %s, port %hd.",
 buf, dnew->port );
   log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
   if ( sysdata.NO_NAME_RESOLVING )
     dnew->host = STRALLOC( buf );
   else
   {
      from = gethostbyaddr( (char *) &sock.sin_addr,
   sizeof(sock.sin_addr), AF_INET );
      dnew->host = STRALLOC( (char *)( from ? from->h_name : buf) );
   }

   if ( check_total_bans( dnew ) )
   {
         write_to_descriptor (desc,
                        "Your site has been banned from this Mud.\n\r", 0);
         free_desc (dnew);
         set_alarm (0);
         return;
    }

   /*
    * Init descriptor data.
    */

   if ( !last_descriptor && first_descriptor )
   {
DESCRIPTOR_DATA *d;

bug( "New_descriptor: last_desc is NULL, but first_desc is not! ...fixing" );
for ( d = first_descriptor; d; d = d->next )
   if ( !d->next )
 last_descriptor = d;
   }

   LINK( dnew, first_descriptor, last_descriptor, next, prev );

   /*
    * Send the greeting.
    */
   {
extern char * help_greeting;
if ( help_greeting[0] == '.' )
    write_to_buffer( dnew, help_greeting+1, 0 );
else
    write_to_buffer( dnew, help_greeting  , 0 );
   }

   alarm_section = "new_descriptor: set_auth";
   set_auth(dnew);
   alarm_section = "new_descriptor: after set_auth";

   if ( ++num_descriptors > sysdata.maxplayers )
sysdata.maxplayers = num_descriptors;
   if ( sysdata.maxplayers > sysdata.alltimemax )
   {
if ( sysdata.time_of_max )
  DISPOSE(sysdata.time_of_max);
sprintf(buf, "%24.24s", ctime(¤t_time));
sysdata.time_of_max = str_dup(buf);
sysdata.alltimemax = sysdata.maxplayers;
sprintf( log_buf, "Broke all-time maximum player record: %d", sysdata.alltimemax );
log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
save_sysdata( sysdata );
   }
   set_alarm(0);
   return;
}

/*  From Erwin  */

void log_printf(char *fmt, ...)
{
 char buf[MAX_STRING_LENGTH*2];
 va_list args;

 va_start(args, fmt);
 vsprintf(buf, fmt, args);
 va_end(args);

 log_string(buf);
}

And yes, that last chunk from Erwin belongs here so don't omit it.

--------------------------------------------------------

TABLES.C  

add the entries for do_copyover, e.g. 

find:

if ( !str_cmp( name, "do_cook" ))  return do_cook;

underneath, add:

if ( !str_cmp( name, "do_copyover" ))  return do_copyover;

find:

if ( skill == do_cook ) return "do_cook";

underneath, add:

if ( skill == do_copyover ) return "do_copyover";

-----------------------------------------------------------------

act_wiz.c

find:

/*
* Global variables.
*/

char reboot_time[50];
time_t new_boot_time_t;
extern struct tm new_boot_struct;
extern OBJ_INDEX_DATA *obj_index_hash[MAX_KEY_HASH];
extern MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];

and add:

extern int port, control;



Finally, throw this in at the very bottom of the file: 

void do_copyover (CHAR_DATA *ch, char * argument)
{
       FILE *fp;
       DESCRIPTOR_DATA *d, *d_next;
       char buf [100], buf2[100], buf3[100];

       fp = fopen (COPYOVER_FILE, "w";);

       if (!fp)
       {
               send_to_char ("Copyover file not writeable, aborted.\n\r",ch);
               log_printf ("Could not write to copyover file: %s", COPYOVER_FILE);
               perror ("do_copyover:fopen";);
               return;
       }

       /* Consider changing all saved areas here, if you use OLC */

       /* do_asave (NULL, "";); - autosave changed areas */


       sprintf (buf, "\n\r *** COPYOVER by %s - please remain seated!\n\r", ch->name);

       /* For each playing descriptor, save its state */
       for (d = first_descriptor; d; d = d_next)
       {
               CHAR_DATA * och = CH (d);
               d_next = d->next; /* We delete from the list , so need to save this */

               if (!d->character || d->connected < CON_PLAYING ) /* drop those logging on */
               {
                       write_to_descriptor (d->descriptor, "\n\rSorry, we are rebooting. Come back in a few minutes.\n\r", 0);
                       close_socket (d, FALSE); /* throw'em out */
               }
               else
               {
                       fprintf (fp, "%d %s %s\n", d->descriptor, och->name, d->host);
                       if (och->level == 1)
                       {
                               write_to_descriptor (d->descriptor, "Since you are level one, and level one characters do not save, you gain a free level!\n\r", 0);
                               advance_level (och);
                               och->level++; /* Advance_level doesn't do that */
                       }
                       save_char_obj (och);
                       write_to_descriptor (d->descriptor, buf, 0);
               }
       }

       fprintf (fp, "-1\n";);
       fclose (fp);

       /* Close reserve and other always-open files and release other resources */

       fclose (fpReserve);
fclose ( fpLOG );

#ifdef IMC
  imc_shutdown( FALSE );
#endif
#ifdef USE_WEBSVR
if (sysdata.webtoggle==TRUE)
{
 shutdown_web();
}
#endif
#ifdef I3
  if( I3_is_connected() )
  {
I3_savechanlist();
I3_savemudlist();
  }
#endif

       /* exec - descriptors are inherited */

       sprintf (buf, "%d", port);
       sprintf (buf2, "%d", control);
#ifdef I3
   sprintf( buf3, "%d", I3_socket );
#else
   strcpy( buf3, "-1" );
#endif
       execl (EXE_FILE, "smaug", buf, "copyover", buf2, buf3, (char *) NULL);

       /* Failed - sucessful exec will not return */

       perror ("do_copyover: execl";);
       send_to_char ("Copyover FAILED!\n\r",ch);

   /* Here you might want to reopen fpReserve */
   /* Since I'm a neophyte type guy, I'll assume this is
    a good idea and cut and past from main()  */

   if ( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL )
   {
       perror( NULL_FILE );
       exit( 1 );
   }
   if ( ( fpLOG = fopen( NULL_FILE, "r" ) ) == NULL )
   {
       perror( NULL_FILE );
       exit( 1 );
   }

}

-------------------------------------------------------

Use the CEDIT command in-game to add copyover to commands.dat. 

e.g. 

cedit copyover create
cedit copyover level <whatever>
cedit save cmdtable 




[/CODE]

-Updated 9/7 with fixes suggested by Samson

Credit goes to the original copyover-7 code (Erwin, Callidyrr) and to all those who modified SMAUG for the IMMU release of 1.4 revision 4. I take credit for nothing.
       
Pages:<< prev 1 next >>