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 ******************************/