CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

QUEUE.CPP (190119B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 
     16 /* $Header: /counterstrike/QUEUE.CPP 6     3/14/97 5:12p Steve_tall $ */
     17 /***************************************************************************
     18  ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
     19  *                                                                         *
     20  *                 Project Name : Command & Conquer                        *
     21  *                                                                         *
     22  *                    File Name : QUEUE.CPP                                *
     23  *                                                                         *
     24  *                   Programmer : Bill R. Randolph                         *
     25  *                                                                         *
     26  *                   Start Date : 11/28/95                                 *
     27  *                                                                         *
     28  *                  Last Update : October 14, 1996 [BRR]                   *
     29  *                                                                         *
     30  *-------------------------------------------------------------------------*
     31  * Functions for Queueing Events:                                          *
     32  *   Queue_Mission -- Queue a mega mission event.                          *
     33  *   Queue_Options -- Queue the options event.                             *
     34  *   Queue_Exit -- Add the exit game event to the queue.                   *
     35  *                                                                         *
     36  * Functions for processing Queued Events:											*
     37  *   Queue_AI -- Process all queued events.                                *
     38  *   Queue_AI_Normal -- Process all queued events.                         *
     39  *   Queue_AI_Multiplayer -- Process all queued events.                    *
     40  *                                                                         *
     41  * Main Multiplayer Queue Logic:															*
     42  *   Wait_For_Players -- Waits for other systems to come on-line           *
     43  *   Generate_Timing_Event -- computes & queues a RESPONSE_TIME event      *
     44  *   Process_Send_Period -- timing for sending packets every 'n' frames    *
     45  *   Send_Packets -- sends out events from the OutList                     *
     46  *   Send_FrameSync -- Sends a FRAMESYNC packet                            *
     47  *   Process_Receive_Packet -- processes an incoming packet                *
     48  *   Process_Serial_Packet -- Handles an incoming serial packet            *
     49  *   Can_Advance -- determines if it's OK to advance to the next frame     *
     50  *   Process_Reconnect_Dialog -- processes the reconnection dialog         *
     51  *   Handle_Timeout -- attempts to reconnect; if fails, bails.             *
     52  *   Stop_Game -- stops the game															*
     53  *                                                                         *
     54  * Packet Compression / Decompression:													*
     55  *   Build_Send_Packet -- Builds a big packet from a bunch of little ones.	*
     56  *   Add_Uncompressed_Events -- adds uncompressed events to a packet       *
     57  *   Add_Compressed_Events -- adds compressed events to a packet        	*
     58  *   Breakup_Receive_Packet -- Splits a big packet into little ones.			*
     59  *   Extract_Uncompressed_Events -- extracts events from a packet				*
     60  *   Extract_Compressed_Events -- extracts events from a packet            *
     61  *                                                                         *
     62  * DoList Management:																		*
     63  *   Execute_DoList -- Executes commands from the DoList                   *
     64  *   Clean_DoList -- Cleans out old events from the DoList                 *
     65  *   Queue_Record -- Records the DoList to disk                            *
     66  *   Queue_Playback -- plays back queue entries from a record file         *
     67  *                                                                         *
     68  * Debugging:																					*
     69  *   Compute_Game_CRC -- Computes a CRC value of the entire game.				*
     70  *   Add_CRC -- Adds a value to a CRC                                      *
     71  *   Print_CRCs -- Prints a data file for finding Sync Bugs						*
     72  *   Init_Queue_Mono -- inits mono display                                 *
     73  *   Update_Queue_Mono -- updates mono display                             *
     74  *   Print_Framesync_Values -- displays frame-sync variables               *
     75  *   Check_Mirror -- Checks mirror memory                                  *
     76  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     77 #include	"function.h"
     78 
     79 #ifdef WOLAPI_INTEGRATION
     80 //#include "WolDebug.h"
     81 #include "WolapiOb.h"
     82 extern WolapiObject* pWolapi;
     83 
     84 bool bReconnectDialogCancelled;
     85 #endif
     86 
     87 
     88 /********************************** Defines *********************************/
     89 #define SHOW_MONO	0
     90 
     91 
     92 /********************************** Globals *********************************/
     93 //---------------------------------------------------------------------------
     94 //	GameCRC is the current computed CRC value for this frame.
     95 //	CRC[] is a record of our last 32 game CRC's.
     96 // ColorNames is for debug output in Print_CRCs
     97 //---------------------------------------------------------------------------
     98 static unsigned long GameCRC;
     99 static unsigned long CRC[32] =
    100 	{0,0,0,0,0,0,0,0,0,0,
    101 	 0,0,0,0,0,0,0,0,0,0,
    102 	 0,0,0,0,0,0,0,0,0,0,
    103 	 0,0};
    104 static char *ColorNames[8] = {
    105 	"Yellow",
    106 	"LtBlue",
    107 	"Red",
    108 	"Green",
    109 	"Orange",
    110 	"Grey",
    111 	"Blue",
    112 	"Brown"
    113 };
    114 
    115 //...........................................................................
    116 // Mono debugging variables:
    117 // NetMonoMode: 0 = show connection output, 1 = flowcount output
    118 // NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen
    119 // IsMono: used for taking control of Mono screen away from the engine
    120 //...........................................................................
    121 int NetMonoMode = 1;
    122 int NewMonoMode = 1;
    123 static int IsMono = 0;
    124 
    125 //---------------------------------------------------------------------------
    126 // Several routines return various codes; here's an enum for all of them.
    127 //---------------------------------------------------------------------------
    128 typedef enum RetcodeEnum {
    129 	RC_NORMAL,						// no news is good news
    130 	RC_PLAYER_READY,				// a new player has been heard from
    131 	RC_SCENARIO_MISMATCH,		// scenario mismatch
    132 	RC_DOLIST_FULL,				// DoList is full
    133 	RC_SERIAL_PROCESSED,			// modem: SERIAL packet was processed
    134 	RC_PLAYER_LEFT,				// modem: other player left the game
    135 	RC_HUNG_UP,						// modem has hung up
    136 	RC_NOT_RESPONDING,			// other player not responding (timeout/hung up)
    137 	RC_CANCEL,						// user cancelled
    138 } RetcodeType;
    139 
    140 #ifdef FIXIT_CSII	//	checked - ajw 9/28/98
    141 extern void Enable_Secret_Units(void);
    142 #endif
    143 
    144 /********************************* Prototypes *******************************/
    145 //...........................................................................
    146 // Main multiplayer queue logic
    147 //...........................................................................
    148 static void Queue_AI_Normal(void);
    149 static void Queue_AI_Multiplayer(void);
    150 static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
    151 	int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
    152 	int my_sent, long *their_frame,  unsigned short *their_sent,
    153 	unsigned short *their_recv);
    154 static void Generate_Timing_Event(ConnManClass *net, int my_sent);
    155 static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent);
    156 static void Generate_Process_Time_Event(ConnManClass *net);
    157 static int Process_Send_Period(ConnManClass *net);	//, int init);
    158 static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
    159 	int multi_packet_max, int max_ahead, int my_sent);
    160 static void Send_FrameSync(ConnManClass *net, int cmd_count);
    161 static RetcodeType Process_Receive_Packet(ConnManClass *net,
    162 	char *multi_packet_buf, int id, int packetlen, long *their_frame,
    163 	unsigned short *their_sent, unsigned short *their_recv);
    164 static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
    165 	int first_time);
    166 static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
    167 	unsigned short *their_sent, unsigned short *their_recv);
    168 static int Process_Reconnect_Dialog(CDTimerClass<SystemTimerClass> *timeout_timer,
    169 	long *their_frame, int num_conn, int reconn, int fresh);
    170 static int Handle_Timeout(ConnManClass *net, long *their_frame,
    171 	unsigned short *their_sent, unsigned short *their_recv);
    172 static void Stop_Game(void);
    173 
    174 //...........................................................................
    175 // Packet compression/decompression:
    176 //...........................................................................
    177 static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
    178 	int num_cmds, int cap);
    179 int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size,
    180 	int cap);
    181 int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size,
    182 	int cap);
    183 static int Breakup_Receive_Packet(void *buf, int bufsize );
    184 int Extract_Uncompressed_Events(void *buf, int bufsize);
    185 int Extract_Compressed_Events(void *buf, int bufsize);
    186 
    187 //...........................................................................
    188 // DoList management:
    189 //...........................................................................
    190 static int Execute_DoList(int max_houses, HousesType base_house,
    191 	ConnManClass *net, CDTimerClass<FrameTimerClass> *skip_crc,
    192 //	ConnManClass *net, TCountDownTimerClass *skip_crc,
    193 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
    194 static void Clean_DoList(ConnManClass *net);
    195 static void Queue_Record(void);
    196 static void Queue_Playback(void);
    197 
    198 //...........................................................................
    199 // Debugging:
    200 //...........................................................................
    201 static void Compute_Game_CRC(void);
    202 void Add_CRC(unsigned long *crc, unsigned long val);
    203 static void Print_CRCs(EventClass *ev);
    204 static void Init_Queue_Mono(ConnManClass *net);
    205 static void Update_Queue_Mono(ConnManClass *net, int flow_index);
    206 static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
    207 	int num_connections, unsigned short *their_recv,
    208 	unsigned short *their_sent, unsigned short my_sent);
    209 
    210 extern void Keyboard_Process(KeyNumType &input);
    211 void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net,
    212 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
    213 void Check_Mirror(void);
    214 
    215 
    216 /***************************************************************************
    217  * Queue_Mission -- Queue a mega mission event.                            *
    218  *                                                                         *
    219  * This routine is called when the player causes a change to a game unit. 	*
    220  * The event that initiates the change is queued to as a result of a call 	*
    221  * to this routine.                 													*
    222  *                                                                         *
    223  * INPUT:                                                                  *
    224  *		whom		Whom this mission request applies to (a friendly unit).     *
    225  *    mission	The mission to assign to this object.                       *
    226  *    target	The target of this mission (if any).                        *
    227  *    dest		The movement destination for this mission (if any).         *
    228  *                                                                         *
    229  * OUTPUT:                                                                 *
    230  *		Was the mission request queued successfully?                         *
    231  *                                                                         *
    232  * WARNINGS:                                                               *
    233  *		none.                                                                *
    234  *                                                                         *
    235  * HISTORY:                                                                *
    236  *   09/21/1995 JLB : Created.                                             *
    237  *=========================================================================*/
    238 bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination)
    239 {
    240 	if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) {
    241 		return(false);
    242 	} else {
    243 		return(true);
    244 	}
    245 }
    246 
    247 
    248 /***********************************************************************************************
    249  * Queue_Mission -- Queue a mega mission event, formation override for common speed.           *
    250  *                                                                                             *
    251  *    This routine is called when the player causes a change to a game unit. The event that    *
    252  *    initiates the change is queued to as a result of a call to this routine.                 *
    253  *                                                                                             *
    254  * INPUT:   whom     -- Whom this mission request applies to (a friendly unit).                *
    255  *                                                                                             *
    256  *          mission  -- The mission to assign to this object.                                  *
    257  *                                                                                             *
    258  *          target   -- The target of this mission (if any).                                   *
    259  *                                                                                             *
    260  *          dest     -- The movement destination for this mission (if any).                    *
    261  *																															  *
    262  *				speed	   -- The override speed for this unit.												  *
    263  *																															  *
    264  *				maxspeed -- The override maximum speed for this unit.										  *
    265  *                                                                                             *
    266  * OUTPUT:  Was the mission request queued successfully?                                       *
    267  *                                                                                             *
    268  * WARNINGS:   none                                                                            *
    269  *                                                                                             *
    270  * HISTORY:                                                                                    *
    271  *   09/21/1995 JLB : Created.                                                                 *
    272  *=============================================================================================*/
    273 bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed)
    274 {
    275 	if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) {
    276 		return(false);
    277 	} else {
    278 		return(true);
    279 	}
    280 }
    281 
    282 
    283 /***************************************************************************
    284  * Queue_Options -- Queue the options event.                               *
    285  *                                                                         *
    286  * INPUT:                                                                  *
    287  *		none.																						*
    288  *                                                                         *
    289  * OUTPUT:                                                                 *
    290  *		Was the options screen event queued successfully?                    *
    291  *                                                                         *
    292  * WARNINGS:                                                               *
    293  *		none.																						*
    294  *                                                                         *
    295  * HISTORY:                                                                *
    296  *   09/21/1995 JLB : Created.                                             *
    297  *=========================================================================*/
    298 bool Queue_Options(void)
    299 {
    300 	if (! OutList.Add(EventClass(EventClass::OPTIONS))) {
    301 		return(false);
    302 	}
    303 	else {
    304 		return(true);
    305 	}
    306 
    307 }		/* end of Queue_Options */
    308 
    309 
    310 /***************************************************************************
    311  * Queue_Exit -- Add the exit game event to the queue.                     *
    312  *                                                                         *
    313  * INPUT:                                                                  *
    314  *		none.																						*
    315  *                                                                         *
    316  * OUTPUT:                                                                 *
    317  *		Was the exit event queued successfully?                              *
    318  *                                                                         *
    319  * WARNINGS:                                                               *
    320  *		none.																						*
    321  *                                                                         *
    322  * HISTORY:                                                                *
    323  *   09/21/1995 JLB : Created.                                             *
    324  *=========================================================================*/
    325 bool Queue_Exit(void)
    326 {
    327 	if (! OutList.Add(EventClass(EventClass::EXIT))) {
    328 		return(false);
    329 	}
    330 	else {
    331 		return(true);
    332 	}
    333 
    334 }		/* end of Queue_Exit */
    335 
    336 
    337 /***************************************************************************
    338  * Queue_AI -- Process all queued events.                                  *
    339  *                                                                         *
    340  * INPUT:                                                                  *
    341  *		none.																						*
    342  *                                                                         *
    343  * OUTPUT:                                                                 *
    344  *		none.																						*
    345  *                                                                         *
    346  * WARNINGS:                                                               *
    347  *		none.																						*
    348  *                                                                         *
    349  * HISTORY:                                                                *
    350  *   09/21/1995 JLB : Created.                                             *
    351  *=========================================================================*/
    352 void Queue_AI(void)
    353 {
    354 	if (Session.Play) {
    355 		Queue_Playback();
    356 	}
    357 
    358 	else {
    359 
    360 		switch (Session.Type) {
    361 
    362 			case GAME_SKIRMISH:
    363 			case GAME_NORMAL:
    364 				Queue_AI_Normal();
    365 				break;
    366 
    367 			case GAME_MODEM:
    368 			case GAME_NULL_MODEM:
    369 			case GAME_IPX:
    370 			case GAME_INTERNET:
    371 			case GAME_TEN:
    372 			case GAME_MPATH:
    373 				Queue_AI_Multiplayer();
    374 				break;
    375 		}
    376 	}
    377 
    378 }	/* end of Queue_AI */
    379 
    380 
    381 /***************************************************************************
    382  * Queue_AI_Normal -- Process all queued events.                           *
    383  *                                                                         *
    384  * This is the "normal" version of the queue management routine.  It does 	*
    385  * the following:		  																		*
    386  * - Transfers items in the OutList to the DoList									*
    387  * - Executes any commands in the DoList that are supposed to be done on 	*
    388  *   this frame #			  																	*
    389  * - Cleans out the DoList																	*
    390  *                                                                         *
    391  * INPUT:                                                                  *
    392  *		none.																						*
    393  *                                                                         *
    394  * OUTPUT:                                                                 *
    395  *		none.																						*
    396  *                                                                         *
    397  * WARNINGS:                                                               *
    398  *		none.																						*
    399  *                                                                         *
    400  * HISTORY:                                                                *
    401  *   09/21/1995 JLB : Created.                                             *
    402  *=========================================================================*/
    403 static void Queue_AI_Normal(void)
    404 {
    405 	//------------------------------------------------------------------------
    406 	//	Move events from the OutList (events generated by this player) into the
    407 	//	DoList (the list of events to execute).
    408 	//------------------------------------------------------------------------
    409 	while (OutList.Count) {
    410 		OutList.First().IsExecuted = false;
    411 		if (!DoList.Add(OutList.First())) {
    412 			;
    413 		}
    414 		#ifdef MIRROR_QUEUE
    415 		MirrorList.Add(OutList.First());
    416 		#endif
    417 		OutList.Next();
    418 	}
    419 
    420 	//------------------------------------------------------------------------
    421 	// Save the DoList to disk, if we're in "Record" mode
    422 	//------------------------------------------------------------------------
    423 	if (Session.Record) {
    424 		Queue_Record();
    425 	}
    426 
    427 	//------------------------------------------------------------------------
    428 	// Execute the DoList; if an error occurs, bail out.
    429 	//------------------------------------------------------------------------
    430 	if (!Execute_DoList(1, PlayerPtr->Class->House, NULL, NULL, NULL,
    431 		NULL, NULL)) {
    432 		GameActive = 0;
    433 		return;
    434 	}
    435 
    436 	//------------------------------------------------------------------------
    437 	//	Clean out the DoList
    438 	//------------------------------------------------------------------------
    439 	Clean_DoList(NULL);
    440 
    441 }	/* end of Queue_AI_Normal */
    442 
    443 
    444 /***************************************************************************
    445  * Queue_AI_Multiplayer -- Process all queued events.                      *
    446  *                                                                         *
    447  * This is the network version of the queue management routine.  It does 	*
    448  * the following:																				*
    449  * - If this is the 1st frame, waits for other systems to signal ready		*
    450  * - Generates a timing event, to allow the connection time to be dynamic	*
    451  * - Handles timing related to sending packets every 'n' frames				*
    452  * - Sends outgoing events																	*
    453  * - Frame-syncs to the other systems (see below)									*
    454  * - Executes & cleans out the DoList													*
    455  *                                                                         *
    456  * The Frame-Sync'ing logic is the heart & soul of network play.  It works	*
    457  * by ensuring that any system won't out-run the other system by more than	*
    458  * 'Session.MaxAhead' frames; this in turn ensures that a packet's 			*
    459  * execution frame # won't have been passed by the time that packet is 		*
    460  * received by all systems.																*
    461  *                                                                         *
    462  * To achieve this, the system must keep track of all other system's 		*
    463  * current frame #'s; these are stored in an array called 'their_frame[]'. *
    464  * However, because current frame #'s are sent in FRAMEINFO packets, which *
    465  * don't require an ACK, and command packets are sent in packets requiring *
    466  * an ACK, it's possible for a command packet to get lost, and the next 	*
    467  * frame's FRAMEINFO packet to not get lost; the other system may then 		*
    468  * advance past the frame # the command is to execute on!  So, to prevent 	*
    469  * this, all FRAMEINFO packets include a CommandCount field.  This value 	*
    470  * tells the other system how many events it should have received by this 	*
    471  * time.  This system can therefore keep track of how many commands it's	*
    472  * actually received, and compare it to the CommandCount field, to see if 	*
    473  * it's missed an event packet.  The # of events we've received from each 	*
    474  * system is stored in 'their_recv[]', and the # events they say they've 	*
    475  * sent is stored in 'their_sent[]'.													*
    476  *                                                                         *
    477  * Thus, two conditions must be met in order to advance to the next frame:	*
    478  * - Our current frame # must be < their_frame + Session.MaxAhead				*
    479  * - their_recv[i] must be >= their_sent[i]											*
    480  *                                                                         *
    481  * 'their_frame[] is updated by Process_Receive_Packet()							*
    482  * 'their_recv[] is updated by Process_Receive_Packet()							*
    483  * 'their_sent[] is updated by Process_Receive_Packet()							*
    484  * 'my_sent' is updated by this routine.												*
    485  *                                                                         *
    486  * The order of the arrays their_frame[] etc is the same order the 			*
    487  * connections are created in.  The Sender's ID is passed to 					*
    488  * Connection_Index() to obtain the array index.									*
    489  *                                                                         *
    490  * The only routines allowed to pop up dialogs are:								*
    491  * 	Wait_For_Players() (only pops up the reconnect dialog)					*
    492  * 	Execute_DoList() (tells if out of sync, or packet recv'd too late)	*
    493  *                                                                         *
    494  * Sign-off's are detected by:															*
    495  * - Timing out while waiting for a packet											*
    496  * - Detecting that the other player is now at the score screen or 			*
    497  *   connection dialog (serial)															*
    498  * - If we see an EventClass::EXIT event on the private channel				*
    499  *                                                                         *
    500  * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has 	*
    501  * the following properties:																*
    502  * - It compresses packets, so that the minimum number of bytes are 			*
    503  *   transmitted.  Packets are compressed by extracting all info common to *
    504  *   the events into the packet header, and then sending only the bytes 	*
    505  *   relevant to each type of event.  For instance, if 100 infantry guys 	*
    506  *   are told to move to the same location, the command itself & the 		*
    507  *   location will be included in the 1st movement command only; after 		*
    508  *   that, there will be a rep count then 99 infantry TARGET numbers, 		*
    509  *   identifying all the infantry told to move.										*
    510  * - The protocol also only sends packets out every 'n' frames.  This cuts *
    511  *   the data rate dramatically.  It means that 'Session.MaxAhead' must be *
    512  *   divisible by 'n'; also, the minimum value for 'Session.MaxAhead' is 	*
    513  *   'n * 2', to give both sides some "breathing" room in case a FRAMEINFO	*
    514  *   packet gets missed.																	*
    515  *                                                                         *
    516  * Note:  For synchronization-waiting loops (like waiting to hear from all *
    517  * other players, waiting to advance to the next frame, etc), use 			*
    518  * Net.Num_Connections() rather than Session.NumPlayers; this reflects the *
    519  * actual # of connections, and can be "faked" into playing even when 		*
    520  * there aren't any other players actually there.  A typical example of 	*
    521  * this is playing back a recorded game.  For command-execution loops, use *
    522  * Session.NumPlayers.  This ensures all commands get executed, even if 	*
    523  * there isn't a human generating those commands.									*
    524  *                                                                         *
    525  * The modem works a little differently from the network in this respect:	*
    526  * - The connection has to stay "alive" even if the other player exits to 	*
    527  *   the join dialog.  This prevents each system from timing out & hanging *
    528  *   the modem up.  Thus, packets are sent back & forth & just thrown away,*
    529  *   but each system knows the other is still there.  Messages may be sent	*
    530  *   between systems, though. 															*
    531  * - Destroy_Null_Connection doesn't hang up the modem, so 						*
    532  *   Num_Connections() still reports a value of 1 even though the other 	*
    533  *   player has left.																		*
    534  * - Any waits on Num_Connections() must also check for 							*
    535  *   Session.NumPlayers > 1, to keep from waiting forever if the other 		*
    536  *   guy has left																				*
    537  * - Packets sent to a player who's left require no ACK							*
    538  *                                                                         *
    539  * INPUT:                                                                  *
    540  *		none.																						*
    541  *                                                                         *
    542  * OUTPUT:                                                                 *
    543  *		none.																						*
    544  *                                                                         *
    545  * WARNINGS:                                                               *
    546  *		none.																						*
    547  *                                                                         *
    548  * HISTORY:                                                                *
    549  *   11/21/1995 BRR : Created.                                             *
    550  *=========================================================================*/
    551 static void Queue_AI_Multiplayer(void)
    552 {
    553 	if(Session.Type == GAME_SKIRMISH) return;
    554 
    555 	return;
    556 #if (0)//PG
    557 	//........................................................................
    558 	// Enums:
    559 	//........................................................................
    560 	enum {
    561 		MIXFILE_RESEND_DELTA = 120,	// ticks b/w resends
    562 		MIXFILE_TIMEOUT = 3600*2,		// timeout waiting for mixfiles.
    563 		FRAMESYNC_DLG_TIME = (3*60),	// time until displaying reconnect dialog
    564 		FRAMESYNC_TIMEOUT = (15*60),	// timeout waiting for frame sync packet
    565 	};
    566 
    567 	int timeout_factor = (Session.Type == GAME_INTERNET) ? 6 : 1;
    568 
    569 	//........................................................................
    570 	// Variables for sending, receiving & parsing packets:
    571 	//........................................................................
    572 	ConnManClass *net;					// ptr to access all multiplayer functions
    573 	EventClass packet;					// for sending single frame-sync's
    574 	char *multi_packet_buf;				// buffer for sending/receiving
    575 	int multi_packet_max;				// max length of multi_packet_buf
    576 
    577 	//........................................................................
    578 	// Frame-sync'ing variables
    579 	//........................................................................
    580 	static long
    581 		their_frame[MAX_PLAYERS - 1];	// other players' frame #'s
    582 	static unsigned short
    583 		their_sent[MAX_PLAYERS - 1];	// # cmds other player claims to have sent
    584 	static unsigned short
    585 		their_recv[MAX_PLAYERS - 1];	// # cmds actually received from others
    586 	static unsigned short
    587 		my_sent;								// # cmds I've sent out
    588 
    589 	//........................................................................
    590 	// Timing variables
    591 	//........................................................................
    592 	static CDTimerClass<FrameTimerClass> skip_crc;	// to delay the CRC check
    593 //	static TCountDownTimerClass skip_crc;	// to delay the CRC check
    594 
    595 	//........................................................................
    596 	// Other misc variables
    597 	//........................................................................
    598 	int i;
    599 	RetcodeType rc;
    600 	int reconnect_dlg = 0;				// 1 = the reconnect dialog is displayed
    601 
    602 	//------------------------------------------------------------------------
    603 	//	Initialize the packet buffer pointer & its max size
    604 	//------------------------------------------------------------------------
    605 #if (0)//PG
    606 	if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
    607 		multi_packet_buf = NullModem.BuildBuf;
    608 		multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType);
    609 		net = &NullModem;
    610 	} else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
    611 		multi_packet_buf = Session.MetaPacket;
    612 		multi_packet_max = Session.MetaSize;
    613 		net = &Ipx;
    614 	}
    615 #endif
    616 #if(TEN)
    617 	else if (Session.Type == GAME_TEN) {
    618 		multi_packet_buf = Session.TenPacket;
    619 		multi_packet_max = Session.TenSize;
    620 		net = Ten;
    621 	}
    622 #endif
    623 #if(MPATH)
    624 	else if (Session.Type == GAME_MPATH) {
    625 		multi_packet_buf = Session.MPathPacket;
    626 		multi_packet_max = Session.MPathSize;
    627 		net = MPath;
    628 	}
    629 #endif
    630 
    631 	//------------------------------------------------------------------------
    632 	//	Debug stuff
    633 	//------------------------------------------------------------------------
    634 	Init_Queue_Mono(net);
    635 	Update_Queue_Mono (net, 0);
    636 
    637 	//------------------------------------------------------------------------
    638 	//	Compute the Game's CRC
    639 	//------------------------------------------------------------------------
    640 	Compute_Game_CRC();
    641 	CRC[Frame & 0x001f] = GameCRC;
    642 
    643 	//------------------------------------------------------------------------
    644 	//	If we've just started a game, or loaded a multiplayer game, we must
    645 	// wait for all other systems to signal ready.
    646 	//------------------------------------------------------------------------
    647 	if (Frame==0 || Session.LoadGame) {
    648 		//.....................................................................
    649 		//	Initialize static locals
    650 		//.....................................................................
    651 		for (i = 0; i < MAX_PLAYERS - 1; i++) {
    652 			their_frame[i] = -1;
    653 			their_sent[i] = 0;
    654 			their_recv[i] = 0;
    655 		}
    656 		my_sent = 0;
    657 #ifdef FIXIT_MULTI_SAVE
    658 		skip_crc = 32;
    659 #else
    660 		skip_crc = Frame + 32;
    661 #endif	// FIXIT_MULTI_SAVE
    662 		for (i = 0; i < 32; i++)
    663 			CRC[i] = 0;
    664 
    665 		//.....................................................................
    666 		// If we've loaded a saved game:
    667 		// - If this game was saved as the result of a lost connection, clear
    668 		//   the CRC value so it will always match the other system's
    669 		// - Otherwise, use the GameCRC value, so we'll compare save-game files
    670 		//   rather than scenario INI files
    671 		//.....................................................................
    672 		if (Session.LoadGame) {
    673 			if (Session.EmergencySave)
    674 				ScenarioCRC = 0;
    675 			else
    676 				ScenarioCRC = GameCRC;
    677 		}
    678 
    679 		//.....................................................................
    680 		// Send our initial FRAMESYNC packet
    681 		//.....................................................................
    682 		Send_FrameSync(net, my_sent);
    683 
    684 		//.....................................................................
    685 		// Wait for the other guys
    686 		//.....................................................................
    687 		rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor,
    688 			MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame,
    689 			their_sent, their_recv);
    690 
    691 		if (rc != RC_NORMAL) {
    692 #ifdef WIN32
    693 			if (Session.Type == GAME_INTERNET){
    694 				Register_Game_End_Time();
    695 			}
    696 #endif	//WIN32
    697 
    698 			if (rc == RC_NOT_RESPONDING) {
    699 				WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
    700 			}
    701 			else if (rc == RC_SCENARIO_MISMATCH) {
    702 				WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
    703 			}
    704 			else if (rc == RC_DOLIST_FULL) {
    705 				WWMessageBox().Process(TXT_QUEUE_FULL);
    706 			}
    707 			Stop_Game();
    708 			return;
    709 		}
    710 
    711 		//.....................................................................
    712 		//	Re-initialize frame numbers (in case somebody signed off while I was
    713 		//	waiting for MIX files to load; we would have fallen through, but
    714 		//	their frame # would still be -1).
    715 		//.....................................................................
    716 		for (i = 0; i < MAX_PLAYERS - 1; i++)
    717 			their_frame[i] = 0;
    718 
    719 		//.....................................................................
    720 		//	Reset the network response time computation, now that we're both
    721 		// sending data again  (loading MIX files will have introduced
    722 		// deceptively large values).
    723 		//.....................................................................
    724 		net->Reset_Response_Time();
    725 
    726 		//.....................................................................
    727 		// Initialize the frame timers
    728 		//.....................................................................
    729 		if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    730 			Process_Send_Period(net);//, 1);
    731 		}
    732 
    733 		//.....................................................................
    734 		// Turn off our special load-game flags
    735 		//.....................................................................
    736 		if (Session.LoadGame) {
    737 			Session.EmergencySave = false;
    738 			Session.LoadGame = false;
    739 		}
    740 
    741 	} 	// end of Frame 0 wait
    742 
    743 	//------------------------------------------------------------------------
    744 	// Adjust connection timing parameters every 128 frames.
    745 	//------------------------------------------------------------------------
    746 
    747 	else if ( (Frame & 0x007f) == 0) {
    748 		//
    749 		// If we're using the new spiffy protocol, do proper timing handling.
    750 		// If we're the net "master", compute our desired frame rate & new
    751 		// 'MaxAhead' value.
    752 		//
    753 		if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    754 
    755 			//
    756 			// All systems will transmit their required process time.
    757 			//
    758 			Generate_Process_Time_Event(net);
    759 
    760 			//
    761 			// The game "host" will transmit timing adjustment events.
    762 			//
    763 			if (Session.Am_I_Master()) {
    764 				Generate_Real_Timing_Event(net, my_sent);
    765 			}
    766 		} else {
    767 			//
    768 			// For the older protocols, do the old broken timing handling.
    769 			//
    770 			Generate_Timing_Event(net, my_sent);
    771 		}
    772 	}
    773 
    774 	//------------------------------------------------------------------------
    775 	// Only process every 'FrameSendRate' frames
    776 	//------------------------------------------------------------------------
    777 	if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    778 		if (!Process_Send_Period(net)) {	//, 0)) {
    779 			if (IsMono) {
    780 				MonoClass::Disable();
    781 			}
    782 			return;
    783 		}
    784 	}
    785 
    786 	//------------------------------------------------------------------------
    787 	// Send our data packet(s); update my command-sent counter
    788 	//------------------------------------------------------------------------
    789 	my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max,
    790 		Session.MaxAhead, my_sent);
    791 
    792 	//------------------------------------------------------------------------
    793 	//	If this is our first time through, we're done.
    794 	//------------------------------------------------------------------------
    795 	if (Frame==0) {
    796 		if (IsMono) {
    797 			MonoClass::Disable();
    798 		}
    799 		return;
    800 	}
    801 
    802 	//------------------------------------------------------------------------
    803 	//	Frame-sync'ing: wait until it's OK to advance to the next frame.
    804 	//------------------------------------------------------------------------
    805 #ifdef FIXIT_VERSION_3
    806 	int iFramesyncTimeout;
    807 	if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 )
    808 		//	Shortened resync timeout for non-2 player games.
    809 		iFramesyncTimeout = 5 * 60;		//	One minute.
    810 	else
    811 		iFramesyncTimeout = FRAMESYNC_TIMEOUT;
    812 
    813 	rc = Wait_For_Players (0, net,
    814 		(Session.MaxAhead << 3),
    815 		MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
    816 		iFramesyncTimeout * (2*timeout_factor),
    817 		multi_packet_buf, my_sent, their_frame,
    818 		their_sent, their_recv);
    819 #else
    820 		rc = Wait_For_Players (0, net,
    821 		(Session.MaxAhead << 3),
    822 		MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
    823 		FRAMESYNC_TIMEOUT* (2*timeout_factor),
    824 		multi_packet_buf, my_sent, their_frame,
    825 		their_sent, their_recv);
    826 #endif
    827 
    828 	if (rc != RC_NORMAL) {
    829 #ifdef WIN32
    830 			if (Session.Type == GAME_INTERNET){
    831 				Register_Game_End_Time();
    832 #ifdef WOLAPI_INTEGRATION
    833 				//	New rule - if you cancel a waiting to reconnect dialog, you lose.
    834 				bReconnectDialogCancelled = ( rc == RC_CANCEL );
    835 #endif
    836 			}
    837 #endif	//WIN32
    838 		if (rc == RC_NOT_RESPONDING) {
    839 			WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
    840 		}
    841 		else if (rc == RC_SCENARIO_MISMATCH) {
    842 			WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
    843 		}
    844 		else if (rc == RC_DOLIST_FULL) {
    845 			WWMessageBox().Process(TXT_QUEUE_FULL);
    846 		}
    847 		Stop_Game();
    848 		return;
    849 	}
    850 
    851 	//------------------------------------------------------------------------
    852 	//	Save the DoList to disk, if we're in "Record" mode
    853 	//------------------------------------------------------------------------
    854 	if (Session.Record) {
    855 		Queue_Record();
    856 	}
    857 
    858 	//------------------------------------------------------------------------
    859 	// Execute the DoList; if an error occurs, bail out.
    860 	//------------------------------------------------------------------------
    861 	if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc,
    862 		their_frame, their_sent, their_recv)) {
    863 #ifdef WIN32
    864 		if (Session.Type == GAME_INTERNET){
    865 			Register_Game_End_Time();
    866 		}
    867 #endif	//WIN32
    868 		Stop_Game();
    869 		return;
    870 	}
    871 
    872 	//------------------------------------------------------------------------
    873 	//	Clean out the DoList
    874 	//------------------------------------------------------------------------
    875 	Clean_DoList(net);
    876 
    877 	if (IsMono) {
    878 		MonoClass::Disable();
    879 	}
    880 #endif
    881 }	// end of Queue_AI_Multiplayer
    882 
    883 
    884 /***************************************************************************
    885  * Wait_For_Players -- Waits for other systems to come on-line             *
    886  *                                                                         *
    887  * This routine performs the most critical logic in multiplayer; that of	*
    888  * synchronizing my frame number with those of the other systems.				*
    889  *                                                                         *
    890  * INPUT:                                                                  *
    891  *		first_time				1 = 1st time this routine is called					*
    892  *		net						ptr to connection manager								*
    893  *		resend_delta			time (ticks) between FRAMESYNC resends				*
    894  *		dialog_time				time (ticks) until pop up a reconnect dialog		*
    895  *		timeout					time (ticks) until we give up the ghost			*
    896  *		multi_packet_buf		buffer to store packets in								*
    897  *		my_sent					# commands I've sent so far							*
    898  *		their_frame				array of their frame #'s								*
    899  *		their_sent				array of their CommandCount values					*
    900  *		their_recv				array of # cmds I've received from them			*
    901  *                                                                         *
    902  * OUTPUT:                                                                 *
    903  *		RC_NORMAL				OK to advance to the next frame						*
    904  *		RC_CANCEL				user hit 'Cancel' at the timeout countdown dlg	*
    905  *		RC_NOT_RESPONDING		other player(s) not responding						*
    906  *		RC_SCENARIO_MISMATCH	scenario's don't match (first_time only)			*
    907  *		RC_DOLIST_FULL			DoList was full											*
    908  *                                                                         *
    909  * WARNINGS:                                                               *
    910  *		none.																						*
    911  *                                                                         *
    912  * HISTORY:                                                                *
    913  *   11/21/1995 BRR : Created.                                             *
    914  *=========================================================================*/
    915 static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
    916 	int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
    917 	int my_sent, long *their_frame, unsigned short *their_sent,
    918 	unsigned short *their_recv)
    919 {
    920 	//........................................................................
    921 	// Variables for sending, receiving & parsing packets:
    922 	//........................................................................
    923 	EventClass *event;					// event ptr for parsing incoming packets
    924 	int packetlen;							// size of meta-packet sent, & received
    925 	int id;									// id of other player
    926 	int messages_this_loop;				// to limit # messages processed each loop
    927 	int message_limit;					// max # messages we'll read each frame
    928 
    929 	//........................................................................
    930 	// Variables used only if 'first_time':
    931 	//........................................................................
    932 	int num_ready;							// # players signalling ready
    933 
    934 	//........................................................................
    935 	// Timing variables
    936 	//........................................................................
    937 	CDTimerClass<SystemTimerClass> retry_timer;		// time between FRAMESYNC packet resends
    938 	CDTimerClass<SystemTimerClass> dialog_timer;	// time to pop up a dialog
    939 	CDTimerClass<SystemTimerClass> timeout_timer;	// general-purpose timeout
    940 
    941 	//........................................................................
    942 	// Dialog variables
    943 	//........................................................................
    944 	int reconnect_dlg = 0;				// 1 = the reconnect dialog is displayed
    945 
    946 	//........................................................................
    947 	// Other misc variables
    948 	//........................................................................
    949 	KeyNumType input;						// for user input
    950 	int x,y;									// for map input
    951 	RetcodeType rc;
    952 
    953 	//------------------------------------------------------------------------
    954 	// Wait to hear from all other players
    955 	//------------------------------------------------------------------------
    956 	num_ready = 0;
    957 	retry_timer = resend_delta;	// time to retry
    958 	dialog_timer = dialog_time;	// time to show dlg
    959 	timeout_timer = timeout;		// time to bail out
    960 
    961 	while (1) {
    962 		Keyboard->Check();
    963 
    964 		Update_Queue_Mono (net, 2);
    965 
    966 		//---------------------------------------------------------------------
    967 		//	Resend a frame-sync packet if longer than one propagation delay goes
    968 		// by; this prevents a "deadlock".  If he's waiting for me to advance,
    969 		// but has missed my last few FRAMEINFO packets, I may be waiting for
    970 		// him to advance.  Resending a FRAMESYNC ensures he knows what frame
    971 		// number I'm on.
    972 		//---------------------------------------------------------------------
    973 		if (!retry_timer) {
    974 			retry_timer = resend_delta;		// time to retry
    975 			Update_Queue_Mono (net, 3);
    976 			Send_FrameSync(net, my_sent);
    977 		}
    978 
    979 		//---------------------------------------------------------------------
    980 		//	Service the connections
    981 		//---------------------------------------------------------------------
    982 		net->Service();
    983 
    984 		//---------------------------------------------------------------------
    985 		//	Pop up a reconnect dialog if enough time goes by
    986 		//---------------------------------------------------------------------
    987 		if (!dialog_timer && SpecialDialog==SDLG_NONE) {
    988 			if (reconnect_dlg == 0 && first_time == 0) {
    989 				FILE *fp;
    990 				int i;
    991 				HouseClass *housep;
    992 
    993 				fp = fopen("recon.txt","wt");
    994 				if (fp) {
    995 					fprintf(fp,"# Connections: %d\n",net->Num_Connections());
    996 					fprintf(fp,"   My Frame #: %d\n",Frame);
    997 					for (i = 0; i < net->Num_Connections(); i++) {
    998 						housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i)));
    999 						fprintf(fp,"%15s: Their Sent:%d  Their Recv:%d  Their Frame:%d\n",
   1000 							housep->IniName, their_sent[i], their_recv[i], their_frame[i]);
   1001 					}
   1002 					fclose(fp);
   1003 				}
   1004 
   1005 #ifdef WOLAPI_INTEGRATION
   1006 				//	"Reconnecting" dialog is about to be shown.
   1007 				//	At this point, begin wolapi "disconnect pinging", if appropriate.
   1008 				if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament )
   1009 					pWolapi->Init_DisconnectPinging();
   1010 #endif
   1011 			}
   1012 
   1013 			if (Process_Reconnect_Dialog(&timeout_timer, their_frame,				//	(Returns immediately.)
   1014 				net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) {
   1015 				return (RC_CANCEL);
   1016 			}
   1017 			reconnect_dlg = 1;
   1018 
   1019 #ifdef WOLAPI_INTEGRATION
   1020 				//	Continue wolapi "disconnect pinging", if appropriate.
   1021 				if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging )
   1022 					pWolapi->Pump_DisconnectPinging();
   1023 #endif
   1024 		}
   1025 
   1026 		//---------------------------------------------------------------------
   1027 		//	Exit if too much time goes by (the other system has crashed or
   1028 		// bailed)
   1029 		//---------------------------------------------------------------------
   1030 		if (!timeout_timer) {
   1031 			//..................................................................
   1032 			// For the first-time run, just give up; something's wrong.
   1033 			//..................................................................
   1034 			if (first_time) {
   1035 				return (RC_NOT_RESPONDING);
   1036 			}
   1037 			//..................................................................
   1038 			// Otherwise, we're in the middle of a game; so, the modem &
   1039 			// network must deal with a timeout differently.
   1040 			//..................................................................
   1041 			else {
   1042 				Update_Queue_Mono (net, 4);
   1043 
   1044 				if (Handle_Timeout(net, their_frame, their_sent, their_recv)) {
   1045 					Map.Flag_To_Redraw(true);	// erase modem reconnect dialog
   1046 					Map.Render();
   1047 					retry_timer = resend_delta;
   1048 					dialog_timer = dialog_time;
   1049 					timeout_timer = timeout;
   1050 				}
   1051 #ifdef FIXIT_MULTI_SAVE
   1052 #ifdef FIXIT_VERSION_3
   1053 				else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) {
   1054 #else
   1055 				else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) &&
   1056 					        PlayingAgainstVersion != VERSION_RED_ALERT_104) {
   1057 #endif
   1058 					if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING,
   1059 						TXT_YES, TXT_NO, TXT_NONE) == 0) {
   1060 						Session.EmergencySave = 1;
   1061 //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
   1062 //Print_CRCs(NULL);
   1063 //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
   1064 //	Scen.RandomNumber.Count1,
   1065 //	Scen.RandomNumber.Count2,
   1066 //	Scen.RandomNumber.Seed);
   1067 						Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
   1068 //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
   1069 //	Scen.RandomNumber.Count1,
   1070 //	Scen.RandomNumber.Count2,
   1071 //	Scen.RandomNumber.Seed);
   1072 						Session.EmergencySave = 0;
   1073 					}
   1074 					return (RC_CANCEL);
   1075 				}
   1076 #endif	// FIXIT_MULTI_SAVE
   1077 				else {
   1078 					return (RC_NOT_RESPONDING);
   1079 				}
   1080 			}
   1081 		}
   1082 
   1083 		//---------------------------------------------------------------------
   1084 		//	Check for an incoming message.  We must still process commands
   1085 		// even if 'first_time' is set, in case the other system got my 1st
   1086 		// FRAMESYNC, but I didn't get his; he'll be at the next frame, and
   1087 		// may be sending commands.
   1088 		// We have to limit the number of incoming messages we handle; it's
   1089 		// possible to go into an infinite loop processing modem messages.
   1090 		// (This feature is disabled for Ten; we need to keep the TCP buffers
   1091 		// clear, so we read all the packets we can every time.)
   1092 		//---------------------------------------------------------------------
   1093 		messages_this_loop = 0;
   1094 		message_limit = 5;
   1095 
   1096 		if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
   1097 			message_limit = 9999;
   1098 		}
   1099 
   1100 		while ( (messages_this_loop++ < message_limit) &&
   1101 			net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) {
   1102 
   1103 			Keyboard->Check();
   1104 
   1105 			Update_Queue_Mono (net, 5);
   1106 
   1107 			/*..................................................................
   1108 			Get an event ptr to the incoming message
   1109 			..................................................................*/
   1110 			event = (EventClass *)multi_packet_buf;
   1111 
   1112 			//------------------------------------------------------------------
   1113 			// Special processing for a modem game: process SERIAL packets
   1114 			//------------------------------------------------------------------
   1115 			if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
   1116 				rc = Process_Serial_Packet(multi_packet_buf, first_time);
   1117 				//...............................................................
   1118 				// SERIAL packet received & processed
   1119 				//...............................................................
   1120 				if (rc == RC_SERIAL_PROCESSED) {
   1121 					net->Service();
   1122 					retry_timer = resend_delta;
   1123 					dialog_timer = dialog_time;
   1124 					timeout_timer = timeout;
   1125 					continue;
   1126 				}
   1127 				//...............................................................
   1128 				// other player has left the game
   1129 				//...............................................................
   1130 				else if (rc == RC_PLAYER_LEFT) {
   1131 					if (first_time) {
   1132 						num_ready++;
   1133 					}
   1134 					break;
   1135 				}
   1136 				//...............................................................
   1137 				// Connection was lost
   1138 				//...............................................................
   1139 				else if (rc == RC_HUNG_UP) {
   1140 #ifdef FIXIT_MULTI_SAVE
   1141 #ifndef FIXIT_VERSION_3
   1142 					if (PlayingAgainstVersion != VERSION_RED_ALERT_104){
   1143 #endif
   1144 						if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP,
   1145 							TXT_YES, TXT_NO, TXT_NONE) == 0) {
   1146 							Session.EmergencySave = 1;
   1147 	//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
   1148 	//Print_CRCs(NULL);
   1149 	//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
   1150 	//	Scen.RandomNumber.Count1,
   1151 	//	Scen.RandomNumber.Count2,
   1152 	//	Scen.RandomNumber.Seed);
   1153 							Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
   1154 	//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
   1155 	//	Scen.RandomNumber.Count1,
   1156 	//	Scen.RandomNumber.Count2,
   1157 	//	Scen.RandomNumber.Seed);
   1158 							Session.EmergencySave = 0;
   1159 						}
   1160 						return (RC_CANCEL);
   1161 #ifndef FIXIT_VERSION_3
   1162 					}else{
   1163 						return (RC_NOT_RESPONDING);
   1164 					}
   1165 #endif
   1166 
   1167 #else
   1168 					return (RC_NOT_RESPONDING);
   1169 #endif	// FIXIT_MULTI_SAVE
   1170 				}
   1171 				//...............................................................
   1172 				// If it was any other type of serial packet, break
   1173 				//...............................................................
   1174 				else if (rc != RC_NORMAL) {
   1175 					break;
   1176 				}
   1177 			}
   1178 
   1179 			//------------------------------------------------------------------
   1180 			//	Process the incoming packet
   1181 			//------------------------------------------------------------------
   1182 			rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen,
   1183 				their_frame, their_sent, their_recv);
   1184 			//..................................................................
   1185 			// New player heard from
   1186 			//..................................................................
   1187 			if (rc == RC_PLAYER_READY) {
   1188 				num_ready++;
   1189 			}
   1190 			//..................................................................
   1191 			// Scenario's don't match
   1192 			//..................................................................
   1193 			else if (rc == RC_SCENARIO_MISMATCH) {
   1194 				return (RC_SCENARIO_MISMATCH);
   1195 			}
   1196 			//..................................................................
   1197 			// DoList was full
   1198 			//..................................................................
   1199 			else if (rc == RC_DOLIST_FULL) {
   1200 				return (RC_DOLIST_FULL);
   1201 			}
   1202 
   1203 			//..................................................................
   1204 			//	Service the connection, to clean out the receive queues
   1205 			//..................................................................
   1206 			net->Service();
   1207 		}
   1208 
   1209 		//---------------------------------------------------------------------
   1210 		// Debug output
   1211 		//---------------------------------------------------------------------
   1212 		Print_Framesync_Values(Frame, Session.MaxAhead, net->Num_Connections(),
   1213 			their_recv, their_sent, my_sent);
   1214 
   1215 		//---------------------------------------------------------------------
   1216 		//	Attempt to advance to the next frame.
   1217 		//---------------------------------------------------------------------
   1218 		//.....................................................................
   1219 		// For the first-time run, just check to see if we've heard from
   1220 		// everyone.
   1221 		//.....................................................................
   1222 		if (first_time) {
   1223 			if (num_ready >= net->Num_Connections()) {
   1224 			break;
   1225 			}
   1226 		}
   1227 		//.....................................................................
   1228 		// For in-game processing, we have to check their_sent, their_recv,
   1229 		// their_frame, etc.
   1230 		//.....................................................................
   1231 		else {
   1232 			if (Can_Advance(net, Session.MaxAhead, their_frame, their_sent,
   1233 				their_recv)) {
   1234 				break;
   1235 			}
   1236 		}
   1237 
   1238 		//---------------------------------------------------------------------
   1239 		//	Service game stuff.  Servicing the map's input, and rendering the
   1240 		//	map, allows the map to scroll even though we're hung up waiting for
   1241 		//	packets.  Don't do this if 'first_time' is set, since users could be
   1242 		// waiting a very long time for all systems to load the scenario, and
   1243 		// it gets frustrating being able to scroll around without doing
   1244 		// anything.
   1245 		//---------------------------------------------------------------------
   1246 		Call_Back();
   1247 		if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) {
   1248 #ifdef WIN32
   1249 			WWMouse->Erase_Mouse(&HidPage, TRUE);
   1250 #endif	//WIN32
   1251 			Map.Input(input, x, y);
   1252 			if (input)
   1253 				Keyboard_Process(input);
   1254 			Map.Render();
   1255 		}
   1256 
   1257 	}	/* end of while */
   1258 
   1259 	//------------------------------------------------------------------------
   1260 	//	If the reconnect dialog was shown, force the map to redraw.
   1261 	//------------------------------------------------------------------------
   1262 	if (reconnect_dlg) {
   1263 		Map.Flag_To_Redraw(true);
   1264 		Map.Render();
   1265 	}
   1266 
   1267 	return (RC_NORMAL);
   1268 
   1269 }	// end of Wait_For_Players
   1270 
   1271 
   1272 /***************************************************************************
   1273  * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event        *
   1274  *                                                                         *
   1275  * This routine adjusts the connection timing on the local system; it also	*
   1276  * optionally generates a RESPONSE_TIME event, to tell all systems to		*
   1277  * dynamically adjust the current MaxAhead value.  This allows both the		*
   1278  * MaxAhead & the connection retry logic to have dynamic timing, to adjust	*
   1279  * to varying line conditions.															*
   1280  *                                                                         *
   1281  * INPUT:                                                                  *
   1282  *		net			ptr to connection manager											*
   1283  *		my_sent		# commands I've sent out so far									*
   1284  *                                                                         *
   1285  * OUTPUT:                                                                 *
   1286  *		none.																						*
   1287  *                                                                         *
   1288  * WARNINGS:                                                               *
   1289  *		none.																						*
   1290  *                                                                         *
   1291  * HISTORY:                                                                *
   1292  *   11/21/1995 BRR : Created.                                             *
   1293  *=========================================================================*/
   1294 static void Generate_Timing_Event(ConnManClass *net, int my_sent)
   1295 {
   1296 	unsigned long resp_time;			// connection response time, in ticks
   1297 	EventClass ev;
   1298 
   1299 	//
   1300 	// For now, TEN & MPATH don't measure the net's response time, so there's
   1301 	// no point in adjusting our timing.  Do nothing.
   1302 	//
   1303 	if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
   1304 		return;
   1305 	}
   1306 
   1307 	//------------------------------------------------------------------------
   1308 	// Measure the current connection response time.  This time will be in
   1309 	// 60ths of a second, and represents full round-trip time of a packet.
   1310 	// To convert to one-way packet time, divide by 2; to convert to game
   1311 	// frames, divide again by 4, assuming a game rate of 15 fps.
   1312 	//------------------------------------------------------------------------
   1313 	resp_time = net->Response_Time();
   1314 
   1315 	//------------------------------------------------------------------------
   1316 	//	Adjust my connection retry timing; only do this if I've sent out more
   1317 	//	than 5 commands, so I know I have a measure of the response time.
   1318 	//------------------------------------------------------------------------
   1319 	if (my_sent > 5) {
   1320 
   1321 		net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1322 
   1323 		//.....................................................................
   1324 		// If I'm the network "master", I'm also responsible for updating the
   1325 		// MaxAhead value on all systems, so do that here too.
   1326 		//.....................................................................
   1327 		if (Session.Am_I_Master()) {
   1328 			ev.Type = EventClass::RESPONSE_TIME;
   1329 			//..................................................................
   1330 			// For multi-frame compressed events, the MaxAhead must be an even
   1331 			// multiple of the FrameSendRate.
   1332 			//..................................................................
   1333 			if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   1334 				ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) +
   1335 					(Session.FrameSendRate - 1)) / Session.FrameSendRate) *
   1336 					Session.FrameSendRate), (Session.FrameSendRate * 2) );
   1337 			}
   1338 			//..................................................................
   1339 			// For sending packets every frame, just use the 1-way connection
   1340 			// response time.
   1341 			//..................................................................
   1342 			else {
   1343 				if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
   1344 					ev.Data.FrameInfo.Delay = max( (resp_time / 8),
   1345 						 MODEM_MIN_MAX_AHEAD );
   1346 				} else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
   1347 					ev.Data.FrameInfo.Delay = max( (resp_time / 8),
   1348 						 NETWORK_MIN_MAX_AHEAD );
   1349 				}
   1350 				else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
   1351 					ev.Data.FrameInfo.Delay = max( (resp_time / 8),
   1352 						 MODEM_MIN_MAX_AHEAD );
   1353 				}
   1354 			}
   1355 			OutList.Add(ev);
   1356 		}
   1357 	}
   1358 
   1359 }	// end of Generate_Timing_Event
   1360 
   1361 
   1362 /***************************************************************************
   1363  * Generate_Real_Timing_Event -- Generates a TIMING event                  *
   1364  *                                                                         *
   1365  * INPUT:                                                                  *
   1366  *		net			ptr to connection manager											*
   1367  *		my_sent		# commands I've sent out so far									*
   1368  *                                                                         *
   1369  * OUTPUT:                                                                 *
   1370  *		none.																						*
   1371  *                                                                         *
   1372  * WARNINGS:                                                               *
   1373  *		none.																						*
   1374  *                                                                         *
   1375  * HISTORY:                                                                *
   1376  *   07/02/1996 BRR : Created.                                             *
   1377  *=========================================================================*/
   1378 static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent)
   1379 {
   1380 	unsigned long resp_time;			// connection response time, in ticks
   1381 	EventClass ev;
   1382 	int highest_ticks;
   1383 	int i;
   1384 	int specified_frame_rate;
   1385 	int maxahead;
   1386 
   1387 	//
   1388 	// If we haven't sent out at least 5 guaranteed-delivery packets, don't
   1389 	// bother trying to measure our connection response time; just return.
   1390 	//
   1391 	if (my_sent < 5) {
   1392 		return;
   1393 	}
   1394 
   1395 	//
   1396 	// Find the highest processing time we have stored
   1397 	//
   1398 	highest_ticks = 0;
   1399 	for (i = 0; i < Session.Players.Count(); i++) {
   1400 
   1401 		//
   1402 		// If we haven't heard from all systems yet, bail out.
   1403 		//
   1404 		if (Session.Players[i]->Player.ProcessTime == -1) {
   1405 			return;
   1406 		}
   1407 		if (Session.Players[i]->Player.ProcessTime > highest_ticks) {
   1408 			highest_ticks = Session.Players[i]->Player.ProcessTime;
   1409 		}
   1410 	}
   1411 
   1412 	//
   1413 	// Compute our "desired" frame rate as the lower of:
   1414 	// - What the user has dialed into the options screen
   1415 	// - What we're really able to run at
   1416 	//
   1417 	if (highest_ticks == 0) {
   1418 		Session.DesiredFrameRate = 60;
   1419 	} else {
   1420 		Session.DesiredFrameRate = 60 / highest_ticks;
   1421 	}
   1422 
   1423 	if (Options.GameSpeed == 0) {
   1424 		specified_frame_rate = 60;
   1425 	} else {
   1426 		specified_frame_rate = 60 / Options.GameSpeed;
   1427 	}
   1428 
   1429 	Session.DesiredFrameRate = MIN (Session.DesiredFrameRate, specified_frame_rate);
   1430 
   1431 	//
   1432 	// Measure the current connection response time.  This time will be in
   1433 	// 60ths of a second, and represents full round-trip time of a packet.
   1434 	// To convert to one-way packet time, divide by 2; to convert to game
   1435 	// frames, ....uh....
   1436 	//
   1437 	resp_time = net->Response_Time();
   1438 
   1439 	//
   1440 	// Compute our new 'MaxAhead' value, based upon the response time of our
   1441 	// connection and our desired frame rate.
   1442 	// 'MaxAhead' in frames is:
   1443 	//
   1444 	// (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec)
   1445 	//
   1446 	// resp_time is divided by 2 because, as reported, it represents a round-
   1447 	// trip, and we only want to use a one-way trip.
   1448 	//
   1449 	maxahead = (resp_time * Session.DesiredFrameRate) / (2 * 60);
   1450 
   1451 	//
   1452 	// Now, we have to round 'maxahead' so it's an even multiple of our
   1453 	// send rate.  It also must be at least thrice the FrameSendRate.
   1454 	// (Isn't "thrice" a cool word?)
   1455 	//
   1456 	maxahead = ((maxahead + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate;
   1457 	maxahead = MAX (maxahead, (int)Session.FrameSendRate * 3);
   1458 
   1459 	ev.Type = EventClass::TIMING;
   1460 	ev.Data.Timing.DesiredFrameRate = Session.DesiredFrameRate;
   1461 	ev.Data.Timing.MaxAhead = maxahead;
   1462 
   1463 	OutList.Add(ev);
   1464 
   1465 	//
   1466 	//	Adjust my connection retry timing.  These values set the retry timeout
   1467 	// to just over one round-trip time, the 'maxretries' to -1, and the
   1468 	// connection timeout to allow for about 4 retries.
   1469 	//
   1470 	//net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1471 
   1472 	if (Session.Type == GAME_INTERNET) {
   1473 		net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
   1474 	}else{
   1475 		net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1476 	}
   1477 
   1478 }
   1479 
   1480 
   1481 /***************************************************************************
   1482  * Generate_Process_Time_Event -- Generates a PROCESS_TIME event           *
   1483  *                                                                         *
   1484  * INPUT:                                                                  *
   1485  *		net			ptr to connection manager											*
   1486  *                                                                         *
   1487  * OUTPUT:                                                                 *
   1488  *		none.																						*
   1489  *                                                                         *
   1490  * WARNINGS:                                                               *
   1491  *		none.																						*
   1492  *                                                                         *
   1493  * HISTORY:                                                                *
   1494  *   07/02/1996 BRR : Created.                                             *
   1495  *=========================================================================*/
   1496 static void Generate_Process_Time_Event(ConnManClass *net)
   1497 {
   1498 	EventClass ev;
   1499 	int avgticks;
   1500 	unsigned long resp_time;			// connection response time, in ticks
   1501 
   1502 	//
   1503 	// Measure the current connection response time.  This time will be in
   1504 	// 60ths of a second, and represents full round-trip time of a packet.
   1505 	// To convert to one-way packet time, divide by 2; to convert to game
   1506 	// frames, ....uh....
   1507 	//
   1508 	resp_time = net->Response_Time();
   1509 
   1510 	//
   1511 	//	Adjust my connection retry timing.  These values set the retry timeout
   1512 	// to just over one round-trip time, the 'maxretries' to -1, and the
   1513 	// connection timeout to allow for about 4 retries.
   1514 	//
   1515 	//net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1516 	if (Session.Type == GAME_INTERNET) {
   1517 		net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
   1518 	}else{
   1519 		net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1520 	}
   1521 
   1522 
   1523 	if (IsMono) {
   1524 		MonoClass::Enable();
   1525 		Mono_Set_Cursor(0,23);
   1526 		Mono_Printf("Processing Ticks:%03d Frames:%03d\n", Session.ProcessTicks,Session.ProcessFrames);
   1527 		MonoClass::Disable();
   1528 	}
   1529 
   1530 	avgticks = Session.ProcessTicks / Session.ProcessFrames;
   1531 
   1532 	ev.Type = EventClass::PROCESS_TIME;
   1533 	ev.Data.ProcessTime.AverageTicks = avgticks;
   1534 	OutList.Add(ev);
   1535 
   1536 	Session.ProcessTicks = 0;
   1537 	Session.ProcessFrames = 0;
   1538 }
   1539 
   1540 
   1541 /***************************************************************************
   1542  * Process_Send_Period -- timing for sending packets every 'n' frames      *
   1543  *                                                                         *
   1544  * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only.	*
   1545  * It determines if it's time to send a packet or not.							*
   1546  *                                                                         *
   1547  * INPUT:                                                                  *
   1548  *		net		ptr to connection manager												*
   1549  *                                                                         *
   1550  * OUTPUT:                                                                 *
   1551  *		1 = it's time to send a packet; 0 = don't send a packet this frame.	*
   1552  *                                                                         *
   1553  * WARNINGS:                                                               *
   1554  *                                                                         *
   1555  * HISTORY:                                                                *
   1556  *   11/21/1995 BRR : Created.                                             *
   1557  *=========================================================================*/
   1558 static int Process_Send_Period(ConnManClass *net)	//, int init)
   1559 {
   1560 	//------------------------------------------------------------------------
   1561 	// If the current frame # is not an even multiple of 'FrameSendRate', then
   1562 	// it's not time to send a packet; just return.
   1563 	//------------------------------------------------------------------------
   1564 	if (Frame != (((Frame + (Session.FrameSendRate - 1)) /
   1565 		Session.FrameSendRate) * Session.FrameSendRate) ) {
   1566 
   1567 		net->Service();
   1568 
   1569 		if (IsMono) {
   1570 			MonoClass::Disable();
   1571 		}
   1572 
   1573 		return (0);
   1574 	}
   1575 
   1576 	return (1);
   1577 
   1578 
   1579 }	// end of Process_Send_Period
   1580 
   1581 
   1582 /***************************************************************************
   1583  * Send_Packets -- sends out events from the OutList                       *
   1584  *                                                                         *
   1585  * This routine computes how many events can be sent this frame, and then	*
   1586  * builds the "meta-packet" & sends it.												*
   1587  *                                                                         *
   1588  * The 'cap' value is the max # of events we can send.  Ideally, it should	*
   1589  * be based upon the bandwidth of our connection.  Currently, it's just		*
   1590  * hardcoded to prevent the modem from having to resend "too much" data,	*
   1591  * which is about 200 bytes per frame.													*
   1592  *                                                                         *
   1593  * INPUT:                                                                  *
   1594  *		net						ptr to connection manager								*
   1595  *		multi_packet_buf		buffer to store packets in								*
   1596  *		multi_packet_max		max size of multi_packet_buf							*
   1597  *		max_ahead				current game MaxAhead value							*
   1598  *		my_sent					# commands I've sent this game						*
   1599  *                                                                         *
   1600  * OUTPUT:                                                                 *
   1601  *		# events sent, NOT including the FRAMEINFO event							*
   1602  *                                                                         *
   1603  * WARNINGS:                                                               *
   1604  *                                                                         *
   1605  * HISTORY:                                                                *
   1606  *   11/21/1995 BRR : Created.                                             *
   1607  *=========================================================================*/
   1608 static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
   1609 	int multi_packet_max, int max_ahead, int my_sent)
   1610 {
   1611 	int cap;				// max # events to send, NOT including FRAMEINFO event
   1612 	int do_once;		// true: only go through packet loop once
   1613 	int ack_req;		// 0 = no ack required on outgoing packet
   1614 	int packetlen;		// size of meta-packet sent
   1615 
   1616 	//------------------------------------------------------------------------
   1617 	//	Determine how many events it's OK to send this frame.
   1618 	//------------------------------------------------------------------------
   1619 	//........................................................................
   1620 	// If we have 4 or more packets queued for sending, don't add any more
   1621 	// this frame.
   1622 	//........................................................................
   1623 	if (net->Private_Num_Send() >= 4) {
   1624 		cap = 0;
   1625 		do_once = 1;
   1626 	}
   1627 	//........................................................................
   1628 	// If there are 2 or more packets queued, the entire packet we send must
   1629 	// fit within a single ComQueue buffer, so limit # events to 5.
   1630 	// (The Modem connection manager has a max buffer size of 200 bytes, which
   1631 	// is large enough for 6 uncompressed events, which leaves room for 5
   1632 	// events plus a FRAMEINFO.)
   1633 	//........................................................................
   1634 	else if (net->Private_Num_Send() >= 2) {
   1635 		cap = 5;
   1636 		do_once = 1;
   1637 
   1638 	}
   1639 	//........................................................................
   1640 	// Otherwise, just send all events in the OutList
   1641 	//........................................................................
   1642 	else {
   1643 		cap = OutList.Count;
   1644 		do_once = 0;
   1645 	}
   1646 
   1647 	//........................................................................
   1648 	// Make sure we aren't sending more events than are in the OutList
   1649 	//........................................................................
   1650 	if (cap > OutList.Count) {
   1651 		cap = OutList.Count;
   1652 	}
   1653 
   1654 	//........................................................................
   1655 	// Make sure we don't send so many events that our DoList fills up
   1656 	//........................................................................
   1657 	if (cap > (MAX_EVENTS * 64) - DoList.Count) {
   1658 		cap = (MAX_EVENTS * 64) - DoList.Count;
   1659 	}
   1660 
   1661 	//
   1662 	// 10/21/96 5:12PM - ST
   1663 	//
   1664 	if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){
   1665 		cap = OutList.Count;
   1666 		do_once = 0;
   1667 	}
   1668 
   1669 	//------------------------------------------------------------------------
   1670 	//	Build our meta-packet & transmit it.
   1671 	//------------------------------------------------------------------------
   1672 	while (1) {
   1673 		Keyboard->Check();
   1674 
   1675 		Update_Queue_Mono (net, 1);
   1676 
   1677 		//.....................................................................
   1678 		//	If there are no commands this frame, we'll just be sending a FRAMEINFO
   1679 		//	packet; no ack is required.  For the modem's sake, check
   1680 		// Session.NumPlayers; no ACK is needed if we're just sending to someone
   1681 		// who's left the game.
   1682 		//.....................................................................
   1683 		if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 1) {
   1684 			ack_req = 0;
   1685 		}
   1686 		else {
   1687 			ack_req = 1;
   1688 		}
   1689 
   1690 		//.....................................................................
   1691 		//	Build & send out our message
   1692 		//.....................................................................
   1693 		packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max,
   1694 			max_ahead, my_sent, cap);
   1695 		net->Send_Private_Message (multi_packet_buf, packetlen, ack_req);
   1696 
   1697 		//.....................................................................
   1698 		//	Call Service() to actually send the packet
   1699 		//.....................................................................
   1700 		net->Service();
   1701 
   1702 		//.....................................................................
   1703 		//	Stop if there's no more data to send, or if our send queue is
   1704 		// filling up.
   1705 		//.....................................................................
   1706 		if (OutList.Count == 0 || do_once) {
   1707 			break;
   1708 		}
   1709 	}
   1710 
   1711 	return (cap);
   1712 
   1713 }	// end of Send_Packets
   1714 
   1715 
   1716 /***************************************************************************
   1717  * Send_FrameSync -- Sends a FRAMESYNC packet                              *
   1718  *                                                                         *
   1719  * This routine is used to periodically remind the other systems that 		*
   1720  * we're still here, and to tell them what frame # we're on, in case			*
   1721  * they've missed my FRAMEINFO packets.												*
   1722  *                                                                         *
   1723  * INPUT:                                                                  *
   1724  *		net				ptr to connection manager										*
   1725  *		cmd_count		# commands I've sent so far									*
   1726  *                                                                         *
   1727  * OUTPUT:                                                                 *
   1728  *		none.																						*
   1729  *                                                                         *
   1730  * WARNINGS:                                                               *
   1731  *		none.																						*
   1732  *                                                                         *
   1733  * HISTORY:                                                                *
   1734  *   11/21/1995 BRR : Created.                                             *
   1735  *=========================================================================*/
   1736 static void Send_FrameSync(ConnManClass *net, int cmd_count)
   1737 {
   1738 	EventClass packet;
   1739 
   1740 	//------------------------------------------------------------------------
   1741 	//	Build a frame-sync event to send.  FRAMESYNC packets contain a
   1742 	// scenario-based CRC rather than a game-state-based CRC, to let the
   1743 	// games compare scenario CRC's on startup.
   1744 	//------------------------------------------------------------------------
   1745 	memset (&packet, 0, sizeof(EventClass));
   1746 	packet.Type = EventClass::FRAMESYNC;
   1747 	if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   1748 		packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) /
   1749 			 Session.FrameSendRate) * Session.FrameSendRate;
   1750 	}
   1751 	else {
   1752 		packet.Frame = Frame + Session.MaxAhead;
   1753 	}
   1754 	packet.ID = PlayerPtr->ID;
   1755 	packet.Data.FrameInfo.CRC = ScenarioCRC;
   1756 	packet.Data.FrameInfo.CommandCount = cmd_count;
   1757 	packet.Data.FrameInfo.Delay = Session.MaxAhead;
   1758 
   1759 	//------------------------------------------------------------------------
   1760 	// Send the event.  For modem, this just sends to the other player;
   1761 	// for network, it sends to everyone we're connected to.
   1762 	//------------------------------------------------------------------------
   1763 
   1764 	net->Send_Private_Message (&packet, (offsetof(EventClass, Data) +
   1765 		size_of(EventClass, Data.FrameInfo)), 0 );
   1766 
   1767 	return;
   1768 
   1769 }	// end of Send_FrameSync
   1770 
   1771 
   1772 /***************************************************************************
   1773  * Process_Receive_Packet -- processes an incoming packet                  *
   1774  *                                                                         *
   1775  * This routine receives a packet from another system, adds it to our		*
   1776  * execution queue (the DoList), and updates my arrays of their frame #,	*
   1777  * their commands-sent, and their commands-received.								*
   1778  *                                                                         *
   1779  * INPUT:                                                                  *
   1780  *		net					ptr to connection manager									*
   1781  *		multi_packet_buf	buffer containing packet(s) to parse					*
   1782  *		id						id of sender													*
   1783  *		their_frame			array containing frame #'s of other players			*
   1784  *		their_sent			array containing command count of other players		*
   1785  *		their_recv			array containing # recv'd cmds from other players	*
   1786  *                                                                         *
   1787  * OUTPUT:                                                                 *
   1788  *		RC_NORMAL:					nothing unusual happened, although 				*
   1789  * 									their_sent or their_recv may have been 		*
   1790  *										altered													*
   1791  *		RC_PLAYER_READY:			player has been heard from for the 1st time; *
   1792  * 									this presumes that his original 					*
   1793  * 									'their_frame[]' value was -1 when this 		*
   1794  * 									routine was called									*
   1795  *		RC_SCENARIO_MISMATCH:	FRAMEINFO scenario CRC doesn't match; 			*
   1796  * 									normally only applies after loading a new 	*
   1797  * 									scenario or save-game								*
   1798  *		RC_DOLIST_FULL:			fatal error; unable to add events to DoList	*
   1799  *                                                                         *
   1800  * WARNINGS:                                                               *
   1801  *		none.																						*
   1802  *                                                                         *
   1803  * HISTORY:                                                                *
   1804  *   11/21/1995 BRR : Created.                                             *
   1805  *=========================================================================*/
   1806 static RetcodeType Process_Receive_Packet(ConnManClass *net,
   1807 	char *multi_packet_buf, int id, int packetlen, long *their_frame,
   1808 	unsigned short *their_sent, unsigned short *their_recv)
   1809 {
   1810 	EventClass *event;
   1811 	int index;
   1812 	RetcodeType retcode = RC_NORMAL;
   1813 	int i;
   1814 
   1815 	//------------------------------------------------------------------------
   1816 	//	Get an event ptr to the incoming message
   1817 	//------------------------------------------------------------------------
   1818 	event = (EventClass *)multi_packet_buf;
   1819 
   1820 	//------------------------------------------------------------------------
   1821 	//	Get the index of the sender
   1822 	//------------------------------------------------------------------------
   1823 	index = net->Connection_Index(id);
   1824 
   1825 	//------------------------------------------------------------------------
   1826 	//	Compute the other player's frame # (at the time this packet was sent)
   1827 	//------------------------------------------------------------------------
   1828 	if (their_frame[index] <
   1829 		(int)(event->Frame - event->Data.FrameInfo.Delay)) {
   1830 
   1831 		//.....................................................................
   1832 		// If the original frame # for this player is -1, it means we've heard
   1833 		// from this player for the 1st time; return the appropriate value.
   1834 		//.....................................................................
   1835 		if (their_frame[index]==-1) {
   1836 			retcode = RC_PLAYER_READY;
   1837 		}
   1838 
   1839 		their_frame[index] = event->Frame - event->Data.FrameInfo.Delay;
   1840 	}
   1841 
   1842 	//------------------------------------------------------------------------
   1843 	//	Extract the other player's CommandCount.  This count will include
   1844 	//	the commands in this packet, if there are any.
   1845 	//------------------------------------------------------------------------
   1846 	if (event->Data.FrameInfo.CommandCount > their_sent[index]) {
   1847 
   1848 		if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) {
   1849 			FILE *fp;
   1850 			fp = fopen("badcount.txt","wt");
   1851 			if (fp) {
   1852 				fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]);
   1853 				fprintf(fp,"Frame:%d  ID:%d  IsExec:%d\n",
   1854 					event->Frame,
   1855 					event->ID,
   1856 					event->IsExecuted);
   1857 				if (event->Type != EventClass::FRAMEINFO) {
   1858 					fprintf(fp,"Wrong Event Type!\n");
   1859 				} else {
   1860 					fprintf(fp,"CRC:%x  CommandCount:%d  Delay:%d\n",
   1861 						event->Data.FrameInfo.CRC,
   1862 						event->Data.FrameInfo.CommandCount,
   1863 						event->Data.FrameInfo.Delay);
   1864 				}
   1865 			}
   1866 		}
   1867 
   1868 		their_sent[index] = event->Data.FrameInfo.CommandCount;
   1869 	}
   1870 
   1871 	if (Debug_Print_Events) {
   1872 		if (event->Type == EventClass::FRAMESYNC) {
   1873 			printf("(%d) Received FRAMESYNC: ", Frame);
   1874 		} else {
   1875 			printf("(%d) Received FRAMEINFO: ", Frame);
   1876 		}
   1877 		printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n",
   1878 			event->Frame,
   1879 			event->ID,
   1880 			event->Data.FrameInfo.CRC,
   1881 			event->Data.FrameInfo.CommandCount,
   1882 			event->Data.FrameInfo.Delay);
   1883 	}
   1884 
   1885 	//------------------------------------------------------------------------
   1886 	//	If this packet was not a FRAMESYNC packet:
   1887 	//	- Add the events in it to our DoList
   1888 	//	- Increment our commands-received counter by the number of non-
   1889 	//	  FRAMEINFO packets received
   1890 	//------------------------------------------------------------------------
   1891 	if (event->Type != EventClass::FRAMESYNC) {
   1892 		//.....................................................................
   1893 		// Break up the packet into its component events.  A returned packet
   1894 		// count of -1 indicates a fatal queue-full error.
   1895 		//.....................................................................
   1896 		i = Breakup_Receive_Packet( multi_packet_buf, packetlen);
   1897 		if (i==-1) {
   1898 			return (RC_DOLIST_FULL);
   1899 		}
   1900 		//.....................................................................
   1901 		// Compute the actual # commands in the packet by subtracting off the
   1902 		// FRAMEINFO event
   1903 		//.....................................................................
   1904 		if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) {
   1905 			i--;
   1906 		}
   1907 
   1908 		their_recv[index] += i;
   1909 	}
   1910 
   1911 	//------------------------------------------------------------------------
   1912 	//	If the event was a FRAMESYNC packet, there will be no commands to add,
   1913 	//	but we must check the ScenarioCRC value.
   1914 	//------------------------------------------------------------------------
   1915 	else if (event->Data.FrameInfo.CRC != ScenarioCRC) {
   1916 		return (RC_SCENARIO_MISMATCH);
   1917 	}
   1918 
   1919 	return (retcode);
   1920 
   1921 }	// end of Process_Receive_Packet
   1922 
   1923 
   1924 /***************************************************************************
   1925  * Process_Serial_Packet -- Handles an incoming serial packet              *
   1926  *                                                                         *
   1927  * This routine is needed because the modem classes don't support a 			*
   1928  * "global channel" like the network classes do, but that functionality is	*
   1929  * still needed for modem communications.  Specifically, the modem dialogs	*
   1930  * transmit their own special packets back & forth, and messages are sent	*
   1931  * using a special packet type.  Thus, we have to call this routine when	*
   1932  * we receive a modem packet, to allow it to process messages & dialog		*
   1933  * packets.																						*
   1934  *                                                                         *
   1935  * INPUT:                                                                  *
   1936  *		multi_packet_buf		packet buffer to process								*
   1937  *		first_time				1 = this is the 1st game frame						*
   1938  *                                                                         *
   1939  * OUTPUT:                                                                 *
   1940  *		RC_NORMAL:				this wasn't a SERIAL-type packet						*
   1941  *		RC_SERIAL_PROCESSED:	this was a SERIAL-type packet, and was 			*
   1942  * 								processed; the other player is still connected,	*
   1943  * 								even if he's not in the game.							*
   1944  *		RC_PLAYER_LEFT:		other player has left the game						*
   1945  *		RC_HUNG_UP:				we're getting our own packets back; thus, the 	*
   1946  * 								modem is mirroring our packets, which means the *
   1947  * 								modem hung up!												*
   1948  *                                                                         *
   1949  * WARNINGS:                                                               *
   1950  *		none.																						*
   1951  *                                                                         *
   1952  * HISTORY:                                                                *
   1953  *   11/21/1995 BRR : Created.                                             *
   1954  *=========================================================================*/
   1955 static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
   1956 	int first_time)
   1957 {
   1958 	multi_packet_buf;
   1959 	first_time;
   1960 	return (RC_NORMAL);
   1961 #if (0) // ST - 5/13/2019
   1962 	SerialPacketType *serial_packet;		// for parsing serial packets
   1963 	int player_gone;
   1964 	EventClass *event;
   1965 
   1966 	//------------------------------------------------------------------------
   1967 	//	Determine if this packet means that the other player has left the game
   1968 	//------------------------------------------------------------------------
   1969 	serial_packet = (SerialPacketType *)multi_packet_buf;
   1970 	player_gone = 0;
   1971 	//........................................................................
   1972 	// On Frame 0, only a SIGN_OFF means the other player left; the other
   1973 	// packet types may be left over from a previous session.
   1974 	//........................................................................
   1975 	if (first_time) {
   1976 		if (serial_packet->Command == SERIAL_SIGN_OFF) {
   1977 			player_gone = 1;
   1978 		}
   1979 	}
   1980 	//........................................................................
   1981 	// On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means
   1982 	// the other player is gone.
   1983 	//........................................................................
   1984 	else {
   1985 		if (serial_packet->Command == SERIAL_SIGN_OFF ||
   1986 			serial_packet->Command == SERIAL_TIMING ||
   1987 			serial_packet->Command == SERIAL_SCORE_SCREEN ) {
   1988 			player_gone = 1;
   1989 		}
   1990 	}
   1991 	if (player_gone) {
   1992 		Destroy_Null_Connection(serial_packet->ScenarioInfo.Color, 0);
   1993 		return (RC_PLAYER_LEFT);
   1994 	}
   1995 
   1996 	//------------------------------------------------------------------------
   1997 	// Process an incoming message
   1998 	//------------------------------------------------------------------------
   1999 	if (serial_packet->Command == SERIAL_MESSAGE) {
   2000 		if (!Session.Messages.Concat_Message(serial_packet->Name,
   2001 			serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) {
   2002 #ifdef FIXIT_CSII	//	checked - ajw 9/28/98 - Appears to do nothing
   2003 	char *ptr = &serial_packet->Message.Message[0];
   2004 	if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) {
   2005 		Enable_Secret_Units();
   2006 	}
   2007 #endif
   2008 			Session.Messages.Add_Message (serial_packet->Name,
   2009 				serial_packet->ID, serial_packet->Message.Message,
   2010 				(PlayerColorType)serial_packet->ID,
   2011 				TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
   2012 
   2013 			Sound_Effect(VOC_INCOMING_MESSAGE);
   2014 		}
   2015 
   2016 		//.....................................................................
   2017 		//	Save this message in our last-message buffer
   2018 		//.....................................................................
   2019 		if (strlen (serial_packet->Message.Message)) {
   2020 			strcpy (Session.LastMessage, serial_packet->Message.Message);
   2021 		}
   2022 
   2023 		//.....................................................................
   2024 		//	Tell the map to do a partial update (just to force the
   2025 		// messages to redraw).
   2026 		//.....................................................................
   2027 		//Map.Flag_To_Redraw(false);
   2028 		Map.Flag_To_Redraw(true);
   2029 		return (RC_SERIAL_PROCESSED);
   2030 	}
   2031 
   2032 	//------------------------------------------------------------------------
   2033 	// Any other SERIAL-type packet means the other player is still there;
   2034 	// throw them away, but let the caller know the connection is OK.
   2035 	//------------------------------------------------------------------------
   2036 	if ( (serial_packet->Command >= SERIAL_CONNECT &&
   2037 		serial_packet->Command < SERIAL_LAST_COMMAND) ||
   2038 		(serial_packet->Command >= SERIAL_REQ_SCENARIO &&
   2039 		 serial_packet->Command <= SERIAL_NO_SCENARIO) ||
   2040 		Session.NumPlayers == 1) {
   2041 		return (RC_SERIAL_PROCESSED);
   2042 	}
   2043 
   2044 	//........................................................................
   2045 	//	are we getting our own packets back??
   2046 	//........................................................................
   2047 	event = (EventClass *)multi_packet_buf;
   2048 
   2049 	if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED);
   2050 
   2051 	if (event->ID == PlayerPtr->ID) {
   2052 		return (RC_HUNG_UP);
   2053 	}
   2054 
   2055 	return (RC_NORMAL);
   2056 #endif
   2057 }	// end of Process_Serial_Packet
   2058 
   2059 
   2060 /***************************************************************************
   2061  * Can_Advance -- determines if it's OK to advance to the next frame       *
   2062  *                                                                         *
   2063  * This routine uses the current values stored in their_frame[], 				*
   2064  * their_send[], and their_recv[] to see if it's OK to advance to the next	*
   2065  * game frame.  We must not advance if:												*
   2066  * - If our frame # would be too far ahead of the slowest player (the 		*
   2067  *   lowest their_frame[] value).  "Too far" means 								*
   2068  *   (Frame >= their_frame + MaxAhead).												*
   2069  * - our current command count doesn't match the sent command count of one	*
   2070  *   other player (meaning that we've missed a command packet from that 	*
   2071  *   player, and thus the frame # we're receiving from him may be due to a	*
   2072  *   FRAMEINFO packet sent later than the command, so we shouldn't use 		*
   2073  *   this frame # to see if we should advance; we should wait until we 		*
   2074  *   have all the commands before we advance.										*
   2075  *                                                                         *
   2076  * Of course, this routine assumes the values in their_frame[] etc are		*
   2077  * kept current by the caller.															*
   2078  *                                                                         *
   2079  * INPUT:                                                                  *
   2080  *		net				ptr to connection manager										*
   2081  *		max_ahead		max frames ahead													*
   2082  *		their_frame		array of their frame #'s										*
   2083  *		their_sent		array of their sent command count							*
   2084  *		their_recv		array of their # received commands							*
   2085  *                                                                         *
   2086  * OUTPUT:                                                                 *
   2087  *		1 = OK to advance; 0 = not OK														*
   2088  *                                                                         *
   2089  * WARNINGS:                                                               *
   2090  *		none.																						*
   2091  *                                                                         *
   2092  * HISTORY:                                                                *
   2093  *   11/21/1995 BRR : Created.                                             *
   2094  *=========================================================================*/
   2095 static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
   2096 	unsigned short *their_sent, unsigned short *their_recv)
   2097 {
   2098 	long their_oldest_frame;			// other players' oldest frame #
   2099 	int count_ok;							// true = my cmd count matches theirs
   2100 	int i;
   2101 
   2102 	//------------------------------------------------------------------------
   2103 	// Special case for modem: if the other player has left, go ahead and
   2104 	// advance to the next frame; don't wait on him.
   2105 	//------------------------------------------------------------------------
   2106 	if (Session.NumPlayers == 1) {
   2107 		return (1);
   2108 	}
   2109 
   2110 	//------------------------------------------------------------------------
   2111 	//	Find the oldest frame # in 'their_frame'
   2112 	//------------------------------------------------------------------------
   2113 	their_oldest_frame = Frame + 1000;
   2114 	for (i = 0; i < net->Num_Connections(); i++) {
   2115 		if (their_frame[i] < their_oldest_frame)
   2116 			their_oldest_frame = their_frame[i];
   2117 	}
   2118 
   2119 	//------------------------------------------------------------------------
   2120 	//	I can advance to the next frame IF:
   2121 	//	1) I'm less than a one-way propagation delay ahead of the other
   2122 	//    players' frame numbers, AND
   2123 	//	2) their_recv[i] >= their_sent[i] (ie I've received all the commands
   2124 	//	   the other players have sent so far).
   2125 	//------------------------------------------------------------------------
   2126 	count_ok = 1;
   2127 	for (i = 0; i < net->Num_Connections(); i++) {
   2128 		if (their_recv[i] < their_sent[i]) {
   2129 			count_ok = 0;
   2130 			break;
   2131 		}
   2132 	}
   2133 	if (count_ok && (Frame < (their_oldest_frame + max_ahead))) {
   2134 		return (1);
   2135 	}
   2136 
   2137 	return (0);
   2138 
   2139 }	// end of Can_Advance
   2140 
   2141 
   2142 /***************************************************************************
   2143  * Process_Reconnect_Dialog -- processes the reconnection dialog           *
   2144  *                                                                         *
   2145  * This routine [re]draws the reconnection dialog; if 'reconn' is set,		*
   2146  * it tells the user who we're trying to reconnect to; otherwise, is just	*
   2147  * says something generic like "Waiting for connections".						*
   2148  *                                                                         *
   2149  * INPUT:                                                                  *
   2150  *		timeout_timer	ptr to count down timer, showing time remaining			*
   2151  *		their_frame		array of other players' frame #'s							*
   2152  *		num_conn			# connections in 'their_frame'								*
   2153  *		reconn			1 = reconnect, 0 = waiting for first-time connection	*
   2154  *		fresh				1 = draw from scratch, 0 = only update time counter	*
   2155  *                                                                         *
   2156  * OUTPUT:                                                                 *
   2157  *		1 = user wants to cancel, 0 = not												*
   2158  *                                                                         *
   2159  * WARNINGS:                                                               *
   2160  *                                                                         *
   2161  * HISTORY:                                                                *
   2162  *   11/21/1995 BRR : Created.                                             *
   2163  *=========================================================================*/
   2164 static int Process_Reconnect_Dialog(CDTimerClass<SystemTimerClass> *timeout_timer,
   2165 	long *their_frame, int num_conn, int reconn, int fresh)
   2166 {
   2167 	static int displayed_time = 0;	// time value currently displayed
   2168 	int new_time;
   2169 	int oldest_index;						// index of person requiring a reconnect
   2170 	int i,j;
   2171 
   2172 	//------------------------------------------------------------------------
   2173 	// Convert the timer to seconds
   2174 	//------------------------------------------------------------------------
   2175 	new_time = *timeout_timer / 60;
   2176 
   2177 	//------------------------------------------------------------------------
   2178 	// If the timer has changed, or 'fresh' is set, redraw the dialog
   2179 	//------------------------------------------------------------------------
   2180 	if (fresh || (new_time != displayed_time)) {
   2181 		//.....................................................................
   2182 		// Find the index of the person we're trying to reconnect to
   2183 		//.....................................................................
   2184 		if (reconn) {
   2185 			j = 0x7fffffff;
   2186 			oldest_index = 0;
   2187 			for (i = 0; i < num_conn; i++) {
   2188 				if (their_frame[i] < j) {
   2189 					j = their_frame[i];
   2190 					oldest_index = i;
   2191 				}
   2192 			}
   2193 		}
   2194 		Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time);
   2195 	}
   2196 	displayed_time = new_time;
   2197 
   2198 	//........................................................................
   2199 	//	If user hits ESC, bail out
   2200 	//........................................................................
   2201 	if (Keyboard->Check()) {
   2202 		if (Keyboard->Get() == KN_ESC) {
   2203 			return (1);
   2204 		}
   2205 	}
   2206 
   2207 	return (0);
   2208 
   2209 }	// end of Process_Reconnect_Dialog
   2210 
   2211 
   2212 /***************************************************************************
   2213  * Handle_Timeout -- handles a timeout in the wait-for-players loop			*
   2214  *                                                                         *
   2215  * This routine "gracefully" handles a timeout in the frame-sync loop.		*
   2216  * The timeout must be handled differently by a modem game or network 		*
   2217  * game.																							*
   2218  *                                                                         *
   2219  * The modem game must detect if the other player is still connected			*
   2220  * physically, even if he's not playing the game any more; if so, this		*
   2221  * routine returns an OK status.  If the other player isn't even 				*
   2222  * physically connected, an error is returned.										*
   2223  *                                                                         *
   2224  * The network game must find the connection that's causing the timeout,	*
   2225  * and destroy it.  The game continues, even if there are no more human		*
   2226  * players left.																				*
   2227  *                                                                         *
   2228  * INPUT:                                                                  *
   2229  *		net					ptr to connection manager									*
   2230  *		their_frame			array containing frame #'s of other players			*
   2231  *		their_sent			array containing command count of other players		*
   2232  *		their_recv			array containing # recv'd cmds from other players	*
   2233  *                                                                         *
   2234  * OUTPUT:                                                                 *
   2235  *		1 = it's OK; reset timeout timers & keep processing						*
   2236  *		0 = game over, man																	*
   2237  *                                                                         *
   2238  * WARNINGS:                                                               *
   2239  *                                                                         *
   2240  * HISTORY:                                                                *
   2241  *   11/21/1995 BRR : Created.                                             *
   2242  *=========================================================================*/
   2243 static int Handle_Timeout(ConnManClass *net, long *their_frame,
   2244 	unsigned short *their_sent, unsigned short *their_recv)
   2245 {
   2246 	int oldest_index;						// index of person requiring a reconnect
   2247 	int i,j;
   2248 	int id;
   2249 
   2250 	//------------------------------------------------------------------------
   2251 	// For modem, attempt to reconnect; if that fails, save the game & bail.
   2252 	//------------------------------------------------------------------------
   2253 	if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
   2254 		if ( net->Num_Connections() ) {
   2255 			if (!Reconnect_Modem()) {
   2256 #ifndef FIXIT_MULTI_SAVE
   2257 				//...............................................................
   2258 				// Set 'Session.EmergencySave', so when this game is loaded, we
   2259 				// won't check the CRC of the game state (this system & the
   2260 				// other may be on different frames, in which case the CRC
   2261 				// won't match).
   2262 				//...............................................................
   2263 				Session.EmergencySave = 1;
   2264 				//Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
   2265 				Session.EmergencySave = 0;
   2266 #endif	// FIXIT_MULTI_SAVE
   2267 				return (0);
   2268 			} else {
   2269 				return (1);
   2270 			}
   2271 		}
   2272 	}
   2273 
   2274 	//------------------------------------------------------------------------
   2275 	//	For network, destroy the oldest connection
   2276 	//------------------------------------------------------------------------
   2277 	else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET ||
   2278 		Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
   2279 		j = 0x7fffffff;
   2280 		oldest_index = 0;
   2281 		for (i = 0; i < net->Num_Connections(); i++) {
   2282 			if (their_frame[i] < j) {
   2283 				j = their_frame[i];
   2284 				oldest_index = i;
   2285 			}
   2286 		}
   2287 
   2288 		id = net->Connection_ID(oldest_index);
   2289 #ifdef WIN32
   2290 		/*
   2291 		** Send the game statistics packet now if the game is effectivly over
   2292 		*/
   2293 		if (Session.Players.Count() == 2 &&
   2294 				Session.Type == GAME_INTERNET &&
   2295 				!GameStatisticsPacketSent) {
   2296 			Register_Game_End_Time();
   2297 			ConnectionLost = true;
   2298 			Send_Statistics_Packet();		//	Disconnect, and I'll be the only one left.
   2299 		}
   2300 #endif	//WIN32
   2301 
   2302 		if (id != ConnManClass::CONNECTION_NONE) {
   2303 			for (i = oldest_index; i < net->Num_Connections() - 1; i++) {
   2304 				their_frame[i] = their_frame[i+1];
   2305 				their_sent[i] = their_sent[i+1];
   2306 				their_recv[i] = their_recv[i+1];
   2307 			}
   2308 			if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
   2309 				Destroy_Connection(id,1);
   2310 			}
   2311 #if(TEN)
   2312 			else if (Session.Type == GAME_TEN) {
   2313 				Destroy_TEN_Connection(id,1);
   2314 			}
   2315 #endif
   2316 #if(MPATH)
   2317 			else if (Session.Type == GAME_MPATH) {
   2318 				Destroy_MPATH_Connection(id,1);
   2319 			}
   2320 #endif
   2321 		}
   2322 	}
   2323 
   2324 	return (1);
   2325 
   2326 }	// end of Handle_Timeout
   2327 
   2328 
   2329 /***************************************************************************
   2330  * Stop_Game -- stops the game															*
   2331  *                                                                         *
   2332  * This routine clears any global flags that need it, in preparation for	*
   2333  * halting the game prematurely.															*
   2334  *                                                                         *
   2335  * INPUT:                                                                  *
   2336  *		none.																						*
   2337  *                                                                         *
   2338  * OUTPUT:                                                                 *
   2339  *		none.																						*
   2340  *                                                                         *
   2341  * WARNINGS:                                                               *
   2342  *		none.																						*
   2343  *                                                                         *
   2344  * HISTORY:                                                                *
   2345  *   11/22/1995 BRR : Created.                                             *
   2346  *=========================================================================*/
   2347 static void Stop_Game(void)
   2348 {
   2349 	Session.LoadGame = false;
   2350 	Session.EmergencySave = false;
   2351 	GameActive = 0;
   2352 	if (IsMono) {
   2353 		MonoClass::Disable();
   2354 	}
   2355 #ifdef WIN32
   2356 	if (Session.Type == GAME_INTERNET){
   2357 		ConnectionLost = true;
   2358 		Send_Statistics_Packet();			//	Stop_Game()
   2359 	}
   2360 #endif	//WIN32
   2361 
   2362 	return;
   2363 
   2364 }	// end of Stop_Game
   2365 
   2366 
   2367 /***************************************************************************
   2368  * Build_Send_Packet -- Builds a big packet from a bunch of little ones.	*
   2369  *                                                                         *
   2370  * This routine takes events from the OutList, and puts them into a 			*
   2371  * "meta-packet", which is transmitted to all systems we're connected to.	*
   2372  * Also, these events are added to our own DoList.									*
   2373  *                                                                         *
   2374  * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this 		*
   2375  * tells the other systems what frame we're on, as well as serving as a 	*
   2376  * standard packet header.																	*
   2377  *                                                                         *
   2378  * INPUT:                                                                  *
   2379  *		buf				buffer to store packet in										*
   2380  *		bufsize			max size of buffer												*
   2381  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2382  *		num_cmds			value to use for the CommandCount field					*
   2383  *		cap				max # events to send												*
   2384  *                                                                         *
   2385  * OUTPUT:                                                                 *
   2386  *		new size of packet																	*
   2387  *                                                                         *
   2388  * WARNINGS:                                                               *
   2389  * 'num_cmds' should be the total of of commands, including all those sent *
   2390  * this frame!																					*
   2391  *                                                                         *
   2392  * HISTORY:                                                                *
   2393  *   11/21/1995 BRR : Created.                                             *
   2394  *=========================================================================*/
   2395 static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
   2396 	int num_cmds, int cap)
   2397 {
   2398 	int size = 0;
   2399 	EventClass *finfo;
   2400 
   2401 	//------------------------------------------------------------------------
   2402 	// All events start with a FRAMEINFO event; fill this part in.
   2403 	//------------------------------------------------------------------------
   2404 	//........................................................................
   2405 	// Set the event type
   2406 	//........................................................................
   2407 	finfo = (EventClass *)buf;
   2408 	finfo->Type = EventClass::FRAMEINFO;
   2409 	//........................................................................
   2410 	// Set the frame to execute this event on; this is protocol-specific
   2411 	//........................................................................
   2412 	if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   2413 		finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) /
   2414 			 Session.FrameSendRate) * Session.FrameSendRate;
   2415 	}
   2416 	else {
   2417 		finfo->Frame = Frame + frame_delay;
   2418 	}
   2419 	//........................................................................
   2420 	// Fill in the rest of the event
   2421 	//........................................................................
   2422 	finfo->ID = PlayerPtr->ID;
   2423 	finfo->Data.FrameInfo.CRC = GameCRC;
   2424 	finfo->Data.FrameInfo.CommandCount = num_cmds;
   2425 	finfo->Data.FrameInfo.Delay = frame_delay;
   2426 
   2427 	//------------------------------------------------------------------------
   2428 	// Initialize the # of bytes processed; this is protocol-specific
   2429 	//------------------------------------------------------------------------
   2430 	if (Session.CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) {
   2431 		size += sizeof(EventClass);
   2432 	}
   2433 	else {
   2434 		size += (offsetof(EventClass, Data) +
   2435 			size_of(EventClass, Data.FrameInfo));
   2436 	}
   2437 
   2438 	//------------------------------------------------------------------------
   2439 	//	Transfer all events from the OutList into the DoList, building our
   2440 	//	packet while we go.
   2441 	//------------------------------------------------------------------------
   2442 	switch (Session.CommProtocol) {
   2443 		//.....................................................................
   2444 		// COMM_PROTOCOL_SINGLE_NO_COMP:
   2445 		// We'll send at least a FRAMEINFO every single frame, no compression
   2446 		//.....................................................................
   2447 		case (COMM_PROTOCOL_SINGLE_NO_COMP):
   2448 			size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap);
   2449 			break;
   2450 
   2451 		//.....................................................................
   2452 		// COMM_PROTOCOL_SINGLE_E_COMP:
   2453 		//   Compress a group of packets into our send buffer; send out
   2454 		//   compressed packets every frame.
   2455 		// COMM_PROTOCOL_MULTI_E_COMP:
   2456 		//   Compress a group of packets into our send buffer; send out
   2457 		//   compressed packets every 'n' frames.
   2458 		//.....................................................................
   2459 		case (COMM_PROTOCOL_SINGLE_E_COMP):
   2460 		case (COMM_PROTOCOL_MULTI_E_COMP):
   2461 			size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap);
   2462 			break;
   2463 
   2464 		//.....................................................................
   2465 		// Default: We have no idea what to do, so do nothing.
   2466 		//.....................................................................
   2467 		default:
   2468 			size = 0;
   2469 			break;
   2470 	}
   2471 
   2472 	return( size );
   2473 
   2474 }	/* end of Build_Send_Packet */
   2475 
   2476 
   2477 /***************************************************************************
   2478  * Add_Uncompressed_Events -- adds uncompressed events to a packet         *
   2479  *                                                                         *
   2480  * INPUT:                                                                  *
   2481  *		buf				buffer to store packet in										*
   2482  *		bufsize			max size of buffer												*
   2483  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2484  *		size				current packet size												*
   2485  *		cap				max # events to process											*
   2486  *                                                                         *
   2487  * OUTPUT:                                                                 *
   2488  *		new size value																			*
   2489  *                                                                         *
   2490  * WARNINGS:                                                               *
   2491  *		This routine MUST check to be sure it doesn't overflow the buffer.	*
   2492  *                                                                         *
   2493  * HISTORY:                                                                *
   2494  *   11/21/1995 DRD : Created.                                             *
   2495  *=========================================================================*/
   2496 static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay,
   2497 	int size, int cap)
   2498 {
   2499 	int num = 0;			// # of events processed
   2500 	int ev_size;			// size of event we're adding
   2501 
   2502 	//------------------------------------------------------------------------
   2503 	// Loop until there are no more events, or we've processed our max # of
   2504 	// events, or the buffer is full.
   2505 	//------------------------------------------------------------------------
   2506 	while (OutList.Count && (num < cap)) {
   2507 
   2508 		Keyboard->Check();
   2509 
   2510 		if (OutList.First().Type==EventClass::ADDPLAYER) {
   2511 			ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size;
   2512 		}
   2513 		else {
   2514 			ev_size = sizeof(EventClass);
   2515 		}
   2516 		//.....................................................................
   2517 		// Will the next event exceed the size of the buffer?  If so, break.
   2518 		//.....................................................................
   2519 		if ( (size + ev_size) > bufsize ) {
   2520 			return (size);
   2521 		}
   2522 
   2523 		//.....................................................................
   2524 		// Set the event's frame delay
   2525 		//.....................................................................
   2526 		OutList.First().Frame = Frame + frame_delay;
   2527 
   2528 		//.....................................................................
   2529 		// Set the event's ID
   2530 		//.....................................................................
   2531 		OutList.First().ID = PlayerPtr->ID;
   2532 
   2533 		//.....................................................................
   2534 		// Transfer the event in OutList to DoList, un-queue the OutList
   2535 		// event.  If the DoList is full, stop transferring immediately.
   2536 		//.....................................................................
   2537 		OutList.First().IsExecuted = 0;
   2538 		if (!DoList.Add(OutList.First())) {
   2539 			return (size);
   2540 		}
   2541 		#ifdef MIRROR_QUEUE
   2542 		MirrorList.Add(OutList.First());
   2543 		#endif
   2544 
   2545 		//.....................................................................
   2546 		// Add event to the send packet
   2547 		//.....................................................................
   2548 		if (OutList.First().Type==EventClass::ADDPLAYER) {
   2549 			memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
   2550 			size += sizeof(EventClass);
   2551 			memcpy ( ((char *)buf) + size,
   2552 				OutList.First().Data.Variable.Pointer,
   2553 				OutList.First().Data.Variable.Size);
   2554 			size += OutList.First().Data.Variable.Size;
   2555 		}
   2556 		else {
   2557 			memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
   2558 			size += sizeof(EventClass);
   2559 		}
   2560 
   2561 		//.....................................................................
   2562 		// Increment our event counter; delete the last event from the queue
   2563 		//.....................................................................
   2564 		num++;
   2565 		OutList.Next();
   2566 	}
   2567 
   2568 	return (size);
   2569 
   2570 }	// end of Add_Uncompressed_Events
   2571 
   2572 
   2573 /***************************************************************************
   2574  * Add_Compressed_Events -- adds an compressed events to a packet          *
   2575  *                                                                         *
   2576  * INPUT:                                                                  *
   2577  *		buf				buffer to store packet in										*
   2578  *		bufsize			max size of buffer												*
   2579  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2580  *		size				reference to current packet size								*
   2581  *		cap				max # events to process											*
   2582  *                                                                         *
   2583  * OUTPUT:                                                                 *
   2584  *		new size value																			*
   2585  *                                                                         *
   2586  * WARNINGS:                                                               *
   2587  *		This routine MUST check to be sure it doesn't overflow the buffer.	*
   2588  *                                                                         *
   2589  * HISTORY:                                                                *
   2590  *   11/21/1995 DRD : Created.                                             *
   2591  *=========================================================================*/
   2592 static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay,
   2593 	int size, int cap)
   2594 {
   2595 	int num = 0;							// # of events processed
   2596 	EventClass::EventType eventtype;	// type of event being compressed
   2597 	EventClass prevevent;				// last event processed
   2598 	int datasize;							// size of element plucked from event union
   2599 	int storedsize;						// actual # bytes stored from event
   2600 	unsigned char *unitsptr = NULL;	// ptr to buffer pos to store mega. rep count
   2601 	unsigned char numunits = 0;		// megamission rep count value
   2602 	bool missiondup = false;			// flag: is this event a megamission repeat?
   2603 
   2604 	//------------------------------------------------------------------------
   2605 	// clear previous event
   2606 	//------------------------------------------------------------------------
   2607 	memset (&prevevent, 0, sizeof(EventClass));
   2608 
   2609 	if (Debug_Print_Events) {
   2610 		printf("\n(%d) Building Send Packet\n", Frame);
   2611 	}
   2612 
   2613 	//------------------------------------------------------------------------
   2614 	// Loop until there are no more events, we've processed our max # of
   2615 	// events, or the buffer is full.
   2616 	//------------------------------------------------------------------------
   2617 	while (OutList.Count && (num < cap)) {
   2618 
   2619 		Keyboard->Check();
   2620 
   2621 		eventtype = OutList.First().Type;
   2622 		datasize = EventClass::EventLength[ eventtype ];
   2623 		//.....................................................................
   2624 		// For a variable-sized event, pull the size from the event; otherwise,
   2625 		// the size will be the data element size plus the event type value.
   2626 		// (The other data elements in the event, Frame, ID, etc, are stored
   2627 		// in the packet header.)
   2628 		//.....................................................................
   2629 		if (eventtype==EventClass::ADDPLAYER) {
   2630 			storedsize = datasize + sizeof (EventClass::EventType) +
   2631 				OutList.First().Data.Variable.Size;
   2632 		}
   2633 		else {
   2634 			storedsize = datasize + sizeof (EventClass::EventType);
   2635 		}
   2636 
   2637 		//.....................................................................
   2638 		// MegaMission compression:  MegaMissions are stored as:
   2639 		//   EventType
   2640 		//   Rep Count
   2641 		//   MegaMission structure (event # 1 only)
   2642 		//   Whom #2
   2643 		//   Whom #3
   2644 		//   Whom #4
   2645 		//   ...
   2646 		//   Whom #n
   2647 		//.....................................................................
   2648 		if (prevevent.Type == EventClass::MEGAMISSION) {
   2649 			//..................................................................
   2650 			// If previous & current events are both MegaMissions:
   2651 			//..................................................................
   2652 			if (eventtype == EventClass::MEGAMISSION) {
   2653 				//...............................................................
   2654 				// If the Mission, Target, & Destination are the same, compress
   2655 				// the events into one:
   2656 				// - Change datasize to the size of the 'Whom' field only
   2657 				// - set total # bytes to store to the size of the 'Whom' only
   2658 				// - increment the MegaMission rep count
   2659 				// - set the MegaMission rep flag
   2660 				//...............................................................
   2661 				if (OutList.First().Data.MegaMission.Mission ==
   2662 					prevevent.Data.MegaMission.Mission &&
   2663 					OutList.First().Data.MegaMission.Target ==
   2664 						prevevent.Data.MegaMission.Target &&
   2665 					OutList.First().Data.MegaMission.Destination ==
   2666 						prevevent.Data.MegaMission.Destination) {
   2667 #if (0)//PG
   2668 					if (Debug_Print_Events) {
   2669 						printf("      adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n",
   2670 						OutList.First().Data.MegaMission.Whom.As_TARGET(),
   2671 						OutList.First().Data.MegaMission.Whom,
   2672 						MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission),
   2673 						OutList.First().Data.MegaMission.Target.As_TARGET(),
   2674 						OutList.First().Data.MegaMission.Target,
   2675 						OutList.First().Data.MegaMission.Destination.As_TARGET(),
   2676 						OutList.First().Data.MegaMission.Destination);
   2677 					}
   2678 #endif
   2679 					datasize = sizeof(prevevent.Data.MegaMission.Whom);
   2680 					storedsize = datasize;
   2681 					numunits++;
   2682 					missiondup = true;
   2683 				}
   2684 				//...............................................................
   2685 				// Data doesn't match; start a new run of MegaMissions:
   2686 				// - Store previous MegaMission rep count
   2687 				// - Init 'unitsptr' to buffer pos after next EventType
   2688 				// - set total # bytes to store to 'datasize' + sizeof(EventType) +
   2689 				//   sizeof (numunits)
   2690 				// - init the MegaMission rep count to 1
   2691 				// - clear the MegaMission rep flag
   2692 				//...............................................................
   2693 				else {
   2694 
   2695 					if (Debug_Print_Events) {
   2696 						printf("  New MEGAMISSION run:\n");
   2697 					}
   2698 
   2699 					*unitsptr = numunits;
   2700 					unitsptr = ((unsigned char *)buf) + size +
   2701 						sizeof(EventClass::EventType);
   2702 					storedsize += sizeof(numunits);
   2703 					numunits = 1;
   2704 					missiondup = false;
   2705 				}
   2706 			}
   2707 			//..................................................................
   2708 			// Previous event was a MegaMission, but this one isn't: end the
   2709 			// run of MegaMissions:
   2710 			// - Store previous MegaMission rep count
   2711 			// - Clear variables
   2712 			//..................................................................
   2713 			else {
   2714 				*unitsptr = numunits;		// save # events in our run
   2715 				unitsptr = NULL;				// init other values
   2716 				numunits = 0;
   2717 				missiondup = false;
   2718 			}
   2719 		}
   2720 
   2721 		//.....................................................................
   2722 		// The previous event is not a MEGAMISSION but the current event is:
   2723 		// Set up a new run of MegaMissions:
   2724 		// - Init 'unitsptr' to buffer pos after next EventType
   2725 		// - set total # bytes to store to 'datasize' + sizeof(EventType) +
   2726 		//   sizeof (numunits)
   2727 		// - init the MegaMission rep count to 1
   2728 		// - clear the MegaMission rep flag
   2729 		//.....................................................................
   2730 		else if (eventtype == EventClass::MEGAMISSION) {
   2731 
   2732 			if (Debug_Print_Events) {
   2733 				printf("  New MEGAMISSION run:\n");
   2734 			}
   2735 
   2736 			unitsptr = ((unsigned char *)buf) + size +
   2737 				sizeof(EventClass::EventType);
   2738 			storedsize += sizeof(numunits);
   2739 			numunits = 1;
   2740 			missiondup = false;
   2741 		}
   2742 
   2743 		//.....................................................................
   2744 		// Will the next event exceed the size of the buffer?  If so,
   2745 		// stop compressing.
   2746 		//.....................................................................
   2747 		if ( (size + storedsize) > bufsize )
   2748 			break;
   2749 
   2750 		//.....................................................................
   2751 		// Set the event's frame delay (this is protocol-dependent)
   2752 		//.....................................................................
   2753 		if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   2754 			OutList.First().Frame = ((Frame + frame_delay +
   2755 				(Session.FrameSendRate - 1)) / Session.FrameSendRate) *
   2756 				Session.FrameSendRate;
   2757 		}
   2758 		else {
   2759 			OutList.First().Frame = Frame + frame_delay;
   2760 		}
   2761 
   2762 		//.....................................................................
   2763 		// Set the event's ID
   2764 		//.....................................................................
   2765 		OutList.First().ID = PlayerPtr->ID;
   2766 
   2767 		//.....................................................................
   2768 		// Transfer the event in OutList to DoList, un-queue the OutList event.
   2769 		// If the DoList is full, stop transferring immediately.
   2770 		//.....................................................................
   2771 		OutList.First().IsExecuted = 0;
   2772 		if ( !DoList.Add( OutList.First() ) ) {
   2773 			break;
   2774 		}
   2775 		#ifdef MIRROR_QUEUE
   2776 		MirrorList.Add(OutList.First());
   2777 		#endif
   2778 
   2779 		//---------------------------------------------------------------------
   2780 		// Compress the event into the send packet buffer
   2781 		//---------------------------------------------------------------------
   2782 		switch ( eventtype ) {
   2783 			//..................................................................
   2784 			// RESPONSE_TIME: just use the Delay field of the FrameInfo union
   2785 			//..................................................................
   2786 			case (EventClass::RESPONSE_TIME):
   2787 
   2788 				*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2789 
   2790 				memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
   2791 					&OutList.First().Data.FrameInfo.Delay, datasize );
   2792 
   2793 				size += (datasize + sizeof(EventClass::EventType));
   2794 				break;
   2795 
   2796 			//..................................................................
   2797 			// MEGAMISSION:
   2798 			//..................................................................
   2799 			case (EventClass::MEGAMISSION):
   2800 				//...............................................................
   2801 				// Repeated mission in a run:
   2802 				//   - Update the rep count (in case we break out)
   2803 				//   - Copy the Whom field only
   2804 				//...............................................................
   2805 				if (missiondup) {
   2806 					*unitsptr = numunits;
   2807 
   2808 					memcpy ( ((char *)buf) + size,
   2809 						&OutList.First().Data.MegaMission.Whom, datasize );
   2810 
   2811 					size += datasize;
   2812 				}
   2813 				//...............................................................
   2814 				// 1st mission in a run:
   2815 				//   - Init the rep count (in case we break out)
   2816 				//   - Set the EventType
   2817 				//   - Copy the MegaMission structure, leaving room for 'numunits'
   2818 				//...............................................................
   2819 				else {
   2820 					*unitsptr = numunits;
   2821 
   2822 					*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2823 
   2824 					memcpy ( ((char *)buf) + size +
   2825 						sizeof(EventClass::EventType) + sizeof(numunits),
   2826 						&OutList.First().Data.MegaMission, datasize );
   2827 
   2828 					size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits));
   2829 				}
   2830 				break;
   2831 
   2832 			//..................................................................
   2833 			// Variable-sized packets: Copy the packet Size & the buffer
   2834 			//..................................................................
   2835 			case (EventClass::ADDPLAYER):
   2836 				*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2837 
   2838 				memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
   2839 					&OutList.First().Data.Variable.Size, datasize );
   2840 				size += (datasize + sizeof(EventClass::EventType));
   2841 
   2842 				memcpy ( ((char *)buf) + size,
   2843 					OutList.First().Data.Variable.Pointer,
   2844 					OutList.First().Data.Variable.Size);
   2845 				size += OutList.First().Data.Variable.Size;
   2846 
   2847 				break;
   2848 
   2849 			//..................................................................
   2850 			// Default case: Just copy over the data field from the union
   2851 			//..................................................................
   2852 			default:
   2853 				*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2854 
   2855 				memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
   2856 					&OutList.First().Data, datasize );
   2857 
   2858 				size += (datasize + sizeof(EventClass::EventType));
   2859 
   2860 				break;
   2861 		}
   2862 
   2863 		//---------------------------------------------------------------------
   2864 		// update # events processed
   2865 		//---------------------------------------------------------------------
   2866 		num++;
   2867 
   2868 		//---------------------------------------------------------------------
   2869 		// Update 'prevevent'
   2870 		//---------------------------------------------------------------------
   2871 		memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) );
   2872 
   2873 		//---------------------------------------------------------------------
   2874 		// Go to the next event to process
   2875 		//---------------------------------------------------------------------
   2876 		OutList.Next();
   2877 	}
   2878 
   2879 	if (Debug_Print_Events) {
   2880 		printf("\n");
   2881 	}
   2882 
   2883 	return (size);
   2884 
   2885 }	// end of Add_Compressed_Events
   2886 
   2887 
   2888 /***************************************************************************
   2889  * Breakup_Receive_Packet -- Splits a big packet into little ones.			*
   2890  *                                                                         *
   2891  * INPUT:                                                                  *
   2892  *		buf			buffer to break up													*
   2893  *		bufsize		length of buffer														*
   2894  *                                                                         *
   2895  * OUTPUT:                                                                 *
   2896  *		# events added to queue, -1 if fatal error (queue is full)				*
   2897  *    (return value includes any FRAMEINFO packets encountered; 				*
   2898  *		FRAMESYNC's are ignored)		  													*
   2899  *                                                                         *
   2900  * WARNINGS:                                                               *
   2901  *		none.																						*
   2902  *                                                                         *
   2903  * HISTORY:                                                                *
   2904  *   11/21/1995 BRR : Created.                                             *
   2905  *=========================================================================*/
   2906 static int Breakup_Receive_Packet(void *buf, int bufsize )
   2907 {
   2908 	int count = 0;
   2909 
   2910 	/*
   2911 	** is there enough leftover for another record
   2912 	*/
   2913 	switch (Session.CommProtocol) {
   2914 		case (COMM_PROTOCOL_SINGLE_NO_COMP):
   2915 			count = Extract_Uncompressed_Events(buf, bufsize);
   2916 			break;
   2917 
   2918 		default:
   2919 			count = Extract_Compressed_Events(buf, bufsize);
   2920 			break;
   2921 	}
   2922 
   2923 	return (count);
   2924 
   2925 }	/* end of Breakup_Receive_Packet */
   2926 
   2927 
   2928 /***************************************************************************
   2929  * Extract_Uncompressed_Events -- extracts events from a packet            *
   2930  *                                                                         *
   2931  * INPUT:                                                                  *
   2932  *		buf			buffer containing events to extract								*
   2933  *		bufsize		length of 'buf'														*
   2934  *                                                                         *
   2935  * OUTPUT:                                                                 *
   2936  *		# events extracted																	*
   2937  *                                                                         *
   2938  * WARNINGS:                                                               *
   2939  *		none.																						*
   2940  *                                                                         *
   2941  * HISTORY:                                                                *
   2942  *   11/21/1995 DRD : Created.                                             *
   2943  *=========================================================================*/
   2944 static int Extract_Uncompressed_Events(void *buf, int bufsize)
   2945 {
   2946 	int count = 0;
   2947 	int pos = 0;
   2948 	int leftover = bufsize;
   2949 	EventClass *event;
   2950 
   2951 	//------------------------------------------------------------------------
   2952 	// Loop until there are no more events in the packet
   2953 	//------------------------------------------------------------------------
   2954 	while (leftover >= sizeof(EventClass) ) {
   2955 
   2956 		Keyboard->Check();
   2957 
   2958 		event = (EventClass *)(((char *)buf) + pos);
   2959 
   2960 		//.....................................................................
   2961 		// add event to the DoList, only if it's not a FRAMESYNC
   2962 		// (but FRAMEINFO's do get added.)
   2963 		//.....................................................................
   2964 		if (event->Type != EventClass::FRAMESYNC) {
   2965 			event->IsExecuted = 0;
   2966 
   2967 			//..................................................................
   2968 			// Special processing for variable-sized events
   2969 			//..................................................................
   2970 			if (event->Type == EventClass::ADDPLAYER) {
   2971 				event->Data.Variable.Pointer = new char[event->Data.Variable.Size];
   2972 				memcpy (event->Data.Variable.Pointer,
   2973 					((char *)buf) + sizeof(EventClass),
   2974 					event->Data.Variable.Size);
   2975 
   2976 				pos += event->Data.Variable.Size;
   2977 				leftover -= event->Data.Variable.Size;
   2978 			}
   2979 
   2980 			if (!DoList.Add( *event )) {
   2981 				if (event->Type == EventClass::ADDPLAYER) {
   2982 					delete [] event->Data.Variable.Pointer;
   2983 				}
   2984 				return (-1);
   2985 			}
   2986 			#ifdef MIRROR_QUEUE
   2987 			MirrorList.Add(*event);
   2988 			#endif
   2989 
   2990 			//..................................................................
   2991 			// Keep count of how many events we add to the queue
   2992 			//..................................................................
   2993 			count++;
   2994 		}
   2995 
   2996 		//.....................................................................
   2997 		// Point to the next position in the buffer; decrement our 'leftover'
   2998 		//.....................................................................
   2999 		pos += sizeof(EventClass);
   3000 		leftover -= sizeof(EventClass);
   3001 	}
   3002 
   3003 	return (count);
   3004 
   3005 }	// end of Extract_Uncompressed_Events
   3006 
   3007 
   3008 /***************************************************************************
   3009  * Extract_Compressed_Events -- extracts events from a packet   				*
   3010  *                                                                         *
   3011  * INPUT:                                                                  *
   3012  *		buf			buffer containing events to extract								*
   3013  *		bufsize		length of 'buf'														*
   3014  *                                                                         *
   3015  * OUTPUT:                                                                 *
   3016  *		# events extracted																	*
   3017  *                                                                         *
   3018  * WARNINGS:                                                               *
   3019  *		none.																						*
   3020  *                                                                         *
   3021  * HISTORY:                                                                *
   3022  *   11/21/1995 DRD : Created.                                             *
   3023  *=========================================================================*/
   3024 static int Extract_Compressed_Events(void *buf, int bufsize)
   3025 {
   3026 	int pos = 0;						// current buffer parsing position
   3027 	int leftover = bufsize;			// # bytes left to process
   3028 	EventClass *event;				// event ptr for parsing buffer
   3029 	int count = 0;						// # events processed
   3030 	int datasize = 0;					// size of data to copy
   3031 	EventClass eventdata;			// stores Frame, ID, etc
   3032 	unsigned char numunits = 0;	// # units stored in compressed MegaMissions
   3033 
   3034 	//------------------------------------------------------------------------
   3035 	// Clear work event structure
   3036 	//------------------------------------------------------------------------
   3037 	memset (&eventdata, 0, sizeof(EventClass));
   3038 
   3039 	//------------------------------------------------------------------------
   3040 	// Assume the first event is a FRAMEINFO event
   3041 	// Init 'datasize' to the amount of data to copy, minus the EventType value
   3042 	// For the 1st packet only, this will include all info before the Data
   3043 	// union, plus the size of the FrameInfo structure, minus the EventType size.
   3044 	//------------------------------------------------------------------------
   3045 	datasize = (offsetof(EventClass, Data) +
   3046 		size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType);
   3047 	event = (EventClass *)(((char *)buf) + pos);
   3048 
   3049 	while (leftover >= (datasize + (int)sizeof(EventClass::EventType)) ) {
   3050 
   3051 		Keyboard->Check();
   3052 
   3053 		//.....................................................................
   3054 		// add event to the DoList, only if it's not a FRAMESYNC
   3055 		// (but FRAMEINFO's do get added.)
   3056 		//.....................................................................
   3057 		if (event->Type != EventClass::FRAMESYNC) {
   3058 			//..................................................................
   3059 			// initialize the common data from the FRAMEINFO event
   3060 			// keeping IsExecuted 0
   3061 			//..................................................................
   3062 			if (event->Type == EventClass::FRAMEINFO) {
   3063 				eventdata.Frame = event->Frame;
   3064 				eventdata.ID = event->ID;
   3065 
   3066 				//...............................................................
   3067 				// Adjust position past the common data
   3068 				//...............................................................
   3069 				pos += (offsetof(EventClass, Data) -
   3070 						 sizeof(EventClass::EventType));
   3071 				leftover -= (offsetof(EventClass, Data) -
   3072 								sizeof(EventClass::EventType));
   3073 			}
   3074 			//..................................................................
   3075 			// if MEGAMISSION event get the number of units (events to generate)
   3076 			//..................................................................
   3077 			else if (event->Type == EventClass::MEGAMISSION) {
   3078 				numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type));
   3079 				pos += sizeof(numunits);
   3080 				leftover -= sizeof(numunits);
   3081 			}
   3082 
   3083 			//..................................................................
   3084 			// clear the union data portion of the event
   3085 			//..................................................................
   3086 			memset (&eventdata.Data, 0, sizeof(eventdata.Data));
   3087 			eventdata.Type = event->Type;
   3088 			datasize = EventClass::EventLength[ eventdata.Type ];
   3089 
   3090 			switch (eventdata.Type) {
   3091 				case (EventClass::RESPONSE_TIME):
   3092 					memcpy ( &eventdata.Data.FrameInfo.Delay,
   3093 						((char *)buf) + pos + sizeof(EventClass::EventType),
   3094 						datasize );
   3095 					break;
   3096 
   3097 				case (EventClass::ADDPLAYER):
   3098 
   3099 					memcpy ( &eventdata.Data.Variable.Size,
   3100 						((char *)buf) + pos + sizeof(EventClass::EventType),
   3101 						datasize );
   3102 
   3103 					eventdata.Data.Variable.Pointer =
   3104 						new char[eventdata.Data.Variable.Size];
   3105 					memcpy (eventdata.Data.Variable.Pointer,
   3106 						((char *)buf) + pos + sizeof(EventClass::EventType) + datasize,
   3107 						eventdata.Data.Variable.Size);
   3108 
   3109 					pos += eventdata.Data.Variable.Size;
   3110 					leftover -= eventdata.Data.Variable.Size;
   3111 
   3112 					break;
   3113 
   3114 				case (EventClass::MEGAMISSION):
   3115 					memcpy ( &eventdata.Data.MegaMission,
   3116 						((char *)buf) + pos + sizeof(EventClass::EventType),
   3117 						datasize );
   3118 
   3119 					if (numunits > 1) {
   3120 						pos += (datasize + sizeof(EventClass::EventType));
   3121 						leftover -= (datasize + sizeof(EventClass::EventType));
   3122 						datasize = sizeof(eventdata.Data.MegaMission.Whom);
   3123 
   3124 						while (numunits) {
   3125 
   3126 							Keyboard->Check();
   3127 
   3128 							if ( !DoList.Add( eventdata ) ) {
   3129 								return (-1);
   3130 							}
   3131 							#ifdef MIRROR_QUEUE
   3132 							MirrorList.Add( eventdata );
   3133 							#endif
   3134 
   3135 							//......................................................
   3136 							// Keep count of how many events we add to the queue
   3137 							//......................................................
   3138 							count++;
   3139 							numunits--;
   3140 							memcpy ( &eventdata.Data.MegaMission.Whom,
   3141 								((char *)buf) + pos, datasize );
   3142 
   3143 							//......................................................
   3144 							// if one unit left fall thru to normal code
   3145 							//......................................................
   3146 							if (numunits == 1) {
   3147 								datasize -= sizeof(EventClass::EventType);
   3148 								break;
   3149 							}
   3150 							else {
   3151 								pos += datasize;
   3152 								leftover -= datasize;
   3153 							}
   3154 						}
   3155 					}
   3156 					break;
   3157 
   3158 				default:
   3159 					memcpy ( &eventdata.Data,
   3160 						((char *)buf) + pos + sizeof(EventClass::EventType),
   3161 						datasize );
   3162 					break;
   3163 			}
   3164 
   3165 			if ( !DoList.Add( eventdata ) ) {
   3166 				if (eventdata.Type == EventClass::ADDPLAYER) {
   3167 					delete [] eventdata.Data.Variable.Pointer;
   3168 				}
   3169 				return (-1);
   3170 			}
   3171 			#ifdef MIRROR_QUEUE
   3172 			MirrorList.Add( eventdata );
   3173 			#endif
   3174 
   3175 			//..................................................................
   3176 			// Keep count of how many events we add to the queue
   3177 			//..................................................................
   3178 			count++;
   3179 
   3180 			pos += (datasize + sizeof(EventClass::EventType));
   3181 			leftover -= (datasize + sizeof(EventClass::EventType));
   3182 
   3183 			if (leftover) {
   3184 				event = (EventClass *)(((char *)buf) + pos);
   3185 				datasize = EventClass::EventLength[ event->Type ];
   3186 				if (event->Type == EventClass::MEGAMISSION) {
   3187 					datasize += sizeof(numunits);
   3188 				}
   3189 			}
   3190 		}
   3191 		//.....................................................................
   3192 		// FRAMESYNC event: This >should< be the only event in the buffer,
   3193 		// and it will be uncompressed.
   3194 		//.....................................................................
   3195 		else {
   3196 			pos += (datasize + sizeof(EventClass::EventType));
   3197 			leftover -= (datasize + sizeof(EventClass::EventType));
   3198 			event = (EventClass *)(((char *)buf) + pos);
   3199 
   3200 			//..................................................................
   3201 			// size of FRAMESYNC event - EventType size
   3202 			//..................................................................
   3203 			datasize = (offsetof(EventClass, Data) +
   3204 							size_of(EventClass, Data.FrameInfo)) -
   3205 							sizeof(EventClass::EventType);
   3206 		}
   3207 	}
   3208 
   3209 	return (count);
   3210 
   3211 }	// end of Extract_Compressed_Events
   3212 
   3213 
   3214 /***************************************************************************
   3215  * Execute_DoList -- Executes commands from the DoList                     *
   3216  *                                                                         *
   3217  * This routine executes any events in the DoList that need to be executed	*
   3218  * on the current game frame.  The events must be executed in a special		*
   3219  * order, so that all systems execute all events in exactly the same			*
   3220  * order.																						*
   3221  *                                                                         *
   3222  * This routine also handles checking the Game CRC sent by other systems	*
   3223  * against my own, to be sure we're still in sync.									*
   3224  *                                                                         *
   3225  * INPUT:                                                                  *
   3226  *		max_houses	# houses to execute commands for									*
   3227  *		base_house	HousesType to start with											*
   3228  *		net			ptr to connection manager; NULL if none						*
   3229  *		skip_crc		a frame-based countdown timer; if it's non-zero, the		*
   3230  *						CRC check will be skipped.  Ignored if NULL.					*
   3231  *		their_frame	array of their frame #'s											*
   3232  *		their_sent	array of # commands they've sent									*
   3233  *		their_recv	array of # commands I've received from them					*
   3234  *                                                                         *
   3235  * (their_xxx are ignored if 'net' is NULL.)											*
   3236  *                                                                         *
   3237  * OUTPUT:                                                                 *
   3238  *		1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.)	*
   3239  *                                                                         *
   3240  * WARNINGS:                                                               *
   3241  *                                                                         *
   3242  * HISTORY:                                                                *
   3243  *   11/21/1995 BRR : Created.                                             *
   3244  *=========================================================================*/
   3245 static int Execute_DoList(int max_houses, HousesType base_house,
   3246 	ConnManClass *net, CDTimerClass<FrameTimerClass> *skip_crc,
   3247 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
   3248 {
   3249 	HousesType house;
   3250 	HouseClass *hptr;
   3251 	int i,j,k;
   3252 	int index;
   3253 	int check_crc;
   3254 
   3255 	Check_Mirror();
   3256 
   3257 #if(TIMING_FIX)
   3258 	//
   3259 	// If MPlayerMaxAhead is recomputed such that it increases, the systems
   3260 	// may try to free-run to the new MaxAhead value.  If so, they may miss
   3261 	// an event that was generated after the TIMING event was created, but
   3262 	// before it executed; this event will be scheduled with the older,
   3263 	// shorter MaxAhead value.  If a system doesn't receive this event, it
   3264 	// may execute past the frame it's scheduled to execute on, creating
   3265 	// a Packet-Recieved-Too-Late error.  To prevent this, find any events
   3266 	// that are scheduled to execute during this "period of vulnerability",
   3267 	// and re-schedule for the end of that period.
   3268 	//
   3269 	for (j = 0; j < DoList.Count; j++) {
   3270 		if (DoList[j].Type != EventClass::FRAMEINFO &&
   3271 			DoList[j].Frame > (unsigned)NewMaxAheadFrame1 &&
   3272 			DoList[j].Frame < (unsigned)NewMaxAheadFrame2) {
   3273 			DoList[j].Frame = (unsigned)NewMaxAheadFrame2;
   3274 			#ifdef MIRROR_QUEUE
   3275 			MirrorList[j].Frame = NewMaxAheadFrame2;
   3276 			#endif
   3277 		}
   3278 	}
   3279 #endif
   3280 
   3281 	//------------------------------------------------------------------------
   3282 	//	Execute the DoList.  Events must be executed in the same order on all
   3283 	//	systems; so, execute them in the order of the HouseClass array.  This
   3284 	//	array is stored in the same order on all systems.
   3285 	//------------------------------------------------------------------------
   3286 	for (i = 0; i < max_houses; i++) {
   3287 		//.....................................................................
   3288 		//	Convert our index into a HousesType value
   3289 		//.....................................................................
   3290 		house = (HousesType)(i + base_house);
   3291 		hptr = HouseClass::As_Pointer(house);
   3292 
   3293 		//.....................................................................
   3294 		// If for some reason this house doesn't exist, skip it.
   3295 		// Also, if this house has exited the game, skip it.  (The user can
   3296 		// generate events after he exits, because the exit event is scheduled
   3297 		// at least FrameSendRate*3 frames ahead.  If one system gets these
   3298 		// packets & another system doesn't, they'll go out of sync because
   3299 		// they aren't checking the CommandCount for that house, since that
   3300 		// house isn't connected any more.)
   3301 		//.....................................................................
   3302 		if (!hptr) {
   3303 			continue;
   3304 		}
   3305 		if (!hptr->IsHuman) {
   3306 			continue;
   3307 		}
   3308 
   3309 		//.....................................................................
   3310 		//	Loop through all events
   3311 		//.....................................................................
   3312 		for (j = 0; j < DoList.Count; j++) {
   3313 
   3314 			if (net)
   3315 				Update_Queue_Mono (net, 6);
   3316 
   3317 			//..................................................................
   3318 			//	If this event was from the currently-executing player ID, and it's
   3319 			//	time to execute it, execute it.
   3320 			//..................................................................
   3321 			if (DoList[j].ID == hptr->ID && (unsigned) Frame >= DoList[j].Frame &&
   3322 				!DoList[j].IsExecuted) {
   3323 
   3324 				//...............................................................
   3325 				//	Error if it's too late to execute this packet!
   3326 				// (Hack: disable this check for solo or skirmish mode.)
   3327 				//...............................................................
   3328 				if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type !=
   3329 					EventClass::FRAMEINFO && Session.Type != GAME_NORMAL &&
   3330 					Session.Type != GAME_SKIRMISH) {
   3331 
   3332 #if(TEN)
   3333 					Send_TEN_Packet_Too_Late();
   3334 #endif	// TEN
   3335 
   3336 #if(MPATH)
   3337 					//Send_MPATH_Packet_Too_Late();
   3338 #endif	// MPATH
   3339 
   3340 					Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame,
   3341 						their_sent, their_recv);
   3342 					WWMessageBox().Process (TXT_PACKET_TOO_LATE);
   3343 					return (0);
   3344 				}
   3345 
   3346 				//...............................................................
   3347 				//	Only execute EXIT & OPTIONS commands if they're from myself.
   3348 				//...............................................................
   3349 				if (DoList[j].Type==EventClass::EXIT ||
   3350 						DoList[j].Type==EventClass::OPTIONS) {
   3351 
   3352 #ifdef WIN32
   3353 					if (DoList[j].Type==EventClass::EXIT) {
   3354 						/*
   3355 						** Flag that this house lost because it quit.
   3356 						*/
   3357 						HousesType quithouse;
   3358 						HouseClass *quithptr;
   3359 
   3360 						for (int player = 0; player < max_houses ; player++) {
   3361 							quithouse = (HousesType)(player + base_house);
   3362 							quithptr = HouseClass::As_Pointer(quithouse);
   3363 							if (!quithptr) {
   3364 								continue;
   3365 							}
   3366 							if (quithptr->ID == DoList[j].ID) {
   3367 								quithptr->IsGiverUpper = true;
   3368 								break;
   3369 							}
   3370 						}
   3371 
   3372 						/*
   3373 						** Send the game statistics packet now since the game is effectivly over
   3374 						*/
   3375 						if (Session.Players.Count() == 2 &&
   3376 								Session.Type == GAME_INTERNET &&
   3377 								!GameStatisticsPacketSent) {
   3378 							Register_Game_End_Time();
   3379 							Send_Statistics_Packet();		//	Event - player aborted, and there were only 2 left.
   3380 						}
   3381 					}
   3382 #endif	//WIN32
   3383 
   3384 					if (Debug_Print_Events) {
   3385 						if (DoList[j].Type==EventClass::EXIT) {
   3386 							printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n",
   3387 								Frame,
   3388 								DoList[j].ID,
   3389 								(HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName,
   3390 								DoList[j].Frame);
   3391 						}
   3392 					}
   3393 
   3394 					if (DoList[j].ID == PlayerPtr->ID) {
   3395 						DoList[j].Execute();
   3396 					} else if (DoList[j].Type==EventClass::EXIT) {
   3397 					//............................................................
   3398 					//	If this EXIT event isn't from myself, destroy the connection
   3399 					//	for that player.  The HousesType for this event is the
   3400 					// connection ID.
   3401 					//............................................................
   3402 						if (Session.Type == GAME_MODEM ||
   3403 							Session.Type == GAME_NULL_MODEM) {
   3404 							//PG Destroy_Null_Connection( house, 0 );
   3405 						} else if ((Session.Type == GAME_IPX ||
   3406 							Session.Type == GAME_INTERNET ||
   3407 							Session.Type == GAME_TEN ||
   3408 							Session.Type == GAME_MPATH) && net) {
   3409 							index = net->Connection_Index (house);
   3410 							if (index != -1) {
   3411 								for (k = index; k < net->Num_Connections() - 1; k++) {
   3412 									their_frame[k] = their_frame[k+1];
   3413 									their_sent[k] = their_sent[k+1];
   3414 									their_recv[k] = their_recv[k+1];
   3415 								}
   3416 								if (Session.Type == GAME_IPX ||
   3417 									Session.Type == GAME_INTERNET) {
   3418 									Destroy_Connection(house,0);
   3419 								}
   3420 #if(TEN)
   3421 								else if (Session.Type == GAME_TEN) {
   3422 									Destroy_TEN_Connection(house,0);
   3423 								}
   3424 #endif	// TEN
   3425 #if(MPATH)
   3426 								else if (Session.Type == GAME_MPATH) {
   3427 									Destroy_MPATH_Connection(house,0);
   3428 								}
   3429 #endif	// MPATH
   3430 							}
   3431 						}
   3432 						//
   3433 						// Special case for recording playback: turn the house over
   3434 						// to the computer.
   3435 						//
   3436 						if (Session.Play && DoList[j].Type==EventClass::EXIT) {
   3437 							hptr->IsHuman = false;
   3438 							hptr->IQ = Rule.MaxIQ;
   3439 							hptr->Computer_Paranoid();
   3440 							strcpy (hptr->IniName,Text_String(TXT_COMPUTER));
   3441 							Session.NumPlayers--;
   3442 						}
   3443 					}
   3444 				}
   3445 
   3446 				//...............................................................
   3447 				//	For a FRAMEINFO event, check the CRC value.
   3448 				//...............................................................
   3449 				else if (DoList[j].Type == EventClass::FRAMEINFO) {
   3450 					//............................................................
   3451 					// Skip the CRC check if we're less than 32 frames into the game;
   3452 					// this will prevent a newly-loaded modem game from instantly
   3453 					// going out of sync, if the games were saved at different
   3454 					// frame numbers.
   3455 					//............................................................
   3456 					if (!skip_crc || *skip_crc == 0) {
   3457 						check_crc = 1;
   3458 					}
   3459 					else {
   3460 						check_crc = 0;
   3461 					}
   3462 					if (check_crc
   3463 						&& DoList[j].Frame == Frame
   3464 						&& DoList[j].Data.FrameInfo.Delay < 32) {
   3465 						index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) &
   3466 							0x001f);
   3467 						if (CRC[index] != DoList[j].Data.FrameInfo.CRC) {
   3468 							Print_CRCs(&DoList[j]);
   3469 
   3470 #if(TEN)
   3471 							Send_TEN_Out_Of_Sync();
   3472 #endif	// TEN
   3473 
   3474 #if(MPATH)
   3475 							//Send_MPATH_Out_Of_Sync();
   3476 #endif	// MPATH
   3477 #if (0)//PG
   3478 							if (WWMessageBox().Process (TXT_OUT_OF_SYNC,
   3479 								TXT_CONTINUE, TXT_STOP) == 0) {
   3480 								if (Session.Type == GAME_MODEM ||
   3481 									Session.Type == GAME_NULL_MODEM) {
   3482 									//PG Destroy_Null_Connection( house, -1 );
   3483 									Shutdown_Modem();
   3484 									Session.Type = GAME_NORMAL;
   3485 								}
   3486 								else if ((Session.Type == GAME_IPX ||
   3487 									Session.Type == GAME_INTERNET) && net) {
   3488 									while (net->Num_Connections()) {
   3489 										Keyboard->Check();
   3490 										Destroy_Connection (net->Connection_ID(0), -1);
   3491 									}
   3492 								}
   3493 #if(TEN)
   3494 								else if (Session.Type == GAME_TEN && net) {
   3495 									while (net->Num_Connections()) {
   3496 										Destroy_TEN_Connection (net->Connection_ID(0), -1);
   3497 									}
   3498 								}
   3499 #endif
   3500 #if(MPATH)
   3501 								else if (Session.Type == GAME_MPATH && net) {
   3502 									while (net->Num_Connections()) {
   3503 										Destroy_MPATH_Connection (net->Connection_ID(0), -1);
   3504 									}
   3505 								}
   3506 #endif
   3507 								Map.Flag_To_Redraw(true);
   3508 							}
   3509 							else {
   3510 								return (0);
   3511 							}
   3512 #endif
   3513 							return (1);
   3514 						}
   3515 					}
   3516 				}
   3517 				//...............................................................
   3518 				//	Execute other commands
   3519 				//...............................................................
   3520 				else {
   3521 					DoList[j].Execute();
   3522 				}
   3523 
   3524 				//...............................................................
   3525 				//	Mark this event as executed.
   3526 				//...............................................................
   3527 				DoList[j].IsExecuted = 1;
   3528 				#ifdef MIRROR_QUEUE
   3529 				MirrorList[j].IsExecuted = 1;
   3530 				#endif
   3531 			}
   3532 		}
   3533 	}
   3534 
   3535 	return (1);
   3536 
   3537 }	// end of Execute_DoList
   3538 
   3539 
   3540 /***************************************************************************
   3541  * Clean_DoList -- Cleans out old events from the DoList                   *
   3542  *                                                                         *
   3543  * Currently, an event can only be removed from the DoList if it's at the	*
   3544  * head of the list; and event can't be removed from the middle.  So,		*
   3545  * this routine loops as long as the next event in the DoList has been		*
   3546  * executed, it's removed.																	*
   3547  *                                                                         *
   3548  * INPUT:                                                                  *
   3549  *		net		ptr to connection manager; ignored if NULL						*
   3550  *                                                                         *
   3551  * OUTPUT:                                                                 *
   3552  *		none.																						*
   3553  *                                                                         *
   3554  * WARNINGS:                                                               *
   3555  *		none.																						*
   3556  *                                                                         *
   3557  * HISTORY:                                                                *
   3558  *   11/21/1995 BRR : Created.                                             *
   3559  *=========================================================================*/
   3560 static void Clean_DoList(ConnManClass *net)
   3561 {
   3562 	while (DoList.Count) {
   3563 
   3564 		Keyboard->Check();
   3565 
   3566 		if (net)
   3567 			Update_Queue_Mono (net, 7);
   3568 
   3569 		//.....................................................................
   3570 		//	Discard events that have been executed, OR it's too late to execute.
   3571 		//	(This happens if another player exits the game; he'll leave FRAMEINFO
   3572 		//	events lying around in my queue.  They won't have been "executed",
   3573 		//	because his IPX connection was destroyed.)
   3574 		//.....................................................................
   3575 		if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) {
   3576 			DoList.Next();
   3577 			#ifdef MIRROR_QUEUE
   3578 			MirrorList.Next();
   3579 			#endif
   3580 		}
   3581 		else {
   3582 			break;
   3583 		}
   3584 	}
   3585 
   3586 }	// end of Clean_DoList
   3587 
   3588 
   3589 /***************************************************************************
   3590  * Queue_Record -- Records the DoList to disk                              *
   3591  *                                                                         *
   3592  * This routine just saves any events in the DoList to disk; we can later	*
   3593  * "play back" the recording just be pulling events from disk rather than	*
   3594  * from the network!																			*
   3595  *                                                                         *
   3596  * INPUT:                                                                  *
   3597  *		none.																						*
   3598  *                                                                         *
   3599  * OUTPUT:                                                                 *
   3600  *		none.																						*
   3601  *                                                                         *
   3602  * WARNINGS:                                                               *
   3603  *		none.																						*
   3604  *                                                                         *
   3605  * HISTORY:                                                                *
   3606  *   08/14/1995 BRR : Created.                                             *
   3607  *=========================================================================*/
   3608 static void Queue_Record(void)
   3609 {
   3610 	int i,j;
   3611 
   3612 	//------------------------------------------------------------------------
   3613 	//	Compute # of events to save this frame
   3614 	//------------------------------------------------------------------------
   3615 	j = 0;
   3616 	for (i = 0; i < DoList.Count; i++) {
   3617 		if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
   3618 			j++;
   3619 		}
   3620 	}
   3621 
   3622 	//------------------------------------------------------------------------
   3623 	//	Save the # of events, then all events.
   3624 	//------------------------------------------------------------------------
   3625 	Session.RecordFile.Write (&j,sizeof(j));
   3626 	for (i = 0; i < DoList.Count; i++) {
   3627 		if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
   3628 			Session.RecordFile.Write (&DoList[i],sizeof (EventClass));
   3629 			j--;
   3630 		}
   3631 	}
   3632 
   3633 }	/* end of Queue_Record */
   3634 
   3635 
   3636 /***************************************************************************
   3637  * Queue_Playback -- plays back queue entries from a record file           *
   3638  *                                                                         *
   3639  * This routine reads events from disk, putting them into the DoList;		*
   3640  * it then executes the DoList just like the network version does.  The		*
   3641  * result is that the game "plays back" like a recording.						*
   3642  *																									*
   3643  * This routine detects mouse motion and stops playback, so it can work		*
   3644  * like an "attract" mode, showing a demo of the game itself.					*
   3645  *                                                                         *
   3646  * INPUT:                                                                  *
   3647  *		none.																						*
   3648  *                                                                         *
   3649  * OUTPUT:                                                                 *
   3650  *		none.																						*
   3651  *                                                                         *
   3652  * WARNINGS:                                                               *
   3653  *		none.																						*
   3654  *                                                                         *
   3655  * HISTORY:                                                                *
   3656  *   05/15/1995 BRR : Created.                                             *
   3657  *=========================================================================*/
   3658 static void Queue_Playback(void)
   3659 {
   3660 	int numevents;
   3661 	EventClass event;
   3662 	int i;
   3663 	int ok;
   3664   	static int mx,my;
   3665 	int max_houses;
   3666 	HousesType base_house;
   3667 	int key;
   3668 	int testframe;
   3669 
   3670 	//------------------------------------------------------------------------
   3671 	//	If the user hits ESC, stop the playback
   3672 	//------------------------------------------------------------------------
   3673 	if (Keyboard->Check()) {
   3674 		key = Keyboard->Get();
   3675 		if (key == KA_ESC || Session.Attract) {
   3676 			GameActive = 0;
   3677 			return;
   3678 		}
   3679 	}
   3680 
   3681 	//------------------------------------------------------------------------
   3682 	// If we're in "Attract" mode, and the user moves the mouse, stop the
   3683 	// playback.
   3684 	//------------------------------------------------------------------------
   3685 	if (Session.Attract && Frame > 0 &&
   3686 		(mx != Get_Mouse_X() || my != Get_Mouse_Y())) {
   3687 		GameActive = 0;
   3688 		return;
   3689 	}
   3690 	mx = Get_Mouse_X();
   3691 	my = Get_Mouse_Y();
   3692 
   3693 	//------------------------------------------------------------------------
   3694 	//	Compute the Game's CRC
   3695 	//------------------------------------------------------------------------
   3696 	Compute_Game_CRC();
   3697 	CRC[Frame & 0x001f] = GameCRC;
   3698 
   3699 	//------------------------------------------------------------------------
   3700 	// If we've reached the CRC print frame, do so & exit
   3701 	//------------------------------------------------------------------------
   3702 	if (Frame >= Session.TrapPrintCRC) {
   3703 		Print_CRCs(NULL);
   3704 		Prog_End("Queue_Playback reached CRC print frame", true);
   3705 		Emergency_Exit(0);
   3706 	}
   3707 
   3708 	//------------------------------------------------------------------------
   3709 	//	Don't read anything the first time through (since the Queue_AI_Network
   3710 	//	routine didn't write anything the first time through); do this after the
   3711 	//	CRC is computed, since we'll still need a CRC for Frame 0.
   3712 	//------------------------------------------------------------------------
   3713 	if (Frame==0 && Session.Type!=GAME_NORMAL) {
   3714 		return;
   3715 	}
   3716 
   3717 	//------------------------------------------------------------------------
   3718 	// Only process every 'FrameSendRate' frames
   3719 	//------------------------------------------------------------------------
   3720 	testframe = ((Frame + (Session.FrameSendRate - 1)) /
   3721 		Session.FrameSendRate) * Session.FrameSendRate;
   3722 	if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) &&
   3723 		Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   3724 		if (Frame != testframe) {
   3725 			return;
   3726 		}
   3727 	}
   3728 
   3729 	//------------------------------------------------------------------------
   3730 	//	Read the DoList from disk
   3731 	//------------------------------------------------------------------------
   3732 	ok = 1;
   3733 	if (Session.RecordFile.Read (&numevents, sizeof(numevents)) ==
   3734 		sizeof(numevents)) {
   3735 		for (i = 0; i < numevents; i++) {
   3736 			if (Session.RecordFile.Read (&event, sizeof(EventClass)) ==
   3737 				sizeof(EventClass)) {
   3738 				event.IsExecuted = 0;
   3739 				DoList.Add (event);
   3740 				#ifdef MIRROR_QUEUE
   3741 				MirrorList.Add(event);
   3742 				#endif
   3743 			}
   3744 			else {
   3745 				ok = 0;
   3746 				break;
   3747 			}
   3748 		}
   3749 	}
   3750 	else {
   3751 		ok = 0;
   3752 	}
   3753 
   3754 	if (!ok) {
   3755 		GameActive = 0;
   3756 		return;
   3757 	}
   3758 
   3759 
   3760 	//------------------------------------------------------------------------
   3761 	// Execute the DoList; if an error occurs, bail out.
   3762 	//------------------------------------------------------------------------
   3763 	if (Session.Type == GAME_NORMAL) {
   3764 		max_houses = 1;
   3765 		base_house = PlayerPtr->Class->House;
   3766 	}
   3767 	else {
   3768 		max_houses = Session.MaxPlayers;
   3769 		base_house = HOUSE_MULTI1;
   3770 	}
   3771 	if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) {
   3772 		GameActive = 0;
   3773 		return;
   3774 	}
   3775 
   3776 	//------------------------------------------------------------------------
   3777 	//	Clean out the DoList
   3778 	//------------------------------------------------------------------------
   3779 	Clean_DoList(NULL);
   3780 
   3781 }	/* end of Queue_Playback */
   3782 
   3783 
   3784 /***************************************************************************
   3785  * Compute_Game_CRC -- Computes a CRC value of the entire game.				*
   3786  *                                                                         *
   3787  * INPUT:                                                                  *
   3788  *		none.																						*
   3789  *                                                                         *
   3790  * OUTPUT:                                                                 *
   3791  *		none.																						*
   3792  *                                                                         *
   3793  * WARNINGS:                                                               *
   3794  *		none.																						*
   3795  *                                                                         *
   3796  * HISTORY:                                                                *
   3797  *   05/09/1995 BRR : Created.                                             *
   3798  *=========================================================================*/
   3799 static void Compute_Game_CRC(void)
   3800 {
   3801 	int i,j;
   3802 	VesselClass *vessp;
   3803 	InfantryClass *infp;
   3804 	UnitClass *unitp;
   3805 	BuildingClass *bldgp;
   3806 	ObjectClass *objp;
   3807 	HouseClass *housep;
   3808 
   3809 	GameCRC = 0;
   3810 
   3811 	//------------------------------------------------------------------------
   3812 	//	Infantry
   3813 	//------------------------------------------------------------------------
   3814 	for (i = 0; i < Infantry.Count(); i++) {
   3815 		infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3816 		Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3817 		Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
   3818 		Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
   3819 	}
   3820 
   3821 	//------------------------------------------------------------------------
   3822 	//	Units
   3823 	//------------------------------------------------------------------------
   3824 	for (i = 0; i < Units.Count(); i++) {
   3825 		unitp = (UnitClass *)Units.Active_Ptr(i);
   3826 		Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
   3827 			(int)unitp->SecondaryFacing);
   3828 	}
   3829 
   3830 	//------------------------------------------------------------------------
   3831 	//	Shippies
   3832 	//------------------------------------------------------------------------
   3833 	for (i = 0; i < Vessels.Count(); i++) {
   3834 		vessp = (VesselClass *)Vessels.Active_Ptr(i);
   3835 		Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing);
   3836 		Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom);
   3837 		Add_CRC (&GameCRC, (int)vessp->Strength);
   3838 		Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom);
   3839 	}
   3840 
   3841 	//------------------------------------------------------------------------
   3842 	//	Buildings
   3843 	//------------------------------------------------------------------------
   3844 	for (i = 0; i < Buildings.Count(); i++) {
   3845 		bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3846 		Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3847 	}
   3848 
   3849 	//------------------------------------------------------------------------
   3850 	//	Houses
   3851 	//------------------------------------------------------------------------
   3852 	for (i = 0; i < Houses.Count(); i++) {
   3853 		housep = (HouseClass *)Houses.Active_Ptr(i);
   3854 		Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
   3855 			(int)housep->Drain);
   3856 	}
   3857 
   3858 	//------------------------------------------------------------------------
   3859 	//	Map Layers
   3860 	//------------------------------------------------------------------------
   3861 	for (i = 0; i < LAYER_COUNT; i++) {
   3862 		for (j = 0; j < Map.Layer[i].Count(); j++) {
   3863 			objp = Map.Layer[i][j];
   3864 			Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3865 		}
   3866 	}
   3867 
   3868 	//------------------------------------------------------------------------
   3869 	//	Logic Layers
   3870 	//------------------------------------------------------------------------
   3871 	for (i = 0; i < Logic.Count(); i++) {
   3872 		objp = Logic[i];
   3873 		Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3874 	}
   3875 
   3876 	//------------------------------------------------------------------------
   3877 	//	A random #
   3878 	//------------------------------------------------------------------------
   3879 //	Add_CRC(&GameCRC, Scen.RandomNumber.Seed);
   3880 	Add_CRC(&GameCRC, Scen.RandomNumber);
   3881 
   3882 }	/* end of Compute_Game_CRC */
   3883 
   3884 
   3885 /***************************************************************************
   3886  * Add_CRC -- Adds a value to a CRC                                        *
   3887  *                                                                         *
   3888  * INPUT:                                                                  *
   3889  *		crc		ptr to crc																	*
   3890  *		val		value to add																*
   3891  *                                                                         *
   3892  * OUTPUT:                                                                 *
   3893  *		none																						*
   3894  *                                                                         *
   3895  * WARNINGS:                                                               *
   3896  *		none																						*
   3897  *                                                                         *
   3898  * HISTORY:                                                                *
   3899  *   05/09/1995 BRR : Created.                                             *
   3900  *=========================================================================*/
   3901 void Add_CRC(unsigned long *crc, unsigned long val)
   3902 {
   3903 	int hibit;
   3904 
   3905 	if ( (*crc) & 0x80000000) {
   3906 		hibit = 1;
   3907 	}
   3908 	else {
   3909 		hibit = 0;
   3910 	}
   3911 
   3912 	(*crc) <<= 1;
   3913 	(*crc) += val;
   3914 	(*crc) += hibit;
   3915 
   3916 }	/* end of Add_CRC */
   3917 
   3918 /***************************************************************************
   3919  * Print_CRCs -- Prints a data file for finding Sync Bugs						*
   3920  *                                                                         *
   3921  * INPUT:                                                                  *
   3922  *		ev -- event to display																*
   3923  *                                                                         *
   3924  * OUTPUT:                                                                 *
   3925  *		none																						*
   3926  *                                                                         *
   3927  * WARNINGS:                                                               *
   3928  *		none																						*
   3929  *                                                                         *
   3930  * HISTORY:                                                                *
   3931  *   05/09/1995 BRR : Created.                                             *
   3932  *=========================================================================*/
   3933 static void Print_CRCs(EventClass *ev)
   3934 {
   3935 	int i,j;
   3936 	InfantryClass *infp;
   3937 	UnitClass *unitp;
   3938 	VesselClass *vesselp;
   3939 	BuildingClass *bldgp;
   3940 	ObjectClass *objp;
   3941 	FILE *fp;
   3942 	HouseClass *housep;
   3943 	HousesType house;
   3944 	int color;
   3945 
   3946 	Mono_Clear_Screen();
   3947 	Mono_Set_Cursor (0,0);
   3948 	fp = fopen("OUT.TXT","wt");
   3949 	if (fp==NULL) {
   3950 		return;
   3951 	}
   3952 
   3953 	for (i = 0; i < 32; i++) {
   3954 		fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]);
   3955 	}
   3956 
   3957 	//
   3958 	// Houses
   3959 	//
   3960 	for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
   3961 		GameCRC = 0;
   3962 		housep = HouseClass::As_Pointer (house);
   3963 		if (housep) {
   3964 			HousesType actlike = housep->ActLike;
   3965 			color = housep->RemapColor;
   3966 			fprintf(fp,"%s: IsHuman:%d  Color:%s  ID:%d  ActLike:%s\n",
   3967 				housep->IniName,
   3968 				housep->IsHuman,
   3969 				ColorNames[color],
   3970 				housep->ID,
   3971 				HouseClass::As_Pointer(actlike)->Class->Name());
   3972 			Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
   3973 				(int)housep->Drain);
   3974 			Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC);
   3975 		}
   3976 	}
   3977 
   3978 	//
   3979 	// Infantry
   3980 	//
   3981 	for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
   3982 		housep = HouseClass::As_Pointer (house);
   3983 		if (housep) {
   3984 			GameCRC = 0;
   3985 			fprintf(fp,"-------------------- %s Infantry -------------------\n",
   3986 				housep->Class->Name());
   3987 			for (i = 0; i < Infantry.Count(); i++) {
   3988 				infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3989 				if (infp->Owner()==house) {
   3990 					Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3991 					Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
   3992 					Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
   3993 					fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d   Tgt:%x Speed:%d NavCom:%x\n",
   3994 						infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3995 						infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom);
   3996 				}
   3997 			}
   3998 			Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC);
   3999 		}
   4000 	}
   4001 
   4002 	//
   4003 	// Units
   4004 	//
   4005 	for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
   4006 		housep = HouseClass::As_Pointer (house);
   4007 		if (housep) {
   4008 			GameCRC = 0;
   4009 			fprintf(fp,"-------------------- %s Units -------------------\n",
   4010 				housep->Class->Name());
   4011 			for (i = 0; i < Units.Count(); i++) {
   4012 				unitp = (UnitClass *)Units.Active_Ptr(i);
   4013 				if (unitp->Owner()==house) {
   4014 					Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
   4015 						(int)unitp->SecondaryFacing);
   4016 					fprintf(fp,
   4017 						"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d   Tgt:%x\n",
   4018 						unitp->Coord,(int)unitp->PrimaryFacing,
   4019 						(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   4020 						unitp->Class->Type, unitp->As_Target());
   4021 				}
   4022 			}
   4023 			Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC);
   4024 		}
   4025 	}
   4026 
   4027 	//
   4028 	// Vessels
   4029 	//
   4030 	for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
   4031 		housep = HouseClass::As_Pointer (house);
   4032 		if (housep) {
   4033 			GameCRC = 0;
   4034 			fprintf(fp,"-------------------- %s Vessels -------------------\n",
   4035 				housep->Class->Name());
   4036 			for (i = 0; i < Vessels.Count(); i++) {
   4037 				vesselp = (VesselClass *)Vessels.Active_Ptr(i);
   4038 				if (vesselp->Owner()==house) {
   4039 					Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing);
   4040 					Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom);
   4041 					Add_CRC (&GameCRC, (int)vesselp->Strength);
   4042 					Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom);
   4043 					fprintf(fp,
   4044 						"COORD:%x   Facing:%d   Mission:%d   Strength:%d Type:%d   Tgt:%x\n",
   4045 						vesselp->Coord,(int)vesselp->PrimaryFacing,
   4046 						vesselp->Get_Mission(), vesselp->Strength,
   4047 						vesselp->Class->Type, vesselp->As_Target());
   4048 				}
   4049 			}
   4050 			Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC);
   4051 		}
   4052 	}
   4053 
   4054 	//
   4055 	// Buildings
   4056 	//
   4057 	for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
   4058 		housep = HouseClass::As_Pointer (house);
   4059 		if (housep) {
   4060 			GameCRC = 0;
   4061 			fprintf(fp,"-------------------- %s Buildings -------------------\n",
   4062 				housep->Class->Name());
   4063 			for (i = 0; i < Buildings.Count(); i++) {
   4064 				bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   4065 				if (bldgp->Owner()==house) {
   4066 					Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   4067 					fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d   Tgt:%x\n",
   4068 						bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   4069 						bldgp->Class->Type, bldgp->As_Target());
   4070 				}
   4071 			}
   4072 			Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC);
   4073 		}
   4074 	}
   4075 
   4076 	//
   4077 	// Animations
   4078 	//
   4079 	AnimClass *animp;
   4080 		fprintf(fp,"-------------------- Animations -------------------\n");
   4081 	for (i = 0; i < Anims.Count(); i++) {
   4082 		animp = (AnimClass *)Anims.Active_Ptr(i);
   4083 		fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n",
   4084 			animp->xObject,
   4085 			animp->OwnerHouse,
   4086 			animp->Loops);
   4087 	}
   4088 
   4089 	//------------------------------------------------------------------------
   4090 	//	Map Layers
   4091 	//------------------------------------------------------------------------
   4092 	GameCRC = 0;
   4093 	for (i = 0; i < LAYER_COUNT; i++) {
   4094 		fprintf(fp,">>>> MAP LAYER %d <<<<\n",i);
   4095 		for (j = 0; j < Map.Layer[i].Count(); j++) {
   4096 			objp = Map.Layer[i][j];
   4097 			Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   4098 			fprintf(fp,"Object %d: %x ",j,objp->Coord);
   4099 
   4100 			if (objp->What_Am_I() == RTTI_AIRCRAFT)
   4101 				fprintf(fp,"Aircraft  (Type:%d) ",
   4102 					(AircraftType)(*((AircraftClass *)objp)));
   4103 			else if (objp->What_Am_I() == RTTI_ANIM)
   4104 				fprintf(fp,"Anim      (Type:%d) ",
   4105 					(AnimType)(*((AnimClass *)objp)));
   4106 			else if (objp->What_Am_I() == RTTI_BUILDING)
   4107 				fprintf(fp,"Building  (Type:%d) ",
   4108 					(StructType)(*((BuildingClass *)objp)));
   4109 			else if (objp->What_Am_I() == RTTI_BULLET)
   4110 				fprintf(fp,"Bullet    (Type:%d) ",
   4111 					(BulletType)(*((BulletClass *)objp)));
   4112 			else if (objp->What_Am_I() == RTTI_INFANTRY)
   4113 				fprintf(fp,"Infantry  (Type:%d) ",
   4114 					(InfantryType)(*((InfantryClass *)objp)));
   4115 			else if (objp->What_Am_I() == RTTI_OVERLAY)
   4116 				fprintf(fp,"Overlay   (Type:%d) ",
   4117 					(OverlayType)(*((OverlayClass *)objp)));
   4118 			else if (objp->What_Am_I() == RTTI_SMUDGE)
   4119 				fprintf(fp,"Smudge    (Type:%d) ",
   4120 					(SmudgeType)(*((SmudgeClass *)objp)));
   4121 			else if (objp->What_Am_I() == RTTI_TEMPLATE)
   4122 				fprintf(fp,"Template  (Type:%d) ",
   4123 					(TemplateType)(*((TemplateClass *)objp)));
   4124 			else if (objp->What_Am_I() == RTTI_TERRAIN)
   4125 				fprintf(fp,"Terrain   (Type:%d) ",
   4126 					(TerrainType)(*((TerrainClass *)objp)));
   4127 			else if (objp->What_Am_I() == RTTI_UNIT)
   4128 				fprintf(fp,"Unit      (Type:%d) ",
   4129 					(UnitType)(*((UnitClass *)objp)));
   4130 			else if (objp->What_Am_I() == RTTI_VESSEL)
   4131 				fprintf(fp,"Vessel    (Type:%d) ",
   4132 					(VesselType)(*((VesselClass *)objp)));
   4133 
   4134 			house = objp->Owner();
   4135 			if (house!=HOUSE_NONE) {
   4136 				housep = HouseClass::As_Pointer (house);
   4137 				fprintf(fp,"Owner: %s\n",housep->Class->IniName);
   4138 			}
   4139 			else {
   4140 				fprintf(fp,"Owner: NONE\n");
   4141 			}
   4142 		}
   4143 	}
   4144 	Mono_Printf("Map Layers:%x  \n",GameCRC);
   4145 
   4146 	//------------------------------------------------------------------------
   4147 	//	Logic Layers
   4148 	//------------------------------------------------------------------------
   4149 	GameCRC = 0;
   4150 	fprintf(fp,">>>> LOGIC LAYER <<<<\n");
   4151 	for (i = 0; i < Logic.Count(); i++) {
   4152 		objp = Logic[i];
   4153 		Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   4154 		fprintf(fp,"Object %d: %x ",i,objp->Coord);
   4155 
   4156 		if (objp->What_Am_I() == RTTI_AIRCRAFT)
   4157 			fprintf(fp,"Aircraft  (Type:%d) ",
   4158 				(AircraftType)(*((AircraftClass *)objp)));
   4159 		else if (objp->What_Am_I() == RTTI_ANIM)
   4160 			fprintf(fp,"Anim      (Type:%d) ",
   4161 				(AnimType)(*((AnimClass *)objp)));
   4162 		else if (objp->What_Am_I() == RTTI_BUILDING)
   4163 			fprintf(fp,"Building  (Type:%d) ",
   4164 				(StructType)(*((BuildingClass *)objp)));
   4165 		else if (objp->What_Am_I() == RTTI_BULLET)
   4166 			fprintf(fp,"Bullet    (Type:%d) ",
   4167 				(BulletType)(*((BulletClass *)objp)));
   4168 		else if (objp->What_Am_I() == RTTI_INFANTRY)
   4169 			fprintf(fp,"Infantry  (Type:%d) ",
   4170 				(InfantryType)(*((InfantryClass *)objp)));
   4171 		else if (objp->What_Am_I() == RTTI_OVERLAY)
   4172 			fprintf(fp,"Overlay   (Type:%d) ",
   4173 				(OverlayType)(*((OverlayClass *)objp)));
   4174 		else if (objp->What_Am_I() == RTTI_SMUDGE)
   4175 			fprintf(fp,"Smudge    (Type:%d) ",
   4176 				(SmudgeType)(*((SmudgeClass *)objp)));
   4177 		else if (objp->What_Am_I() == RTTI_TEMPLATE)
   4178 			fprintf(fp,"Template  (Type:%d) ",
   4179 				(TemplateType)(*((TemplateClass *)objp)));
   4180 		else if (objp->What_Am_I() == RTTI_TERRAIN)
   4181 			fprintf(fp,"Terrain   (Type:%d) ",
   4182 				(TerrainType)(*((TerrainClass *)objp)));
   4183 		else if (objp->What_Am_I() == RTTI_UNIT)
   4184 			fprintf(fp,"Unit      (Type:%d) ",
   4185 				(UnitType)(*((UnitClass *)objp)));
   4186 
   4187 		house = objp->Owner();
   4188 		if (house!=HOUSE_NONE) {
   4189 			housep = HouseClass::As_Pointer (house);
   4190 			fprintf(fp,"Owner: %s\n",housep->Class->IniName);
   4191 		}
   4192 		else {
   4193 			fprintf(fp,"Owner: NONE\n");
   4194 		}
   4195 	}
   4196 	Mono_Printf("Logic:%x  \n",GameCRC);
   4197 
   4198 	//------------------------------------------------------------------------
   4199 	//	Random # generator, frame #
   4200 	//------------------------------------------------------------------------
   4201 	Mono_Printf("Random Number:%x  \n",Scen.RandomNumber.Seed);
   4202 #ifdef RANDOM_COUNT
   4203 	fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n",
   4204 		Scen.RandomNumber.Seed,
   4205 		Scen.RandomNumber.Count1,
   4206 		Scen.RandomNumber.Count2);
   4207 #else
   4208 	fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed);
   4209 #endif
   4210 
   4211 	Mono_Printf("My Frame:%d  \n",Frame);
   4212 	fprintf(fp,"My Frame:%d\n",Frame);
   4213 
   4214 	if (ev) {
   4215 		fprintf(fp,"\n");
   4216 		fprintf(fp,"Offending event:\n");
   4217 		fprintf(fp,"  Type:         %d\n",ev->Type);
   4218 		fprintf(fp,"  Frame:        %d\n",ev->Frame);
   4219 		fprintf(fp,"  ID:           %x\n",ev->ID);
   4220 		fprintf(fp,"  CRC:          %x\n",ev->Data.FrameInfo.CRC);
   4221 		fprintf(fp,"  CommandCount: %d\n",ev->Data.FrameInfo.CommandCount);
   4222 		fprintf(fp,"  Delay:        %d\n",ev->Data.FrameInfo.Delay);
   4223 	}
   4224 
   4225 	fclose(fp);
   4226 
   4227 }	/* end of Print_CRCs */
   4228 
   4229 
   4230 /***************************************************************************
   4231  * Init_Queue_Mono -- inits mono display                                   *
   4232  *                                                                         *
   4233  * This routine steals control of the mono screen away from the rest of		*
   4234  * the engine, by setting the global IsMono; if IsMono is set, the other	*
   4235  * routines in this module turn off the Mono display when they're done		*
   4236  * with it, so the rest of the engine won't over-write what we're writing.	*
   4237  *                                                                         *
   4238  * INPUT:                                                                  *
   4239  *		net		ptr to connection manager												*
   4240  *                                                                         *
   4241  * OUTPUT:                                                                 *
   4242  *		none.																						*
   4243  *                                                                         *
   4244  * WARNINGS:                                                               *
   4245  *		none.																						*
   4246  *                                                                         *
   4247  * HISTORY:                                                                *
   4248  *   11/21/1995 BRR : Created.                                             *
   4249  *=========================================================================*/
   4250 static void Init_Queue_Mono(ConnManClass *net)
   4251 {
   4252 #if(SHOW_MONO)
   4253 	//------------------------------------------------------------------------
   4254 	// Set 'IsMono' so we can steal the mono screen from the engine
   4255 	//------------------------------------------------------------------------
   4256 	if ((Frame==0 || Session.LoadGame) && MonoClass::Is_Enabled()) {
   4257 		IsMono = true;
   4258 	}
   4259 
   4260 	//------------------------------------------------------------------------
   4261 	// Enable mono output for our stuff; we must Disable it before we return
   4262 	// control to the engine.
   4263 	//------------------------------------------------------------------------
   4264 	if (IsMono)
   4265 		MonoClass::Enable();
   4266 
   4267 	if (net->Num_Connections() > 0) {
   4268 		//.....................................................................
   4269 		//	Network mono debugging screen
   4270 		//.....................................................................
   4271 		if (NetMonoMode==0) {
   4272 			if (Frame==0 || Session.LoadGame || NewMonoMode) {
   4273 				net->Configure_Debug (0, sizeof (CommHeaderType),
   4274 					sizeof(EventClass::EventType), EventClass::EventNames, 0, 27);
   4275 				net->Mono_Debug_Print (0,1);
   4276 				NewMonoMode = 0;
   4277 			}
   4278 			else {
   4279 				net->Mono_Debug_Print (0,0);
   4280 			}
   4281 		}
   4282 		//.....................................................................
   4283 		//	Flow control debugging output
   4284 		//.....................................................................
   4285 		else {
   4286 			if (NewMonoMode) {
   4287 				Mono_Clear_Screen();
   4288 				Mono_Printf("                         Queue AI:\n");	// flowcount[0]
   4289 				Mono_Printf("                Build Packet Loop:\n");	// flowcount[1]
   4290 				Mono_Printf("                       Frame Sync:\n");	// flowcount[2]
   4291 				Mono_Printf("                Frame Sync Resend:\n");	// flowcount[3]
   4292 				Mono_Printf("               Frame Sync Timeout:\n");	// flowcount[4]
   4293 				Mono_Printf("           Frame Sync New Message:\n");	// flowcount[5]
   4294 				Mono_Printf("                 DoList Execution:\n");	// flowcount[6]
   4295 				Mono_Printf("                  DoList Cleaning:\n");	// flowcount[7]
   4296 				Mono_Printf("\n");
   4297 				Mono_Printf("                            Frame:\n");
   4298 				Mono_Printf("                 Session.MaxAhead:\n");
   4299 				Mono_Printf("                       their_recv:\n");
   4300 				Mono_Printf("                       their_sent:\n");
   4301 				Mono_Printf("                          my_sent:\n");
   4302 				NewMonoMode = 0;
   4303 			}
   4304 		}
   4305 	}
   4306 #else
   4307 	net = net;
   4308 #endif
   4309 }	// end of Init_Queue_Mono
   4310 
   4311 
   4312 /***************************************************************************
   4313  * Update_Queue_Mono -- updates mono display                               *
   4314  *                                                                         *
   4315  * INPUT:                                                                  *
   4316  *		net				ptr to connection manager										*
   4317  *		flow_index		index # for flow-count updates								*
   4318  *							-1: display															*
   4319  *                                                                         *
   4320  * OUTPUT:                                                                 *
   4321  *		none.																						*
   4322  *                                                                         *
   4323  * WARNINGS:                                                               *
   4324  *		none.																						*
   4325  *                                                                         *
   4326  * HISTORY:                                                                *
   4327  *   11/21/1995 BRR : Created.                                             *
   4328  *=========================================================================*/
   4329 static void Update_Queue_Mono(ConnManClass *net, int flow_index)
   4330 {
   4331 #if(SHOW_MONO)
   4332 	static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
   4333 
   4334 	//------------------------------------------------------------------------
   4335 	// If 'NetMonoMode' is 1, display flowcount info
   4336 	//------------------------------------------------------------------------
   4337 	if (NetMonoMode==1) {
   4338 		if (flow_index >= 0 && flow_index < 20) {
   4339 			Mono_Set_Cursor(35,flow_index);
   4340 			flowcount[flow_index]++;
   4341 			Mono_Printf("%d",flowcount[flow_index]);
   4342 		}
   4343 	}
   4344 	//------------------------------------------------------------------------
   4345 	// Otherwise, display the connection debug screen
   4346 	//------------------------------------------------------------------------
   4347 	else {
   4348 		net->Mono_Debug_Print (0,0);
   4349 	}
   4350 
   4351 #else
   4352 	flow_index = flow_index;
   4353 	net = net;
   4354 #endif
   4355 
   4356 }	// end of Update_Queue_Mono
   4357 
   4358 
   4359 /***************************************************************************
   4360  * Print_Framesync_Values -- displays frame-sync variables                 *
   4361  *                                                                         *
   4362  * INPUT:                                                                  *
   4363  *		curframe					current game Frame #										*
   4364  *		max_ahead				max-ahead value											*
   4365  *		num_connections		# connections												*
   4366  *		their_recv				# commands I've received from my connections		*
   4367  *		their_sent				# commands each connection claims to have sent	*
   4368  *		my_sent					# commands I've sent										*
   4369  *                                                                         *
   4370  * OUTPUT:                                                                 *
   4371  *		none.																						*
   4372  *                                                                         *
   4373  * WARNINGS:                                                               *
   4374  *		none.																						*
   4375  *                                                                         *
   4376  * HISTORY:                                                                *
   4377  *   11/21/1995 BRR : Created.                                             *
   4378  *=========================================================================*/
   4379 static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
   4380 	int num_connections, unsigned short *their_recv,
   4381 	unsigned short *their_sent, unsigned short my_sent)
   4382 {
   4383 #if(SHOW_MONO)
   4384 	int i;
   4385 
   4386 	if (NetMonoMode==1) {
   4387 		Mono_Set_Cursor(35,9);
   4388 		Mono_Printf("%d",curframe);
   4389 
   4390 		Mono_Set_Cursor(35,10);
   4391 		Mono_Printf("%d",max_ahead);
   4392 
   4393 		for (i = 0; i < num_connections; i++) {
   4394 			Mono_Set_Cursor(35 + i*5,11);
   4395 			Mono_Printf("%4d",(int)their_recv[i]);
   4396 		}
   4397 
   4398 		for (i = 0; i < num_connections; i++) {
   4399 			Mono_Set_Cursor(35 + i*5,12);
   4400 			Mono_Printf("%4d",(int)their_sent[i]);
   4401 		}
   4402 
   4403 		Mono_Set_Cursor(35,13);
   4404 		Mono_Printf("%4d",(int)my_sent);
   4405 	}
   4406 #else
   4407 	curframe = curframe;
   4408 	max_ahead = max_ahead;
   4409 	num_connections = num_connections;
   4410 	their_recv = their_recv;
   4411 	their_sent = their_sent;
   4412 	my_sent = my_sent;
   4413 #endif
   4414 }	// end of Print_Framesync_Values
   4415 
   4416 
   4417 /***************************************************************************
   4418  * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk                *
   4419  *                                                                         *
   4420  * INPUT:                                                                  *
   4421  *		event		ptr to event to print													*
   4422  *                                                                         *
   4423  * OUTPUT:                                                                 *
   4424  *		none.																						*
   4425  *                                                                         *
   4426  * WARNINGS:                                                               *
   4427  *		none.																						*
   4428  *                                                                         *
   4429  * HISTORY:                                                                *
   4430  *   06/28/1996 BRR : Created.                                             *
   4431  *=========================================================================*/
   4432 void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net,
   4433 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
   4434 {
   4435 	FILE *fp;
   4436 	int i;
   4437 	HousesType house;
   4438 
   4439 	fp = fopen("toolate.txt", "wt");
   4440 	if (!fp) {
   4441 		return;
   4442 	}
   4443 	fprintf(fp,"----------------- Event data: ----------------------\n");
   4444 	fprintf(fp,"Type:       %s\n",EventClass::EventNames[event->Type]);
   4445 	fprintf(fp,"Frame:      %d\n",event->Frame);
   4446 	fprintf(fp,"ID:         %d\n",event->ID);
   4447 
   4448 	for (i = 0; i < Session.Players.Count(); i++) {
   4449 		if (event->ID == Session.Players[i]->Player.ID) {
   4450 			fprintf(fp,"Player's Name: %s",Session.Players[i]->Name);
   4451 		}
   4452 	}
   4453 	fprintf(fp,"\n");
   4454 
   4455 	fprintf(fp,"--------------------- My data: ---------------------\n");
   4456 	fprintf(fp,"My Frame:%d\n",Frame);
   4457 	fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead);
   4458 
   4459 	if (net) {
   4460 		fprintf(fp,"-------------------- Frame Stats: ------------------\n");
   4461 		fprintf(fp,"Name          ID  TheirFrame  TheirSent  TheirRecv\n");
   4462 		for (i = 0; i < net->Num_Connections(); i++) {
   4463 			house = (HousesType)(net->Connection_ID(i));
   4464 			fprintf(fp,"%12s  %2d    %6d      %6d      %6d\n",
   4465 				(HouseClass::As_Pointer(house))->IniName,
   4466 				net->Connection_ID(i),
   4467 				their_frame[i],
   4468 				their_sent[i],
   4469 				their_recv[i]);
   4470 		}
   4471 	}
   4472 
   4473 	fclose(fp);
   4474 }
   4475 
   4476 /***************************************************************************
   4477  * Check_Mirror -- Checks mirror memory                                    *
   4478  *                                                                         *
   4479  * INPUT:                                                                  *
   4480  *		none.																						*
   4481  *                                                                         *
   4482  * OUTPUT:                                                                 *
   4483  *		none.																						*
   4484  *                                                                         *
   4485  * WARNINGS:                                                               *
   4486  *		none.																						*
   4487  *                                                                         *
   4488  * HISTORY:                                                                *
   4489  *   10/14/1996 BRR : Created.                                             *
   4490  *=========================================================================*/
   4491 void Check_Mirror(void)
   4492 {
   4493 #ifdef MIRROR_QUEUE
   4494 	int i;
   4495 	char txt[80];
   4496 	unsigned long *ptr;
   4497 	int found_5s = 0;
   4498 
   4499 	ptr = (unsigned long *)(DoList.Get_Array());
   4500 	for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
   4501 		sizeof(unsigned long); i++) {
   4502 		if (ptr[i] == 0x55555555) {
   4503 			sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i]));
   4504 			WWMessageBox().Process (txt);
   4505 			found_5s = 1;
   4506 		}
   4507 	}
   4508 
   4509 	ptr = (unsigned long *)(MirrorList.Get_Array());
   4510 	for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
   4511 		sizeof(unsigned long); i++) {
   4512 		if (ptr[i] == 0x55555555) {
   4513 			sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i]));
   4514 			WWMessageBox().Process (txt);
   4515 			found_5s = 1;
   4516 		}
   4517 	}
   4518 
   4519 	ptr = (unsigned long *)(DoList.Get_Array());
   4520 	for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
   4521 		sizeof(unsigned long); i++) {
   4522 		if (ptr[i] == 0xAAAAAAAA) {
   4523 			sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i]));
   4524 			WWMessageBox().Process (txt);
   4525 			found_5s = 1;
   4526 		}
   4527 	}
   4528 
   4529 	ptr = (unsigned long *)(MirrorList.Get_Array());
   4530 	for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
   4531 		sizeof(unsigned long); i++) {
   4532 		if (ptr[i] == 0xAAAAAAAA) {
   4533 			sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i]));
   4534 			WWMessageBox().Process (txt);
   4535 			found_5s = 1;
   4536 		}
   4537 	}
   4538 
   4539 	for (i = 0; i < DoList.Count; i++) {
   4540 		if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) {
   4541 			sprintf(txt,"Queue Memory Trashed!  Head:%d Tail:%d, Addr:%p or %p",
   4542 				DoList.Get_Head(),
   4543 				DoList.Get_Tail(),
   4544 				DoList.Get_Array() + i,
   4545 				MirrorList.Get_Array() + i);
   4546 			WWMessageBox().Process (txt);
   4547 			Prog_End("Check_Mirror", true);
   4548 			Emergency_Exit(0);
   4549 		}
   4550 	}
   4551 
   4552 	if (found_5s) {
   4553 		//Prog_End();
   4554 		Emergency_Exit(0);
   4555 	}
   4556 
   4557 #endif
   4558 }	// end of Check_Mirror
   4559 
   4560 
   4561 /*************************** end of queue.cpp ******************************/