CnC_Remastered_Collection/TIBERIANDAWN/QUEUE.CPP
PG-SteveT 03416d24e1 Initial Source Code commit
Initial commit of original Tiberian Dawn and Red Alert source code converted to build as DLLs, and compatible with the release version of Command & Conquer Remastered.
2020-05-27 12:16:20 -07:00

4214 lines
174 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: F:\projects\c&c0\vcs\code\queue.cpv 2.24 11 Oct 1995 13:47:40 JOE_BOSTIC $ */
/***************************************************************************
* *
* Project Name : Command & Conquer *
* *
* File Name : QUEUE.CPP *
* *
* Programmer : Bill R. Randolph *
* *
* Start Date : 11/28/95 *
* *
* Last Update : November 28, 1995 [BRR] *
* *
*-------------------------------------------------------------------------*
* Functions for Queueing Events: *
* Queue_Mission -- Queue a mega mission event. *
* Queue_Options -- Queue the options event. *
* Queue_Exit -- Add the exit game event to the queue. *
* *
* Functions for processing Queued Events: *
* Queue_AI -- Process all queued events. *
* Queue_AI_Normal -- Process all queued events. *
* Queue_AI_Multiplayer -- Process all queued events. *
* *
* Main Multiplayer Queue Logic: *
* Wait_For_Players -- Waits for other systems to come on-line *
* Generate_Timing_Event -- computes & queues a RESPONSE_TIME event *
* Process_Send_Period -- timing for sending packets every 'n' frames *
* Send_Packets -- sends out events from the OutList *
* Send_FrameSync -- Sends a FRAMESYNC packet *
* Process_Receive_Packet -- processes an incoming packet *
* Process_Serial_Packet -- Handles an incoming serial packet *
* Can_Advance -- determines if it's OK to advance to the next frame *
* Process_Reconnect_Dialog -- processes the reconnection dialog *
* Handle_Timeout -- attempts to reconnect; if fails, bails. *
* Stop_Game -- stops the game *
* *
* Packet Compression / Decompression: *
* Build_Send_Packet -- Builds a big packet from a bunch of little ones. *
* Add_Uncompressed_Events -- adds uncompressed events to a packet *
* Add_Compressed_Events -- adds compressed events to a packet *
* Breakup_Receive_Packet -- Splits a big packet into little ones. *
* Extract_Uncompressed_Events -- extracts events from a packet *
* Extract_Compressed_Events -- extracts events from a packet *
* *
* DoList Management: *
* Execute_DoList -- Executes commands from the DoList *
* Clean_DoList -- Cleans out old events from the DoList *
* Queue_Record -- Records the DoList to disk *
* Queue_Playback -- plays back queue entries from a record file *
* *
* Debugging: *
* Compute_Game_CRC -- Computes a CRC value of the entire game. *
* Add_CRC -- Adds a value to a CRC *
* Print_CRCs -- Prints a data file for finding Sync Bugs *
* Init_Queue_Mono -- inits mono display *
* Update_Queue_Mono -- updates mono display *
* Print_Framesync_Values -- displays frame-sync variables *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "tcpip.h"
/********************************** Defines *********************************/
#define SHOW_MONO 1
int tmp_flag = 0;
/********************************** Globals *********************************/
//---------------------------------------------------------------------------
// GameCRC is the current computed CRC value for this frame.
// CRC[] is a record of our last 32 game CRC's.
// ColorNames is for debug output in Print_CRCs
//---------------------------------------------------------------------------
#ifndef DEMO
static unsigned long GameCRC;
static unsigned long CRC[32] =
{0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0};
static char *ColorNames[6] = {
"Yellow",
"Red",
"BlueGreen",
"Orange",
"Green",
"Blue",
};
#endif //DEMO
//...........................................................................
// Mono debugging variables:
// NetMonoMode: 0 = show connection output, 1 = flowcount output
// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen
// IsMono: used for taking control of Mono screen away from the engine
//...........................................................................
#ifndef DEMO
int NetMonoMode = 1;
int NewMonoMode = 1;
static int IsMono = 0;
#endif //DEMO
//---------------------------------------------------------------------------
// Several routines return various codes; here's an enum for all of them.
//---------------------------------------------------------------------------
typedef enum RetcodeEnum {
RC_NORMAL, // no news is good news
RC_PLAYER_READY, // a new player has been heard from
RC_SCENARIO_MISMATCH, // scenario mismatch
RC_DOLIST_FULL, // DoList is full
RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed
RC_PLAYER_LEFT, // modem: other player left the game
RC_HUNG_UP, // modem has hung up
RC_NOT_RESPONDING, // other player not responding (timeout/hung up)
RC_CANCEL, // user cancelled
} RetcodeType;
/********************************* Prototypes *******************************/
//...........................................................................
// Main multiplayer queue logic
//...........................................................................
static void Queue_AI_Normal(void);
#ifndef DEMO
static void Queue_AI_Multiplayer(void);
static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
int my_sent, long *their_frame, unsigned short *their_sent,
unsigned short *their_recv);
static void Generate_Timing_Event(ConnManClass *net, int my_sent);
static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent);
static void Generate_Process_Time_Event(ConnManClass *net);
static int Process_Send_Period(ConnManClass *net);
static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
int multi_packet_max, int max_ahead, int my_sent);
static void Send_FrameSync(ConnManClass *net, int cmd_count);
static RetcodeType Process_Receive_Packet(ConnManClass *net,
char *multi_packet_buf, int id, int packetlen, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv);
static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
int first_time);
static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv);
static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer,
long *their_frame, int num_conn, int reconn, int fresh);
static int Handle_Timeout(ConnManClass *net, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv);
static void Stop_Game(void);
#endif //DEMO
//...........................................................................
// Packet compression/decompression:
//...........................................................................
#ifndef DEMO
static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
int num_cmds, int cap);
static int Breakup_Receive_Packet(void *buf, int bufsize );
#endif //DEMO
int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size,
int cap);
int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size,
int cap);
int Extract_Uncompressed_Events(void *buf, int bufsize);
int Extract_Compressed_Events(void *buf, int bufsize);
//...........................................................................
// DoList management:
//...........................................................................
static int Execute_DoList(int max_houses, HousesType base_house,
ConnManClass *net, TCountDownTimerClass *skip_crc,
long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
static void Clean_DoList(ConnManClass *net);
#ifndef DEMO
static void Queue_Record(void);
static void Queue_Playback(void);
#endif //DEMO
//...........................................................................
// Debugging:
//...........................................................................
#ifndef DEMO
static void Compute_Game_CRC(void);
static void Init_Queue_Mono(ConnManClass *net);
static void Update_Queue_Mono(ConnManClass *net, int flow_index);
static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
int num_connections, unsigned short *their_recv,
unsigned short *their_sent, unsigned short my_sent);
#endif //DEMO
void Add_CRC(unsigned long *crc, unsigned long val);
void Print_CRCs(EventClass *);
extern void Keyboard_Process(KeyNumType &input);
void Dump_Packet_Too_Late_Stuff(EventClass *event);
extern void Register_Game_End_Time(void);
extern void Send_Statistics_Packet(void);
/***************************************************************************
* Queue_Mission -- Queue a mega mission event. *
* *
* This routine is called when the player causes a change to a game unit. *
* The event that initiates the change is queued to as a result of a call *
* to this routine. *
* *
* INPUT: *
* whom Whom this mission request applies to (a friendly unit). *
* mission The mission to assign to this object. *
* target The target of this mission (if any). *
* dest The movement destination for this mission (if any). *
* *
* OUTPUT: *
* Was the mission request queued successfully? *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 09/21/1995 JLB : Created. *
*=========================================================================*/
bool Queue_Mission(TARGET whom, MissionType mission, TARGET target,
TARGET destination)
{
if (! OutList.Add(EventClass(whom, mission, target, destination))) {
return(false);
}
else {
return(true);
}
} /* end of Queue_Mission */
/***************************************************************************
* Queue_Options -- Queue the options event. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* Was the options screen event queued successfully? *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 09/21/1995 JLB : Created. *
*=========================================================================*/
bool Queue_Options(void)
{
if (! OutList.Add(EventClass(EventClass::OPTIONS))) {
return(false);
}
else {
return(true);
}
} /* end of Queue_Options */
/***************************************************************************
* Queue_Exit -- Add the exit game event to the queue. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* Was the exit event queued successfully? *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 09/21/1995 JLB : Created. *
*=========================================================================*/
bool Queue_Exit(void)
{
if (! OutList.Add(EventClass(EventClass::EXIT))) {
return(false);
}
else {
return(true);
}
} /* end of Queue_Exit */
/***************************************************************************
* Queue_AI -- Process all queued events. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 09/21/1995 JLB : Created. *
*=========================================================================*/
void Queue_AI(void)
{
#ifdef DEMO
Queue_AI_Normal();
#else //DEMO
if (PlaybackGame) {
Queue_Playback();
}
else {
switch (GameToPlay) {
case GAME_NORMAL:
Queue_AI_Normal();
break;
case GAME_MODEM:
case GAME_NULL_MODEM:
case GAME_IPX:
case GAME_INTERNET:
Queue_AI_Multiplayer();
break;
}
}
#endif //DEMO
} /* end of Queue_AI */
/***************************************************************************
* Queue_AI_Normal -- Process all queued events. *
* *
* This is the "normal" version of the queue management routine. It does *
* the following: *
* - Transfers items in the OutList to the DoList *
* - Executes any commands in the DoList that are supposed to be done on *
* this frame # *
* - Cleans out the DoList *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 09/21/1995 JLB : Created. *
*=========================================================================*/
static void Queue_AI_Normal(void)
{
//------------------------------------------------------------------------
// Move events from the OutList (events generated by this player) into the
// DoList (the list of events to execute).
//------------------------------------------------------------------------
while (OutList.Count) {
OutList.First().IsExecuted = false;
if (!DoList.Add(OutList.First())) {
;
}
OutList.Next();
}
//------------------------------------------------------------------------
// Save the DoList to disk, if we're in "Record" mode
//------------------------------------------------------------------------
#ifndef DEMO
if (RecordGame) {
Queue_Record();
}
#endif //DEMO
//------------------------------------------------------------------------
// Execute the DoList
//------------------------------------------------------------------------
if (!Execute_DoList(1,PlayerPtr->Class->House, NULL, NULL, NULL,
NULL, NULL)) {
GameActive = 0;
return;
}
//------------------------------------------------------------------------
// Clean out the DoList
//------------------------------------------------------------------------
Clean_DoList(NULL);
} /* end of Queue_AI_Normal */
#ifndef DEMO
/***************************************************************************
* Queue_AI_Multiplayer -- Process all queued events. *
* *
* This is the network version of the queue management routine. It does *
* the following: *
* - If this is the 1st frame, waits for other systems to signal ready *
* - Generates a timing event, to allow the connection time to be dynamic *
* - Handles timing related to sending packets every 'n' frames *
* - Sends outgoing events *
* - Frame-syncs to the other systems (see below) *
* - Executes & cleans out the DoList *
* *
* The Frame-Sync'ing logic is the heart & soul of network play. It works *
* by ensuring that any system won't out-run the other system by more than *
* 'MaxAhead' frames; this in turn ensures that a packet's *
* execution frame # won't have been passed by the time that packet is *
* received by all systems. *
* *
* To achieve this, the system must keep track of all other system's *
* current frame #'s; these are stored in an array called 'their_frame[]'. *
* However, because current frame #'s are sent in FRAMEINFO packets, which *
* don't require an ACK, and command packets are sent in packets requiring *
* an ACK, it's possible for a command packet to get lost, and the next *
* frame's FRAMEINFO packet to not get lost; the other system may then *
* advance past the frame # the command is to execute on! So, to prevent *
* this, all FRAMEINFO packets include a CommandCount field. This value *
* tells the other system how many events it should have received by this *
* time. This system can therefore keep track of how many commands it's *
* actually received, and compare it to the CommandCount field, to see if *
* it's missed an event packet. The # of events we've received from each *
* system is stored in 'their_recv[]', and the # events they say they've *
* sent is stored in 'their_sent[]'. *
* *
* Thus, two conditions must be met in order to advance to the next frame: *
* - Our current frame # must be <= their_frame + MaxAhead *
* - their_recv[i] must be >= their_sent[i] *
* *
* 'their_frame[] is updated by Process_Receive_Packet() *
* 'their_recv[] is updated by Process_Receive_Packet() *
* 'their_sent[] is updated by Process_Receive_Packet() *
* 'my_sent' is updated by this routine. *
* *
* The only routines allowed to pop up dialogs are: *
* Wait_For_Players() (only pops up the reconnect dialog) *
* Execute_DoList() (tells if out of sync, or packet recv'd too late) *
* *
* Sign-off's are detected by: *
* - Timing out while waiting for a packet *
* - Detecting that the other player is now at the score screen or *
* connection dialog (serial) *
* - If we see an EventClass::EXIT event on the private channel *
* *
* The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has *
* the following properites: *
* - It compresses packets, so that the minimum number of bytes are *
* transmitted. Packets are compressed by extracting all info common to *
* the events into the packet header, and then sending only the bytes *
* relevant to each type of event. For instance, if 100 infantry guys *
* are told to move to the same location, the command itself & the *
* location will be included in the 1st movement command only; after *
* that, there will be a rep count then 99 infantry TARGET numbers, *
* identifying all the infantry told to move. *
* - The protocol also only sends packets out every 'n' frames. This cuts *
* the data rate dramatically. It means that 'MaxAhead' must be *
* divisible by 'n'; also, the minimum value for 'MaxAhead' is *
* 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO *
* packet gets missed. *
* *
* Note: For synchronization-waiting loops (like waiting to hear from all *
* other players, waiting to advance to the next frame, etc), use *
* Net.Num_Connections() rather than NumPlayers; this reflects the *
* actual # of connections, and can be "faked" into playing even when *
* there aren't any other players actually there. A typical example of *
* this is playing back a recorded game. For command-execution loops, use *
* NumPlayers. This ensures all commands get executed, even if *
* there isn't a human generating those commands. *
* *
* The modem works a little differently from the network in this respect: *
* - The connection has to stay "alive" even if the other player exits to *
* the join dialog. This prevents each system from timing out & hanging *
* the modem up. Thus, packets are sent back & forth & just thrown away,*
* but each system knows the other is still there. Messages may be sent *
* between systems, though. *
* - Destroy_Null_Connection doesn't hang up the modem, so *
* Num_Connections() still reports a value of 1 even though the other *
* player has left. *
* - Any waits on Num_Connections() must also check for *
* NumPlayers > 1, to keep from waiting forever if the other *
* guy has left *
* - Packets sent to a player who's left require no ACK *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Queue_AI_Multiplayer(void)
{
//........................................................................
// Enums:
//........................................................................
enum {
MIXFILE_RESEND_DELTA = 120, // ticks b/w resends
MIXFILE_TIMEOUT = 3600, // timeout waiting for mixfiles
FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog
FRAMESYNC_TIMEOUT = (25*60), // timeout waiting for frame sync packet
};
int timeout_factor = (GameToPlay == GAME_INTERNET) ? 6 : 1;
//........................................................................
// Variables for sending, receiving & parsing packets:
//........................................................................
ConnManClass *net; // ptr to access all multiplayer functions
EventClass packet; // for sending single frame-sync's
char *multi_packet_buf; // buffer for sending/receiving
int multi_packet_max; // max length of multi_packet_buf
//........................................................................
// Frame-sync'ing variables. Values in these arrays are stored in the
// order in which the connections are created.
// (ie net->Connection_Index(id))
//........................................................................
static long
their_frame[MAX_PLAYERS - 1]; // other players' frame #'s
static unsigned short
their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent
static unsigned short
their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others
static unsigned short
my_sent; // # cmds I've sent out
//........................................................................
// Other misc variables
//........................................................................
int i;
RetcodeType rc;
int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed
//------------------------------------------------------------------------
// Initialize the packet buffer pointer & its max size
//------------------------------------------------------------------------
if (GameToPlay == GAME_MODEM
|| GameToPlay == GAME_NULL_MODEM){
//PG_TO_FIX
#if (0)
multi_packet_buf = NullModem.BuildBuf;
multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType);
net = &NullModem;
#endif
}
else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
multi_packet_buf = MetaPacket;
multi_packet_max = MetaSize;
net = &Ipx;
}
//------------------------------------------------------------------------
// Debug stuff
//------------------------------------------------------------------------
Init_Queue_Mono(net);
Update_Queue_Mono (net, 0);
//------------------------------------------------------------------------
// If we've just started a game, or loaded a multiplayer game, we must
// wait for all other systems to signal ready.
//------------------------------------------------------------------------
if (Frame==0) {
//.....................................................................
// Initialize static locals
//.....................................................................
for (i = 0; i < MAX_PLAYERS - 1; i++) {
their_frame[i] = -1;
their_sent[i] = 0;
their_recv[i] = 0;
TheirProcessTime[i] = -1;
}
my_sent = 0;
for (i = 0; i < 32; i++) {
CRC[i] = 0;
}
//.....................................................................
// Send our initial FRAMESYNC packet
//.....................................................................
Send_FrameSync(net, my_sent);
//.....................................................................
// Wait for the other guys
//.....................................................................
rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor,
MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame,
their_sent, their_recv);
if (rc != RC_NORMAL) {
if (rc == RC_NOT_RESPONDING) {
CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
}
else if (rc == RC_SCENARIO_MISMATCH) {
CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
}
else if (rc == RC_DOLIST_FULL) {
CCMessageBox().Process(TXT_QUEUE_FULL);
}
Stop_Game();
return;
}
//.....................................................................
// Re-initialize frame numbers (in case somebody signed off while I was
// waiting for MIX files to load; we would have fallen through, but
// their frame # would still be -1).
//.....................................................................
for (i = 0; i < MAX_PLAYERS - 1; i++)
their_frame[i] = 0;
//.....................................................................
// Reset the network response time computation, now that we're both
// sending data again (loading MIX files will have introduced
// deceptively large values).
//.....................................................................
net->Reset_Response_Time();
//.....................................................................
// Initialize the frame timers
//.....................................................................
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
Process_Send_Period(net);
}
} // end of Frame 0 wait
//------------------------------------------------------------------------
// Adjust connection timing parameters every 128 frames.
//------------------------------------------------------------------------
else if ( (Frame & 0x007f) == 0) {
//
// If we're using the new spiffy protocol, do proper timing handling.
// If we're the net "master", compute our desired frame rate & new
// 'MaxAhead' value.
//
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
//
// All systems will transmit their required process time.
//
Generate_Process_Time_Event(net);
//
// The game "host" will transmit timing adjustment events.
//
if (MPlayerLocalID == MPlayerID[0]) {
Generate_Real_Timing_Event(net, my_sent);
}
} else {
//
// For the older protocols, do the old broken timing handling.
//
Generate_Timing_Event(net, my_sent);
}
}
//------------------------------------------------------------------------
// Compute the Game's CRC
//------------------------------------------------------------------------
Compute_Game_CRC();
CRC[Frame & 0x001f] = GameCRC;
//unsigned long save_crc = GameCRC;
//Print_CRCs((EventClass *)NULL);
//GameCRC = save_crc;
//------------------------------------------------------------------------
// Only process every 'FrameSendRate' frames
//------------------------------------------------------------------------
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
if (!Process_Send_Period(net)) {
if (IsMono) {
MonoClass::Disable();
}
return;
}
}
//------------------------------------------------------------------------
// Send our data packet(s); update my command-sent counter
//------------------------------------------------------------------------
my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max,
MPlayerMaxAhead, my_sent);
//------------------------------------------------------------------------
// If this is our first time through, we're done.
//------------------------------------------------------------------------
if (Frame==0) {
if (IsMono) {
MonoClass::Disable();
}
return;
}
//------------------------------------------------------------------------
// Frame-sync'ing: wait until it's OK to advance to the next frame.
//------------------------------------------------------------------------
rc = Wait_For_Players (0, net,
(MPlayerMaxAhead << 3),
MAX ( net->Response_Time() * 3, (unsigned long)FRAMESYNC_DLG_TIME*timeout_factor ),
FRAMESYNC_TIMEOUT * (timeout_factor*2),
multi_packet_buf, my_sent, their_frame,
their_sent, their_recv);
if (rc != RC_NORMAL) {
if (rc == RC_NOT_RESPONDING) {
CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
}
else if (rc == RC_SCENARIO_MISMATCH) {
CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
}
else if (rc == RC_DOLIST_FULL) {
CCMessageBox().Process(TXT_QUEUE_FULL);
}
Stop_Game();
return;
}
//------------------------------------------------------------------------
// Save the DoList to disk, if we're in "Record" mode
//------------------------------------------------------------------------
if (RecordGame) {
Queue_Record();
}
//------------------------------------------------------------------------
// Execute the DoList; if an error occurs, bail out.
//------------------------------------------------------------------------
if (!Execute_DoList(MPlayerMax, HOUSE_MULTI1, net, NULL,
their_frame, their_sent, their_recv)) {
Stop_Game();
return;
}
//------------------------------------------------------------------------
// Clean out the DoList
//------------------------------------------------------------------------
Clean_DoList(net);
if (IsMono) {
MonoClass::Disable();
}
} // end of Queue_AI_Multiplayer
/***************************************************************************
* Wait_For_Players -- Waits for other systems to come on-line *
* *
* This routine performs the most critical logic in multiplayer; that of *
* synchronizing my frame number with those of the other systems. *
* *
* INPUT: *
* first_time 1 = 1st time this routine is called *
* net ptr to connection manager *
* resend_delta time (ticks) between FRAMESYNC resends *
* dialog_time time (ticks) until pop up a reconnect dialog *
* timeout time (ticks) until we give up the ghost *
* multi_packet_buf buffer to store packets in *
* my_sent # commands I've sent so far *
* their_frame array of their frame #'s *
* their_sent array of their CommandCount values *
* their_recv array of # cmds I've received from them *
* *
* OUTPUT: *
* RC_NORMAL OK to advance to the next frame *
* RC_CANCEL user hit 'Cancel' at the timeout countdown dlg *
* RC_NOT_RESPONDING other player(s) not responding *
* RC_SCENARIO_MISMATCH scenario's don't match (first_time only) *
* RC_DOLIST_FULL DoList was full *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
int my_sent, long *their_frame, unsigned short *their_sent,
unsigned short *their_recv)
{
//........................................................................
// Variables for sending, receiving & parsing packets:
//........................................................................
EventClass *event; // event ptr for parsing incoming packets
int packetlen; // size of meta-packet sent, & received
int id; // id of other player
int messages_this_loop; // to limit # messages processed each loop
//........................................................................
// Variables used only if 'first_time':
//........................................................................
int num_ready; // # players signalling ready
//........................................................................
// Timing variables
//........................................................................
CountDownTimerClass retry_timer; // time between FRAMESYNC packet resends
CountDownTimerClass dialog_timer; // time to pop up a dialog
CountDownTimerClass timeout_timer; // general-purpose timeout
//........................................................................
// Dialog variables
//........................................................................
int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed
//........................................................................
// Other misc variables
//........................................................................
KeyNumType input; // for user input
int x,y; // for map input
RetcodeType rc;
//------------------------------------------------------------------------
// Wait to hear from all other players
//------------------------------------------------------------------------
num_ready = 0;
retry_timer.Set (resend_delta, true); // time to retry
dialog_timer.Set (dialog_time, true); // time to show dlg
timeout_timer.Set (timeout, true); // time to bail out
while (1) {
Update_Queue_Mono (net, 2);
//---------------------------------------------------------------------
// Resend a frame-sync packet if longer than one propogation delay goes
// by; this prevents a "deadlock". If he's waiting for me to advance,
// but has missed my last few FRAMEINFO packets, I may be waiting for
// him to advance. Resending a FRAMESYNC ensures he knows what frame
// number I'm on.
//---------------------------------------------------------------------
if (!retry_timer.Time()) {
retry_timer.Set (resend_delta, true); // time to retry
Update_Queue_Mono (net, 3);
Send_FrameSync(net, my_sent);
}
//---------------------------------------------------------------------
// Service the connections
//---------------------------------------------------------------------
net->Service();
//---------------------------------------------------------------------
// Pop up a reconnect dialog if enough time goes by
//---------------------------------------------------------------------
if (!dialog_timer.Time() && SpecialDialog==SDLG_NONE) {
if (Process_Reconnect_Dialog(&timeout_timer, their_frame,
net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) {
return (RC_CANCEL);
}
reconnect_dlg = 1;
}
//---------------------------------------------------------------------
// Exit if too much time goes by (the other system has crashed or
// bailed)
//---------------------------------------------------------------------
if (!timeout_timer.Time()) {
//..................................................................
// For the first-time run, just give up; something's wrong.
//..................................................................
if (first_time) {
return (RC_NOT_RESPONDING);
}
//..................................................................
// Otherwise, we're in the middle of a game; so, the modem &
// network must deal with a timeout differently.
//..................................................................
else {
Update_Queue_Mono (net, 4);
if (Handle_Timeout(net, their_frame, their_sent, their_recv)) {
Map.Flag_To_Redraw(true); // erase modem reconnect dialog
Map.Render();
retry_timer.Set (resend_delta, true);
dialog_timer.Set (dialog_time, true);
timeout_timer.Set (timeout, true);
}
else {
return (RC_NOT_RESPONDING);
}
}
}
//---------------------------------------------------------------------
// Check for an incoming message. We must still process commands
// even if 'first_time' is set, in case the other system got my 1st
// FRAMESYNC, but I didn't get his; he'll be at the next frame, and
// may be sending commands.
// We have to limit the number of incoming messages we handle; it's
// possible to go into an infinite loop processing modem messages.
//---------------------------------------------------------------------
messages_this_loop = 0;
while ( (messages_this_loop++ < 5) &&
net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) {
Update_Queue_Mono (net, 5);
/*..................................................................
Get an event ptr to the incoming message
..................................................................*/
event = (EventClass *)multi_packet_buf;
//------------------------------------------------------------------
// Special processing for a modem game: process SERIAL packets
//------------------------------------------------------------------
if (GameToPlay == GAME_MODEM
|| GameToPlay == GAME_NULL_MODEM){
//|| GameToPlay == GAME_INTERNET) {
rc = Process_Serial_Packet(multi_packet_buf, first_time);
//...............................................................
// SERIAL packet received & processed
//...............................................................
if (rc == RC_SERIAL_PROCESSED) {
net->Service();
retry_timer.Set (resend_delta, true);
dialog_timer.Set (dialog_time, true);
timeout_timer.Set (timeout, true);
continue;
}
//...............................................................
// other player has left the game
//...............................................................
else if (rc == RC_PLAYER_LEFT) {
if (first_time) {
num_ready++;
}
break;
}
//...............................................................
// Connection was lost
//...............................................................
else if (rc == RC_HUNG_UP) {
return (RC_NOT_RESPONDING);
}
//...............................................................
// If it was any other type of serial packet, break
//...............................................................
else if (rc != RC_NORMAL) {
break;
}
}
//------------------------------------------------------------------
// Process the incoming packet
//------------------------------------------------------------------
rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen,
their_frame, their_sent, their_recv);
//..................................................................
// New player heard from
//..................................................................
if (rc == RC_PLAYER_READY) {
num_ready++;
}
//..................................................................
// Scenario's don't match
//..................................................................
else if (rc == RC_SCENARIO_MISMATCH) {
return (RC_SCENARIO_MISMATCH);
}
//..................................................................
// DoList was full
//..................................................................
else if (rc == RC_DOLIST_FULL) {
return (RC_DOLIST_FULL);
}
//..................................................................
// Service the connection, to clean out the receive queues
//..................................................................
net->Service();
}
//---------------------------------------------------------------------
// Debug output
//---------------------------------------------------------------------
Print_Framesync_Values(Frame, MPlayerMaxAhead, net->Num_Connections(),
their_recv, their_sent, my_sent);
//---------------------------------------------------------------------
// Attempt to advance to the next frame.
//---------------------------------------------------------------------
//.....................................................................
// For the first-time run, just check to see if we've heard from
// everyone.
//.....................................................................
if (first_time) {
if (num_ready >= net->Num_Connections()) break;
}
//.....................................................................
// For in-game processing, we have to check their_sent, their_recv,
// their_frame, etc.
//.....................................................................
else {
if (Can_Advance(net, MPlayerMaxAhead, their_frame, their_sent,
their_recv)) {
break;
}
}
//---------------------------------------------------------------------
// Service game stuff. Servicing the map's input, and rendering the
// map, allows the map to scroll even though we're hung up waiting for
// packets. Don't do this if 'first_time' is set, since users could be
// waiting a very long time for all systems to load the scenario, and
// it gets frustrating being able to scroll around without doing
// anything.
//---------------------------------------------------------------------
Call_Back();
if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) {
WWMouse->Erase_Mouse(&HidPage, TRUE);
Map.Input(input, x, y);
if (input)
Keyboard_Process(input);
Map.Render();
}
} /* end of while */
//------------------------------------------------------------------------
// If the reconnect dialog was shown, force the map to redraw.
//------------------------------------------------------------------------
if (reconnect_dlg) {
Map.Flag_To_Redraw(true);
Map.Render();
}
return (RC_NORMAL);
} // end of Wait_For_Players
/***************************************************************************
* Generate_Timing_Event -- computes & queues a RESPONSE_TIME event *
* *
* This routine adjusts the connection timing on the local system; it also *
* optionally generates a RESPONSE_TIME event, to tell all systems to *
* dynamically adjust the current MaxAhead value. This allows both the *
* MaxAhead & the connection retry logic to have dynamic timing, to adjust *
* to varying line conditions. *
* *
* INPUT: *
* net ptr to connection manager *
* my_sent # commands I've sent out so far *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Generate_Timing_Event(ConnManClass *net, int my_sent)
{
unsigned long resp_time; // connection response time, in ticks
EventClass ev;
//------------------------------------------------------------------------
// Measure the current connection response time. This time will be in
// 60ths of a second, and represents full round-trip time of a packet.
// To convert to one-way packet time, divide by 2; to convert to game
// frames, divide again by 4, assuming a game rate of 15 fps.
//------------------------------------------------------------------------
resp_time = net->Response_Time();
//------------------------------------------------------------------------
// Adjust my connection retry timing; only do this if I've sent out more
// than 5 commands, so I know I have a measure of the response time.
//------------------------------------------------------------------------
if (my_sent > 5) {
net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
//.....................................................................
// If I'm the network "master", I'm also responsible for updating the
// MaxAhead value on all systems, so do that here too.
//.....................................................................
if (MPlayerLocalID == MPlayerID[0]) {
ev.Type = EventClass::RESPONSE_TIME;
//..................................................................
// For multi-frame compressed events, the MaxAhead must be an even
// multiple of the FrameSendRate.
//..................................................................
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
ev.Data.FrameInfo.Delay = MAX( ((((resp_time / 8) +
(FrameSendRate - 1)) / FrameSendRate) *
FrameSendRate), (FrameSendRate * 2) );
char flip[128];
sprintf (flip, "C&C95 - Generating timing packet - MaxAhead = %d frames\n", ev.Data.FrameInfo.Delay);
CCDebugString (flip);
}
//..................................................................
// For sending packets every frame, just use the 1-way connection
// response time.
//..................................................................
else {
if (GameToPlay == GAME_MODEM
|| GameToPlay == GAME_NULL_MODEM){
//|| GameToPlay == GAME_INTERNET) {
ev.Data.FrameInfo.Delay = MAX( (resp_time / 8),
(unsigned long)MODEM_MIN_MAX_AHEAD );
}
else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
ev.Data.FrameInfo.Delay = MAX( (resp_time / 8),
(unsigned long)NETWORK_MIN_MAX_AHEAD );
}
}
OutList.Add(ev);
}
}
} // end of Generate_Timing_Event
/***************************************************************************
* Generate_Real_Timing_Event -- Generates a TIMING event *
* *
* INPUT: *
* net ptr to connection manager *
* my_sent # commands I've sent out so far *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 07/02/1996 BRR : Created. *
*=========================================================================*/
static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent)
{
unsigned long resp_time; // connection response time, in ticks
EventClass ev;
int highest_ticks;
int i;
int specified_frame_rate;
int maxahead;
//
// If we haven't sent out at least 5 guaranteed-delivery packets, don't
// bother trying to measure our connection response time; just return.
//
if (my_sent < 5) {
return;
}
//
// Find the highest processing time we have stored
//
highest_ticks = 0;
for (i = 0; i < MPlayerCount; i++) {
//
// If we haven't heard from all systems yet, bail out.
//
if (TheirProcessTime[i] == -1) {
return;
}
if (TheirProcessTime[i] > highest_ticks) {
highest_ticks = TheirProcessTime[i];
}
}
//
// Compute our "desired" frame rate as the lower of:
// - What the user has dialed into the options screen
// - What we're really able to run at
//
if (highest_ticks == 0) {
DesiredFrameRate = 60;
} else {
DesiredFrameRate = 60 / highest_ticks;
}
if (Options.GameSpeed == 0) {
specified_frame_rate = 60;
} else {
specified_frame_rate = 60 / Options.GameSpeed;
}
DesiredFrameRate = MIN (DesiredFrameRate, specified_frame_rate);
//
// Measure the current connection response time. This time will be in
// 60ths of a second, and represents full round-trip time of a packet.
// To convert to one-way packet time, divide by 2; to convert to game
// frames, ....uh....
//
resp_time = net->Response_Time();
//
// Compute our new 'MaxAhead' value, based upon the response time of our
// connection and our desired frame rate.
// 'MaxAhead' in frames is:
//
// (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec)
//
// resp_time is divided by 2 because, as reported, it represents a round-
// trip, and we only want to use a one-way trip.
//
maxahead = (resp_time * DesiredFrameRate) / (2 * 60);
//
// Now, we have to round 'maxahead' so it's an even multiple of our
// send rate. It also must be at least thrice the FrameSendRate.
// (Isn't "thrice" a cool word?)
//
maxahead = ((maxahead + FrameSendRate - 1) / FrameSendRate) * FrameSendRate;
maxahead = MAX (maxahead, (int)FrameSendRate * 3);
ev.Type = EventClass::TIMING;
ev.Data.Timing.DesiredFrameRate = DesiredFrameRate;
ev.Data.Timing.MaxAhead = maxahead;
OutList.Add(ev);
//
// Adjust my connection retry timing. These values set the retry timeout
// to just over one round-trip time, the 'maxretries' to -1, and the
// connection timeout to allow for about 4 retries.
//
net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
}
/***************************************************************************
* Generate_Process_Time_Event -- Generates a PROCESS_TIME event *
* *
* INPUT: *
* net ptr to connection manager *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 07/02/1996 BRR : Created. *
*=========================================================================*/
static void Generate_Process_Time_Event(ConnManClass *net)
{
EventClass ev;
int avgticks;
unsigned long resp_time; // connection response time, in ticks
//
// Measure the current connection response time. This time will be in
// 60ths of a second, and represents full round-trip time of a packet.
// To convert to one-way packet time, divide by 2; to convert to game
// frames, ....uh....
//
resp_time = net->Response_Time();
//
// Adjust my connection retry timing. These values set the retry timeout
// to just over one round-trip time, the 'maxretries' to -1, and the
// connection timeout to allow for about 4 retries.
//
net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
if (IsMono) {
MonoClass::Enable();
Mono_Set_Cursor(0,23);
Mono_Printf("Processing Ticks:%03d Frames:%03d\n", ProcessTicks,ProcessFrames);
MonoClass::Disable();
}
avgticks = ProcessTicks / ProcessFrames;
ev.Type = EventClass::PROCESS_TIME;
ev.Data.ProcessTime.AverageTicks = avgticks;
char flip[128];
sprintf (flip, "C&C95 - Sending PROCESS_TIME packet of %04x ticks\n", ev.Data.ProcessTime.AverageTicks);
CCDebugString (flip);
OutList.Add(ev);
ProcessTicks = 0;
ProcessFrames = 0;
}
/***************************************************************************
* Process_Send_Period -- timing for sending packets every 'n' frames *
* *
* This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. *
* It determines if it's time to send a packet or not. *
* *
* INPUT: *
* net ptr to connection manager *
* *
* OUTPUT: *
* 1 = it's time to send a packet; 0 = don't send a packet this frame. *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Process_Send_Period(ConnManClass *net)
{
//------------------------------------------------------------------------
// If the current frame # is not an even multiple of 'FrameSendRate', then
// it's not time to send a packet; just return.
//------------------------------------------------------------------------
if (Frame != (((Frame + (FrameSendRate - 1)) /
FrameSendRate) * FrameSendRate) ) {
net->Service();
if (IsMono) {
MonoClass::Disable();
}
return (0);
}
return (1);
} // end of Process_Send_Period
/***************************************************************************
* Send_Packets -- sends out events from the OutList *
* *
* This routine computes how many events can be sent this frame, and then *
* builds the "meta-packet" & sends it. *
* *
* The 'cap' value is the max # of events we can send. Ideally, it should *
* be based upon the bandwidth of our connection. Currently, it's just *
* hardcoded to prevent the modem from having to resend "too much" data, *
* which is about 200 bytes per frame. *
* *
* INPUT: *
* net ptr to connection manager *
* multi_packet_buf buffer to store packets in *
* multi_packet_max max size of multi_packet_buf *
* max_ahead current game MaxAhead value *
* my_sent # commands I've sent this game *
* *
* OUTPUT: *
* # events sent, NOT including the FRAMEINFO event *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
int multi_packet_max, int max_ahead, int my_sent)
{
int cap; // max # events to send, NOT including FRAMEINFO event
int do_once; // true: only go through packet loop once
int ack_req; // 0 = no ack required on outgoing packet
int packetlen; // size of meta-packet sent
//------------------------------------------------------------------------
// Determine how many events it's OK to send this frame.
//------------------------------------------------------------------------
//........................................................................
// If we have 4 or more packets queue'd for sending, don't add any more
// this frame.
//........................................................................
if (net->Private_Num_Send() >= 4) {
cap = 0;
do_once = 1;
}
//........................................................................
// If there are 2 or more packets queued, the entire packet we send must
// fit within a single ComQueue buffer, so limit # events to 5.
// (The Modem connection manager has a max buffer size of 200 bytes, which
// is large enough for 6 uncompressed events, which leaves room for 5
// events plus a FRAMEINFO.)
//........................................................................
else if (net->Private_Num_Send() >= 2) {
cap = 5;
do_once = 1;
}
//........................................................................
// Otherwise, just send all events in the OutList
//........................................................................
else {
cap = OutList.Count;
do_once = 0;
}
//........................................................................
// Make sure we aren't sending more events than are in the OutList
//........................................................................
if (cap > OutList.Count) {
cap = OutList.Count;
}
//........................................................................
// Make sure we don't send so many events that our DoList fills up
//........................................................................
if (cap > (MAX_EVENTS * 8) - DoList.Count) {
cap = (MAX_EVENTS * 8) - DoList.Count;
}
/*
** No cap for internet game
**
** Or for serial games for that matter ST - 5/31/96 4:00PM
*/
if (GameToPlay == GAME_INTERNET || GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM){
cap = OutList.Count;
do_once = 0;
}
//------------------------------------------------------------------------
// Build our meta-packet & transmit it.
//------------------------------------------------------------------------
while (1) {
Update_Queue_Mono (net, 1);
//.....................................................................
// If there are no commands this frame, we'll just be sending a FRAMEINFO
// packet; no ack is required. For the modem's sake, check
// MPlayerCount; no ACK is needed if we're just sending to someone
// who's left the game.
//.....................................................................
if (cap == 0 || OutList.Count == 0 || MPlayerCount == 1) {
ack_req = 0;
}
else {
ack_req = 1;
}
//.....................................................................
// Build & send out our message
//.....................................................................
packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max,
max_ahead, my_sent, cap);
net->Send_Private_Message (multi_packet_buf, packetlen, ack_req);
//.....................................................................
// Call Service() to actually send the packet
//.....................................................................
net->Service();
//.....................................................................
// Stop if there's no more data to send, or if our send queue is
// filling up.
//.....................................................................
if (OutList.Count == 0 || do_once) {
break;
}
}
return (cap);
} // end of Send_Packets
/***************************************************************************
* Send_FrameSync -- Sends a FRAMESYNC packet *
* *
* This routine is used to periodically remind the other systems that *
* we're still here, and to tell them what frame # we're on, in case *
* they've missed my FRAMEINFO packets. *
* *
* INPUT: *
* net ptr to connection manager *
* cmd_count # commands I've sent so far *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Send_FrameSync(ConnManClass *net, int cmd_count)
{
EventClass packet;
//------------------------------------------------------------------------
// Build a frame-sync event to send. FRAMESYNC packets contain a
// scenario-based CRC rather than a game-state-based CRC, to let the
// games compare scenario CRC's on startup.
//------------------------------------------------------------------------
memset (&packet, 0, sizeof(EventClass));
packet.Type = EventClass::FRAMESYNC;
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
packet.Frame = ((Frame + MPlayerMaxAhead + (FrameSendRate - 1)) /
FrameSendRate) * FrameSendRate;
}
else {
packet.Frame = Frame + MPlayerMaxAhead;
}
packet.ID = Houses.ID(PlayerPtr);
packet.MPlayerID = MPlayerLocalID;
packet.Data.FrameInfo.CRC = ScenarioCRC;
packet.Data.FrameInfo.CommandCount = cmd_count;
packet.Data.FrameInfo.Delay = MPlayerMaxAhead;
//------------------------------------------------------------------------
// Send the event. For modem, this just sends to the other player;
// for network, it sends to everyone we're connected to.
//------------------------------------------------------------------------
net->Send_Private_Message (&packet, (offsetof(EventClass, Data) +
size_of(EventClass, Data.FrameInfo)), 0 );
return;
} // end of Send_FrameSync
/***************************************************************************
* Process_Receive_Packet -- processes an incoming packet *
* *
* This routine receives a packet from another system, adds it to our *
* execution queue (the DoList), and updates my arrays of their frame #, *
* their commands-sent, and their commands-received. *
* *
* INPUT: *
* net ptr to connection manager *
* multi_packet_buf buffer containing packet(s) to parse *
* id id of sender *
* their_frame array containing frame #'s of other players *
* their_sent array containing command count of other players *
* their_recv array containing # recv'd cmds from other players *
* *
* OUTPUT: *
* RC_NORMAL: nothing unusual happened, although *
* their_sent or their_recv may have been *
* altered *
* RC_PLAYER_READY: player has been heard from for the 1st time; *
* this presumes that his original *
* 'their_frame[]' value was -1 when this *
* routine was called *
* RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; *
* normally only applies after loading a new *
* scenario or save-game *
* RC_DOLIST_FULL: fatal error; unable to add events to DoList *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static RetcodeType Process_Receive_Packet(ConnManClass *net,
char *multi_packet_buf, int id, int packetlen, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv)
{
EventClass *event;
int index;
RetcodeType retcode = RC_NORMAL;
int i;
//------------------------------------------------------------------------
// Get an event ptr to the incoming message
//------------------------------------------------------------------------
event = (EventClass *)multi_packet_buf;
//------------------------------------------------------------------------
// Get the index of the sender
//------------------------------------------------------------------------
index = net->Connection_Index(id);
//------------------------------------------------------------------------
// Compute the other player's frame # (at the time this packet was sent)
//------------------------------------------------------------------------
if (their_frame[index] <
(int)(event->Frame - event->Data.FrameInfo.Delay)) {
//.....................................................................
// If the original frame # for this player is -1, it means we've heard
// from this player for the 1st time; return the appropriate value.
//.....................................................................
if (their_frame[index]==-1) {
retcode = RC_PLAYER_READY;
}
their_frame[index] = event->Frame - event->Data.FrameInfo.Delay;
}
//------------------------------------------------------------------------
// Extract the other player's CommandCount. This count will include
// the commands in this packet, if there are any.
//------------------------------------------------------------------------
if (event->Data.FrameInfo.CommandCount > their_sent[index]) {
their_sent[index] = event->Data.FrameInfo.CommandCount;
}
//------------------------------------------------------------------------
// If this packet was not a FRAMESYNC packet:
// - Add the events in it to our DoList
// - Increment our commands-received counter by the number of non-
// FRAMEINFO packets received
//------------------------------------------------------------------------
if (event->Type != EventClass::FRAMESYNC) {
//.....................................................................
// Break up the packet into its component events. A returned packet
// count of -1 indicates a fatal queue-full error.
//.....................................................................
i = Breakup_Receive_Packet( multi_packet_buf, packetlen);
if (i==-1) {
return (RC_DOLIST_FULL);
}
//.....................................................................
// Compute the actual # commands in the packet by subtracting off the
// FRAMEINFO event
//.....................................................................
if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) {
i--;
}
their_recv[index] += i;
}
//------------------------------------------------------------------------
// If the event was a FRAMESYNC packet, there will be no commands to add,
// but we must check the ScenarioCRC value.
//------------------------------------------------------------------------
else if (event->Data.FrameInfo.CRC != ScenarioCRC) {
return (RC_SCENARIO_MISMATCH);
}
return (retcode);
} // end of Process_Receive_Packet
/***************************************************************************
* Process_Serial_Packet -- Handles an incoming serial packet *
* *
* This routine is needed because the modem classes don't support a *
* "global channel" like the network classes do, but that functionality is *
* still needed for modem communications. Specifically, the modem dialogs *
* transmit their own special packets back & forth, and messages are sent *
* using a special packet type. Thus, we have to call this routine when *
* we receive a modem packet, to allow it to process messages & dialog *
* packets. *
* *
* INPUT: *
* multi_packet_buf packet buffer to process *
* first_time 1 = this is the 1st game frame *
* *
* OUTPUT: *
* RC_NORMAL: this wasn't a SERIAL-type packet *
* RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was *
* processed; the other player is still connected, *
* even if he's not in the game. *
* RC_PLAYER_LEFT: other player has left the game *
* RC_HUNG_UP: we're getting our own packets back; thus, the *
* modem is mirroring our packets, which means the *
* modem hung up! *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
int first_time)
{
multi_packet_buf;
first_time;
return (RC_NORMAL);
#if (0) // ST - 1/2/2019 5:28PM
SerialPacketType *serial_packet; // for parsing serial packets
int player_gone;
EventClass *event;
char txt[MAX_MESSAGE_LENGTH+80];
unsigned short magic_number;
unsigned short crc;
//------------------------------------------------------------------------
// Determine if this packet means that the other player has left the game
//------------------------------------------------------------------------
serial_packet = (SerialPacketType *)multi_packet_buf;
player_gone = 0;
//........................................................................
// On Frame 0, only a SIGN_OFF means the other player left; the other
// packet types may be left over from a previous session.
//........................................................................
if (first_time) {
if (serial_packet->Command == SERIAL_SIGN_OFF) {
player_gone = 1;
}
}
//........................................................................
// On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means
// the other player is gone.
//........................................................................
else {
if (serial_packet->Command == SERIAL_SIGN_OFF ||
serial_packet->Command == SERIAL_TIMING ||
serial_packet->Command == SERIAL_SCORE_SCREEN ) {
player_gone = 1;
}
}
if (player_gone) {
Destroy_Null_Connection(serial_packet->Color, 0);
return (RC_PLAYER_LEFT);
}
//------------------------------------------------------------------------
// Process an incoming message
//------------------------------------------------------------------------
if (serial_packet->Command == SERIAL_MESSAGE) {
sprintf(txt, Text_String(TXT_FROM), serial_packet->Name,
serial_packet->Message);
magic_number = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4));
crc = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2));
Messages.Add_Message (txt,
MPlayerTColors[MPlayerID_To_ColorIndex(serial_packet->ID)],
TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc);
//.....................................................................
// Save this message in our last-message buffer
//.....................................................................
if (strlen (serial_packet->Message)) {
strcpy (LastMessage, serial_packet->Message);
}
//.....................................................................
// Tell the map to do a partial update (just to force the
// messages to redraw).
//.....................................................................
Map.Flag_To_Redraw(false);
return (RC_SERIAL_PROCESSED);
}
//------------------------------------------------------------------------
// Any other SERIAL-type packet means the other player is still there;
// throw them away, but let the caller know the connection is OK.
//------------------------------------------------------------------------
if ( (serial_packet->Command >= SERIAL_CONNECT &&
serial_packet->Command < SERIAL_LAST_COMMAND) ||
MPlayerCount == 1) {
return (RC_SERIAL_PROCESSED);
}
//........................................................................
// are we getting our own packets back??
//........................................................................
event = (EventClass *)multi_packet_buf;
if (event->ID == Houses.ID(PlayerPtr)) {
return (RC_HUNG_UP);
}
return (RC_NORMAL);
#endif
} // end of Process_Serial_Packet
/***************************************************************************
* Can_Advance -- determines if it's OK to advance to the next frame *
* *
* This routine uses the current values stored in their_frame[], *
* their_send[], and their_recv[] to see if it's OK to advance to the next *
* game frame. We must not advance if: *
* - If our frame # would be too far ahead of the slowest player (the *
* lowest their_frame[] value). "Too far" means *
* (Frame > their_frame + MaxAhead). *
* - our current command count doesn't match the sent command count of one *
* other player (meaning that we've missed a command packet from that *
* player, and thus the frame # we're receiving from him may be due to a *
* FRAMEINFO packet sent later than the command, so we shouldn't use *
* this frame # to see if we should advance; we should wait until we *
* have all the commands before we advance. *
* *
* Of course, this routine assumes the values in their_frame[] etc are *
* kept current by the caller. *
* *
* INPUT: *
* net ptr to connection manager *
* max_ahead max frames ahead *
* their_frame array of their frame #'s *
* their_sent array of their sent command count *
* their_recv array of their # received commands *
* *
* OUTPUT: *
* 1 = OK to advance; 0 = not OK *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv)
{
long their_oldest_frame; // other players' oldest frame #
int count_ok; // true = my cmd count matches theirs
int i;
//------------------------------------------------------------------------
// Special case for modem: if the other player has left, go ahead and
// advance to the next frame; don't wait on him.
//------------------------------------------------------------------------
if (MPlayerCount == 1) {
return (1);
}
//------------------------------------------------------------------------
// Find the oldest frame # in 'their_frame'
//------------------------------------------------------------------------
their_oldest_frame = Frame + 1000;
for (i = 0; i < net->Num_Connections(); i++) {
if (their_frame[i] < their_oldest_frame)
their_oldest_frame = their_frame[i];
}
//------------------------------------------------------------------------
// I can advance to the next frame IF:
// 1) I'm less than a one-way propogation delay ahead of the other
// players' frame numbers, AND
// 2) their_recv[i] >= their_sent[i] (ie I've received all the commands
// the other players have sent so far).
//------------------------------------------------------------------------
count_ok = 1;
for (i = 0; i < net->Num_Connections(); i++) {
if (their_recv[i] < their_sent[i]) {
count_ok = 0;
break;
}
}
if (count_ok && (Frame < (their_oldest_frame + max_ahead))) {
return (1);
}
return (0);
} // end of Can_Advance
/***************************************************************************
* Process_Reconnect_Dialog -- processes the reconnection dialog *
* *
* This routine [re]draws the reconnection dialog; if 'reconn' is set, *
* it tells the user who we're trying to reconnect to; otherwise, is just *
* says something generic like "Waiting for connections". *
* *
* INPUT: *
* timeout_timer ptr to count down timer, showing time remaining *
* their_frame array of other players' frame #'s *
* num_conn # connections in 'their_frame' *
* reconn 1 = reconnect, 0 = waiting for first-time connection *
* fresh 1 = draw from scratch, 0 = only update time counter *
* *
* OUTPUT: *
* 1 = user wants to cancel, 0 = not *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer,
long *their_frame, int num_conn, int reconn, int fresh)
{
static int displayed_time = 0; // time value currently displayed
int new_time;
int oldest_index; // index of person requiring a reconnect
int i,j;
//------------------------------------------------------------------------
// Convert the timer to seconds
//------------------------------------------------------------------------
new_time = timeout_timer->Time() / 60;
//--------------------------------------------------------------------------------
// If we have just received input focus again after running in the background then
// we need to redraw the whole dialog.
//--------------------------------------------------------------------------------
if (AllSurfaces.SurfacesRestored){
AllSurfaces.SurfacesRestored=FALSE;
fresh = true;
}
//------------------------------------------------------------------------
// If the timer has changed, or 'fresh' is set, redraw the dialog
//------------------------------------------------------------------------
if (fresh || (new_time != displayed_time)) {
//.....................................................................
// Find the index of the person we're trying to reconnect to
//.....................................................................
if (reconn) {
j = 0x7fffffff;
oldest_index = 0;
for (i = 0; i < num_conn; i++) {
if (their_frame[i] < j) {
j = their_frame[i];
oldest_index = i;
}
}
}
Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time);
}
displayed_time = new_time;
//........................................................................
// If user hits ESC, bail out
//........................................................................
if (Check_Key()) {
if (Get_Key_Num()==KN_ESC) {
return (1);
}
}
return (0);
} // end of Process_Reconnect_Dialog
/***************************************************************************
* Handle_Timeout -- handles a timeout in the wait-for-players loop *
* *
* This routine "gracefully" handles a timeout in the frame-sync loop. *
* The timeout must be handled differently by a modem game or network *
* game. *
* *
* The modem game must detect if the other player is still connected *
* physically, even if he's not playing the game any more; if so, this *
* routine returns an OK status. If the other player isn't even *
* physically connected, an error is returned. *
* *
* The network game must find the connection that's causing the timeout, *
* and destroy it. The game continues, even if there are no more human *
* players left. *
* *
* INPUT: *
* net ptr to connection manager *
* their_frame array containing frame #'s of other players *
* their_sent array containing command count of other players *
* their_recv array containing # recv'd cmds from other players *
* *
* OUTPUT: *
* 1 = it's OK; reset timeout timers & keep processing *
* 0 = game over, man *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Handle_Timeout(ConnManClass *net, long *their_frame,
unsigned short *their_sent, unsigned short *their_recv)
{
int oldest_index; // index of person requiring a reconnect
int i,j;
int id;
//------------------------------------------------------------------------
// For modem, attempt to reconnect; if that fails, save the game & bail.
//------------------------------------------------------------------------
if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) {
#if (0) //ST - 1/2/2019 5:28PM
if ( net->Num_Connections() ) {
if (!Reconnect_Modem()) {
return (0);
}
else {
return (1);
}
}
#endif
}
//------------------------------------------------------------------------
// For network, destroy the oldest connection
//------------------------------------------------------------------------
else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
j = 0x7fffffff;
oldest_index = 0;
for (i = 0; i < net->Num_Connections(); i++) {
if (their_frame[i] < j) {
j = their_frame[i];
oldest_index = i;
}
}
id = net->Connection_ID(oldest_index);
/*
** Send the game statistics packet now if the game is effectivly over
*/
if (MPlayerCount == 2 &&
GameToPlay == GAME_INTERNET &&
!GameStatisticsPacketSent){
Register_Game_End_Time();
ConnectionLost = true;
Send_Statistics_Packet();
}
if (id != ConnManClass::CONNECTION_NONE) {
for (i = oldest_index; i < net->Num_Connections() - 1; i++) {
their_frame[i] = their_frame[i+1];
their_sent[i] = their_sent[i+1];
their_recv[i] = their_recv[i+1];
}
CCDebugString ("C&C95 = Destroying connection due to time out\n");
Destroy_Connection(id,1);
}
}
return (1);
} // end of Handle_Timeout
/***************************************************************************
* Stop_Game -- stops the game *
* *
* This routine clears any global flags that need it, in preparation for *
* halting the game prematurely. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/22/1995 BRR : Created. *
*=========================================================================*/
static void Stop_Game(void)
{
CCDebugString ("C&C95 - In Stop_Game.\n");
GameActive = 0;
if (IsMono) {
MonoClass::Disable();
}
if (GameToPlay == GAME_INTERNET){
ConnectionLost = true;
CCDebugString ("C&C95 - About to send statistics packet.\n");
Register_Game_End_Time();
Send_Statistics_Packet();
CCDebugString ("C&C95 - Returned from sending stats packet.\n");
}
return;
} // end of Stop_Game
/***************************************************************************
* Build_Send_Packet -- Builds a big packet from a bunch of little ones. *
* *
* This routine takes events from the OutList, and puts them into a *
* "meta-packet", which is transmitted to all systems we're connected to. *
* Also, these events are added to our own DoList. *
* *
* Every Meta-Packet we send uses a FRAMEINFO packet as a header; this *
* tells the other systems what frame we're on, as well as serving as a *
* standard packet header. *
* *
* INPUT: *
* buf buffer to store packet in *
* bufsize max size of buffer *
* frame_delay desired frame delay to attach to all outgoing packets *
* num_cmds value to use for the CommandCount field *
* cap max # events to send *
* *
* OUTPUT: *
* new size of packet *
* *
* WARNINGS: *
* 'num_cmds' should be the total of of commands, including all those sent *
* this frame! *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
int num_cmds, int cap)
{
int size = 0;
EventClass *finfo;
//------------------------------------------------------------------------
// All events start with a FRAMEINFO event; fill this part in.
//------------------------------------------------------------------------
//........................................................................
// Set the event type
//........................................................................
finfo = (EventClass *)buf;
finfo->Type = EventClass::FRAMEINFO;
//........................................................................
// Set the frame to execute this event on; this is protocol-specific
//........................................................................
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
finfo->Frame = ((Frame + frame_delay + (FrameSendRate - 1)) /
FrameSendRate) * FrameSendRate;
}
else {
finfo->Frame = Frame + frame_delay;
}
//........................................................................
// Fill in the rest of the event
//........................................................................
finfo->ID = Houses.ID(PlayerPtr);
finfo->MPlayerID = MPlayerLocalID;
finfo->Data.FrameInfo.CRC = GameCRC;
finfo->Data.FrameInfo.CommandCount = num_cmds;
finfo->Data.FrameInfo.Delay = frame_delay;
//------------------------------------------------------------------------
// Initialize the # of bytes processed; this is protocol-specific
//------------------------------------------------------------------------
if (CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) {
size += sizeof(EventClass);
}
else {
size += (offsetof(EventClass, Data) +
size_of(EventClass, Data.FrameInfo));
}
//------------------------------------------------------------------------
// Transfer all events from the OutList into the DoList, building our
// packet while we go.
//------------------------------------------------------------------------
switch (CommProtocol) {
//.....................................................................
// COMM_PROTOCOL_SINGLE_NO_COMP:
// We'll send at least a FRAMEINFO every single frame, no compression
//.....................................................................
case (COMM_PROTOCOL_SINGLE_NO_COMP):
size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap);
break;
//.....................................................................
// COMM_PROTOCOL_SINGLE_E_COMP:
// Compress a group of packets into our send buffer; send out
// compressed packets every frame.
// COMM_PROTOCOL_MULTI_E_COMP:
// Compress a group of packets into our send buffer; send out
// compressed packets every 'n' frames.
//.....................................................................
case (COMM_PROTOCOL_SINGLE_E_COMP):
case (COMM_PROTOCOL_MULTI_E_COMP):
size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap);
break;
//.....................................................................
// Default: We have no idea what to do, so do nothing.
//.....................................................................
default:
size = 0;
break;
}
return( size );
} /* end of Build_Send_Packet */
/***************************************************************************
* Add_Uncompressed_Events -- adds uncompressed events to a packet *
* *
* INPUT: *
* buf buffer to store packet in *
* bufsize max size of buffer *
* frame_delay desired frame delay to attach to all outgoing packets *
* size current packet size *
* cap max # events to process *
* *
* OUTPUT: *
* new size value *
* *
* WARNINGS: *
* This routine MUST check to be sure it doesn't overflow the buffer. *
* *
* HISTORY: *
* 11/21/1995 DRD : Created. *
*=========================================================================*/
static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay,
int size, int cap)
{
int num = 0; // # of events processed
int ev_size; // size of event we're adding
//------------------------------------------------------------------------
// Loop until there are no more events, or we've processed our max # of
// events, or the buffer is full.
//------------------------------------------------------------------------
while (OutList.Count && (num < cap)) {
ev_size = sizeof(EventClass);
//.....................................................................
// Will the next event exceed the size of the buffer? If so, break.
//.....................................................................
if ( (size + ev_size) > bufsize ) {
return (size);
}
//.....................................................................
// Set the event's frame delay
//.....................................................................
OutList.First().Frame = Frame + frame_delay;
//.....................................................................
// Set the event's ID
//.....................................................................
OutList.First().ID = Houses.ID(PlayerPtr);
OutList.First().MPlayerID = MPlayerLocalID;
//.....................................................................
// Transfer the event in OutList to DoList, un-queue the OutList
// event. If the DoList is full, stop transferring immediately.
//.....................................................................
OutList.First().IsExecuted = 0;
if (!DoList.Add(OutList.First())) {
return (size);
}
//.....................................................................
// Add event to the send packet
//.....................................................................
memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
size += sizeof(EventClass);
//.....................................................................
// Increment our event counter; delete the last event from the queue
//.....................................................................
num++;
OutList.Next();
}
return (size);
} // end of Add_Uncompressed_Events
/***************************************************************************
* Add_Compressed_Events -- adds an compressed events to a packet *
* *
* INPUT: *
* buf buffer to store packet in *
* bufsize max size of buffer *
* frame_delay desired frame delay to attach to all outgoing packets *
* size reference to current packet size *
* cap max # events to process *
* *
* OUTPUT: *
* new size value *
* *
* WARNINGS: *
* This routine MUST check to be sure it doesn't overflow the buffer. *
* *
* HISTORY: *
* 11/21/1995 DRD : Created. *
*=========================================================================*/
static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay,
int size, int cap)
{
int num = 0; // # of events processed
EventClass::EventType eventtype; // type of event being compressed
EventClass prevevent; // last event processed
int datasize; // size of element plucked from event union
int storedsize; // actual # bytes stored from event
unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count
unsigned char numunits = 0; // megamission rep count value
bool missiondup = false; // flag: is this event a megamission repeat?
//------------------------------------------------------------------------
// clear previous event
//------------------------------------------------------------------------
memset (&prevevent, 0, sizeof(EventClass));
//------------------------------------------------------------------------
// Loop until there are no more events, we've processed our max # of
// events, or the buffer is full.
//------------------------------------------------------------------------
while (OutList.Count && (num < cap)) {
eventtype = OutList.First().Type;
datasize = EventClass::EventLength[ eventtype ];
//.....................................................................
// For a variable-sized event, pull the size from the event; otherwise,
// the size will be the data element size plus the event type value.
//.....................................................................
storedsize = datasize + sizeof (EventClass::EventType);
//.....................................................................
// MegaMission compression: MegaMissions are stored as:
// EventType
// Rep Count
// MegaMission structure (event # 1 only)
// Whom #2
// Whom #3
// Whom #4
// ...
// Whom #n
//.....................................................................
if (prevevent.Type == EventClass::MEGAMISSION) {
//..................................................................
// If previous & current events are both MegaMissions:
//..................................................................
if (eventtype == EventClass::MEGAMISSION) {
//...............................................................
// If the Mission, Target, & Destination are the same, compress
// the events into one:
// - Change datasize to the size of the 'Whom' field only
// - set total # bytes to store to the size of the 'Whom' only
// - increment the MegaMission rep count
// - set the MegaMission rep flag
//...............................................................
if (OutList.First().Data.MegaMission.Mission ==
prevevent.Data.MegaMission.Mission &&
OutList.First().Data.MegaMission.Target ==
prevevent.Data.MegaMission.Target &&
OutList.First().Data.MegaMission.Destination ==
prevevent.Data.MegaMission.Destination) {
datasize = sizeof(prevevent.Data.MegaMission.Whom);
storedsize = datasize;
numunits++;
missiondup = true;
}
//...............................................................
// Data doesn't match; start a new run of MegaMissions:
// - Store previous MegaMission rep count
// - Init 'unitsptr' to buffer pos after next EventType
// - set total # bytes to store to 'datasize' + sizeof(EventType) +
// sizeof (numunits)
// - init the MegaMission rep count to 1
// - clear the MegaMission rep flag
//...............................................................
else {
*unitsptr = numunits;
unitsptr = ((unsigned char *)buf) + size +
sizeof(EventClass::EventType);
storedsize += sizeof(numunits);
numunits = 1;
missiondup = false;
}
}
//..................................................................
// Previous event was a MegaMission, but this one isn't: end the
// run of MegaMissions:
// - Store previous MegaMission rep count
// - Clear variables
//..................................................................
else {
*unitsptr = numunits; // save # events in our run
unitsptr = NULL; // init other values
numunits = 0;
missiondup = false;
}
}
//.....................................................................
// The previous event is not a MEGAMISSION but the current event is:
// Set up a new run of MegaMissions:
// - Init 'unitsptr' to buffer pos after next EventType
// - set total # bytes to store to 'datasize' + sizeof(EventType) +
// sizeof (numunits)
// - init the MegaMission rep count to 1
// - clear the MegaMission rep flag
//.....................................................................
else if (eventtype == EventClass::MEGAMISSION) {
unitsptr = ((unsigned char *)buf) + size +
sizeof(EventClass::EventType);
storedsize += sizeof(numunits);
numunits = 1;
missiondup = false;
}
//.....................................................................
// Will the next event exceed the size of the buffer? If so,
// stop compressing.
//.....................................................................
if ( (size + storedsize) > bufsize )
break;
//.....................................................................
// Set the event's frame delay (this is protocol-dependent)
//.....................................................................
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
OutList.First().Frame = ((Frame + frame_delay +
(FrameSendRate - 1)) / FrameSendRate) *
FrameSendRate;
}
else {
OutList.First().Frame = Frame + frame_delay;
}
//.....................................................................
// Set the event's ID
//.....................................................................
OutList.First().ID = Houses.ID(PlayerPtr);
OutList.First().MPlayerID = MPlayerLocalID;
//.....................................................................
// Transfer the event in OutList to DoList, un-queue the OutList event.
// If the DoList is full, stop transferring immediately.
//.....................................................................
OutList.First().IsExecuted = 0;
if ( !DoList.Add( OutList.First() ) ) {
break;
}
//---------------------------------------------------------------------
// Compress the event into the send packet buffer
//---------------------------------------------------------------------
switch ( eventtype ) {
//..................................................................
// RESPONSE_TIME: just use the Delay field of the FrameInfo union
//..................................................................
case (EventClass::RESPONSE_TIME):
*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
&OutList.First().Data.FrameInfo.Delay, datasize );
size += (datasize + sizeof(EventClass::EventType));
break;
//..................................................................
// MEGAMISSION:
//..................................................................
case (EventClass::MEGAMISSION):
//...............................................................
// Repeated mission in a run:
// - Update the rep count (in case we break out)
// - Copy the Whom field only
//...............................................................
if (missiondup) {
*unitsptr = numunits;
memcpy ( ((char *)buf) + size,
&OutList.First().Data.MegaMission.Whom, datasize );
size += datasize;
}
//...............................................................
// 1st mission in a run:
// - Init the rep count (in case we break out)
// - Set the EventType
// - Copy the MegaMission structure, leaving room for 'numunits'
//...............................................................
else {
*unitsptr = numunits;
*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
memcpy ( ((char *)buf) + size +
sizeof(EventClass::EventType) + sizeof(numunits),
&OutList.First().Data.MegaMission, datasize );
size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits));
}
break;
//..................................................................
// Default case: Just copy over the data field from the union
//..................................................................
default:
*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
&OutList.First().Data, datasize );
size += (datasize + sizeof(EventClass::EventType));
break;
}
//---------------------------------------------------------------------
// update # events processed
//---------------------------------------------------------------------
num++;
//---------------------------------------------------------------------
// Update 'prevevent'
//---------------------------------------------------------------------
memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) );
//---------------------------------------------------------------------
// Go to the next event to process
//---------------------------------------------------------------------
OutList.Next();
}
return (size);
} // end of Add_Compressed_Events
/***************************************************************************
* Breakup_Receive_Packet -- Splits a big packet into little ones. *
* *
* INPUT: *
* buf buffer to break up *
* bufsize length of buffer *
* *
* OUTPUT: *
* # events added to queue, -1 if fatal error (queue is full) *
* (return value includes any FRAMEINFO packets encountered; *
* FRAMESYNC's are ignored) *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Breakup_Receive_Packet(void *buf, int bufsize )
{
int count = 0;
/*
** is there enough leftover for another record
*/
switch (CommProtocol) {
case (COMM_PROTOCOL_SINGLE_NO_COMP):
count = Extract_Uncompressed_Events(buf, bufsize);
break;
default:
count = Extract_Compressed_Events(buf, bufsize);
break;
}
return (count);
} /* end of Breakup_Receive_Packet */
/***************************************************************************
* Extract_Uncompressed_Events -- extracts events from a packet *
* *
* INPUT: *
* buf buffer containing events to extract *
* bufsize length of 'buf' *
* *
* OUTPUT: *
* # events extracted *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 DRD : Created. *
*=========================================================================*/
static int Extract_Uncompressed_Events(void *buf, int bufsize)
{
int count = 0;
int pos = 0;
int leftover = bufsize;
EventClass *event;
//------------------------------------------------------------------------
// Loop until there are no more events in the packet
//------------------------------------------------------------------------
while (leftover >= sizeof(EventClass) ) {
event = (EventClass *)(((char *)buf) + pos);
//.....................................................................
// add event to the DoList, only if it's not a FRAMESYNC
// (but FRAMEINFO's do get added.)
//.....................................................................
if (event->Type != EventClass::FRAMESYNC) {
event->IsExecuted = 0;
if (!DoList.Add( *event )) {
return (-1);
}
//..................................................................
// Keep count of how many events we add to the queue
//..................................................................
count++;
}
//.....................................................................
// Point to the next position in the buffer; decrement our 'leftover'
//.....................................................................
pos += sizeof(EventClass);
leftover -= sizeof(EventClass);
}
return (count);
} // end of Extract_Uncompressed_Events
/***************************************************************************
* Extract_Compressed_Events -- extracts events from a packet *
* *
* INPUT: *
* buf buffer containing events to extract *
* bufsize length of 'buf' *
* *
* OUTPUT: *
* # events extracted *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 DRD : Created. *
*=========================================================================*/
static int Extract_Compressed_Events(void *buf, int bufsize)
{
int pos = 0; // current buffer parsing position
int leftover = bufsize; // # bytes left to process
EventClass *event; // event ptr for parsing buffer
int count = 0; // # events processed
int datasize = 0; // size of data to copy
EventClass eventdata; // stores Frame, ID, etc
unsigned char numunits = 0; // # units stored in compressed MegaMissions
//int lasteventtype=0;
//------------------------------------------------------------------------
// Clear work event structure
//------------------------------------------------------------------------
memset (&eventdata, 0, sizeof(EventClass));
//------------------------------------------------------------------------
// Assume the first event is a FRAMEINFO event
// Init 'datasize' to the amount of data to copy, minus the EventType value
// For the 1st packet only, this will include all info before the Data
// union, plus the size of the FrameInfo structure, minus the EventType size.
//------------------------------------------------------------------------
datasize = (offsetof(EventClass, Data) +
size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType);
event = (EventClass *)(((char *)buf) + pos);
while ((unsigned)leftover >= (datasize + sizeof(EventClass::EventType)) ) {
//.....................................................................
// add event to the DoList, only if it's not a FRAMESYNC
// (but FRAMEINFO's do get added.)
//.....................................................................
if (event->Type != EventClass::FRAMESYNC) {
//..................................................................
// initialize the common data from the FRAMEINFO event
// keeping IsExecuted 0
//..................................................................
if (event->Type == EventClass::FRAMEINFO) {
eventdata.Frame = event->Frame;
eventdata.ID = event->ID;
eventdata.MPlayerID = event->MPlayerID;
//...............................................................
// Adjust position past the common data
//...............................................................
pos += (offsetof(EventClass, Data) -
sizeof(EventClass::EventType));
leftover -= (offsetof(EventClass, Data) -
sizeof(EventClass::EventType));
}
//..................................................................
// if MEGAMISSION event get the number of units (events to generate)
//..................................................................
else if (event->Type == EventClass::MEGAMISSION) {
numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type));
pos += sizeof(numunits);
leftover -= sizeof(numunits);
}
//..................................................................
// clear the union data portion of the event
//..................................................................
memset (&eventdata.Data, 0, sizeof(eventdata.Data));
eventdata.Type = event->Type;
datasize = EventClass::EventLength[ eventdata.Type ];
switch (eventdata.Type) {
case (EventClass::RESPONSE_TIME):
memcpy ( &eventdata.Data.FrameInfo.Delay,
((char *)buf) + pos + sizeof(EventClass::EventType),
datasize );
break;
case (EventClass::MEGAMISSION):
memcpy ( &eventdata.Data.MegaMission,
((char *)buf) + pos + sizeof(EventClass::EventType),
datasize );
if (numunits > 1) {
pos += (datasize + sizeof(EventClass::EventType));
leftover -= (datasize + sizeof(EventClass::EventType));
datasize = sizeof(eventdata.Data.MegaMission.Whom);
while (numunits) {
if ( !DoList.Add( eventdata ) ) {
return (-1);
}
//......................................................
// Keep count of how many events we add to the queue
//......................................................
count++;
numunits--;
memcpy ( &eventdata.Data.MegaMission.Whom,
((char *)buf) + pos, datasize );
//......................................................
// if one unit left fall thru to normal code
//......................................................
if (numunits == 1) {
datasize -= sizeof(EventClass::EventType);
break;
}
else {
pos += datasize;
leftover -= datasize;
}
}
}
break;
default:
memcpy ( &eventdata.Data,
((char *)buf) + pos + sizeof(EventClass::EventType),
datasize );
break;
}
char flip[128];
sprintf (flip, "C&C95 - Adding event type %d to queue\n", eventdata.Type);
CCDebugString (flip);
//if (lasteventtype == 11){
// break;
//}
//lasteventtype = (int) eventdata.Type;
if ( !DoList.Add( eventdata ) ) {
return (-1);
}
//..................................................................
// Keep count of how many events we add to the queue
//..................................................................
count++;
pos += (datasize + sizeof(EventClass::EventType));
leftover -= (datasize + sizeof(EventClass::EventType));
if (leftover) {
event = (EventClass *)(((char *)buf) + pos);
datasize = EventClass::EventLength[ event->Type ];
if (event->Type == EventClass::MEGAMISSION) {
datasize += sizeof(numunits);
}
}
}
//.....................................................................
// FRAMESYNC event: This >should< be the only event in the buffer,
// and it will be uncompressed.
//.....................................................................
else {
pos += (datasize + sizeof(EventClass::EventType));
leftover -= (datasize + sizeof(EventClass::EventType));
event = (EventClass *)(((char *)buf) + pos);
//..................................................................
// size of FRAMESYNC event - EventType size
//..................................................................
datasize = (offsetof(EventClass, Data) +
size_of(EventClass, Data.FrameInfo)) -
sizeof(EventClass::EventType);
}
}
return (count);
} // end of Extract_Compressed_Events
#endif //DEMO
/***************************************************************************
* Execute_DoList -- Executes commands from the DoList *
* *
* This routine executes any events in the DoList that need to be executed *
* on the current game frame. The events must be executed in a special *
* order, so that all systems execute all events in exactly the same *
* order. *
* *
* This routine also handles checking the Game CRC sent by other systems *
* against my own, to be sure we're still in sync. *
* *
* INPUT: *
* max_houses # houses to execute commands for *
* base_house HousesType to start with *
* net ptr to connection manager; NULL if none *
* skip_crc a frame-based countdown timer; if it's non-zero, the *
* CRC check will be skipped. Ignored if NULL. *
* their_frame array of their frame #'s *
* their_sent array of # commands they've sent *
* their_recv array of # commands I've received from them *
* *
* (their_xxx are ignored if 'net' is NULL.) *
* *
* OUTPUT: *
* 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) *
* *
* WARNINGS: *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static int Execute_DoList(int , HousesType ,
ConnManClass *net, TCountDownTimerClass *,
long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
{
int i,j,k,wibble;
int index;
//------------------------------------------------------------------------
// For a single-player game, just execute all events in the queue.
//------------------------------------------------------------------------
if (GameToPlay == GAME_NORMAL) {
for (i = 0; i < DoList.Count; i++) {
if ((unsigned)Frame >= DoList[i].Frame && !DoList[i].IsExecuted) {
DoList[i].Execute(); // execute it
DoList[i].IsExecuted = true; // mark as having been executed
}
}
return (1);
}
//#if(TIMING_FIX)
//
// If MPlayerMaxAhead is recomputed such that it increases, the systems
// may try to free-run to the new MaxAhead value. If so, they may miss
// an event that was generated after the TIMING event was created, but
// before it executed; this event will be scheduled with the older,
// shorter MaxAhead value. If a system doesn't receive this event, it
// may execute past the frame it's scheduled to execute on, creating
// a Packet-Recieved-Too-Late error. To prevent this, find any events
// that are scheduled to execute during this "period of vulnerability",
// and re-schedule for the end of that period.
//
if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
for (j = 0; j < DoList.Count; j++) {
if (DoList[j].Type != EventClass::FRAMEINFO &&
DoList[j].Frame > (unsigned)NewMaxAheadFrame1 &&
DoList[j].Frame < (unsigned)NewMaxAheadFrame2) {
DoList[j].Frame = (unsigned)NewMaxAheadFrame2;
}
}
}
//#endif
//------------------------------------------------------------------------
// Execute the DoList. Events must be executed in the same order on all
// systems; so, execute them in the order of the MPlayerID array. This
// array is stored in the same order on all systems.
//------------------------------------------------------------------------
for (i = 0; i < MPlayerCount; i++) {
HousesType house;
HouseClass *housep;
house = MPlayerHouses [i];
housep= HouseClass::As_Pointer (house);
//.....................................................................
// If for some reason this house doesn't exist, skip it.
// Also, if this house has exited the game, skip it. (The user can
// generate events after he exits, because the exit event is scheduled
// at least FrameSendRate*3 frames ahead. If one system gets these
// packets & another system doesn't, they'll go out of sync because
// they aren't checking the CommandCount for that house, since that
// house isn't connected any more.)
//.....................................................................
if (!housep){
continue;
}
if (!housep->IsHuman){
continue;
}
//.....................................................................
// Loop through all events
//.....................................................................
for (j = 0; j < DoList.Count; j++) {
#ifndef DEMO
if (net)
Update_Queue_Mono (net, 6);
#endif //DEMO
//..................................................................
// If this event was from the currently-executing player ID, and it's
// time to execute it, execute it.
//..................................................................
if (DoList[j].MPlayerID == MPlayerID[i] && (unsigned)Frame >= DoList[j].Frame &&
!DoList[j].IsExecuted) {
//...............................................................
// Error if it's too late to execute this packet!
//...............................................................
if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type !=
EventClass::FRAMEINFO) {
#ifndef DEMO
Dump_Packet_Too_Late_Stuff(&DoList[j]);
#endif //DEMO
CCMessageBox().Process (TXT_PACKET_TOO_LATE);
return (0);
}
//...............................................................
// Only execute EXIT & OPTIONS commands if they're from myself.
//...............................................................
if (DoList[j].Type==EventClass::EXIT ||
DoList[j].Type==EventClass::OPTIONS) {
if (DoList[j].Type==EventClass::EXIT){
CCDebugString ("C&C95 - Received EXIT packet\n");
/*
** Flag that this house lost because it quit. ST - 6/5/96 0:29AM
*/
for (wibble = 0; wibble < MPlayerCount; wibble++) {
if (MPlayerID[wibble] == DoList[j].MPlayerID) {
house = MPlayerHouses[wibble];
housep = HouseClass::As_Pointer (house);
housep->IGaveUp = true;
break;
}
}
/*
** Send the game statistics packet now since the game is effectivly over
*/
if (MPlayerCount == 2 &&
GameToPlay == GAME_INTERNET &&
!GameStatisticsPacketSent){
Register_Game_End_Time();
Send_Statistics_Packet();
}
}
if (DoList[j].ID == Houses.ID(PlayerPtr)) {
DoList[j].Execute();
}
//............................................................
// If this EXIT event isn't from myself, destroy the connection
// for that player. The HousesType for this event is the
// connection ID.
//............................................................
else if (DoList[j].Type==EventClass::EXIT) {
if (GameToPlay == GAME_MODEM
|| GameToPlay == GAME_NULL_MODEM){
//|| GameToPlay == GAME_INTERNET) {
//ST - 1/2/2019 5:29PM
//Destroy_Null_Connection( DoList[j].MPlayerID, 0 );
}
else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) {
index = net->Connection_Index (DoList[j].MPlayerID);
if (index != -1) {
for (k = index; k < net->Num_Connections() - 1; k++) {
their_frame[k] = their_frame[k+1];
their_sent[k] = their_sent[k+1];
their_recv[k] = their_recv[k+1];
}
CCDebugString ("C&C95 = Destroying connection due to exit event\n");
#ifndef DEMO
Destroy_Connection(DoList[j].MPlayerID,0);
#endif //DEMO
}
}
}
}
//...............................................................
// For a FRAMEINFO event, check the CRC value.
// This could be an old FRAMEINFO packet that was floating around
// for awhile and just arrived; if so, its Frame value will be
// old. Ignore these packets. (This created bogus sync bugs on
// the Internet, when packets that were 35 frames old arrived.)
//...............................................................
#ifndef DEMO
else if (DoList[j].Type == EventClass::FRAMEINFO) {
if (DoList[j].Frame == Frame &&
DoList[j].Data.FrameInfo.Delay < 32) {
index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) &
0x001f);
if (CRC[index] != DoList[j].Data.FrameInfo.CRC) {
Print_CRCs(&DoList[j]);
if (CCMessageBox().Process (TXT_OUT_OF_SYNC,
TXT_CONTINUE, TXT_STOP) == 0) {
if (GameToPlay == GAME_MODEM ||
GameToPlay == GAME_NULL_MODEM){
#if (0)//ST - 1/2/2019 5:29PM
Destroy_Null_Connection( DoList[j].MPlayerID, -1 );
Shutdown_Modem();
#endif
GameToPlay = GAME_NORMAL;
}
else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) {
CCDebugString ("C&C95 = Destroying connections due to bad frame info packet\n");
while (net->Num_Connections()) {
Destroy_Connection (net->Connection_ID(0), -1);
}
}
Map.Flag_To_Redraw(true);
}
else {
return (0);
}
return (1);
}
}
}
#endif //DEMO
//...............................................................
// Execute other commands
//...............................................................
else {
DoList[j].Execute();
}
//...............................................................
// Mark this event as executed.
//...............................................................
DoList[j].IsExecuted = 1;
}
}
}
return (1);
} // end of Execute_DoList
/***************************************************************************
* Clean_DoList -- Cleans out old events from the DoList *
* *
* Currently, an event can only be removed from the DoList if it's at the *
* head of the list; and event can't be removed from the middle. So, *
* this routine loops as long as the next event in the DoList has been *
* executed, it's removed. *
* *
* INPUT: *
* net ptr to connection manager; ignored if NULL *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Clean_DoList(ConnManClass *net)
{
while (DoList.Count) {
#ifndef DEMO
if (net)
Update_Queue_Mono (net, 7);
#else
net = net;
#endif //DEMO
//.....................................................................
// Discard events that have been executed, OR it's too late to execute.
// (This happens if another player exits the game; he'll leave FRAMEINFO
// events lying around in my queue. They won't have been "executed",
// because his IPX connection was destroyed.)
//.....................................................................
if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) {
DoList.Next();
}
else {
break;
}
}
} // end of Clean_DoList
#ifndef DEMO
/***************************************************************************
* Queue_Record -- Records the DoList to disk *
* *
* This routine just saves any events in the DoList to disk; we can later *
* "play back" the recording just be pulling events from disk rather than *
* from the network! *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 08/14/1995 BRR : Created. *
*=========================================================================*/
static void Queue_Record(void)
{
int i,j;
//------------------------------------------------------------------------
// Compute # of events to save this frame
//------------------------------------------------------------------------
j = 0;
for (i = 0; i < DoList.Count; i++) {
if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
j++;
}
}
//------------------------------------------------------------------------
// Save the # of events, then all events.
//------------------------------------------------------------------------
RecordFile.Write (&j,sizeof(j));
for (i = 0; i < DoList.Count; i++) {
if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
RecordFile.Write (&DoList[i],sizeof (EventClass));
j--;
}
}
} /* end of Queue_Record */
/***************************************************************************
* Queue_Playback -- plays back queue entries from a record file *
* *
* This routine reads events from disk, putting them into the DoList; *
* it then executes the DoList just like the network version does. The *
* result is that the game "plays back" like a recording. *
* *
* This routine detects mouse motion and stops playback, so it can work *
* like an "attract" mode, showing a demo of the game itself. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 05/15/1995 BRR : Created. *
*=========================================================================*/
static void Queue_Playback(void)
{
int numevents;
EventClass event;
int i;
int ok;
static int mx,my;
int max_houses;
HousesType base_house;
int key;
int testframe;
//------------------------------------------------------------------------
// If the user hits ESC, stop the playback
//------------------------------------------------------------------------
if (Check_Key_Num()) {
key = Get_Key();
//
// If the user hit ESC, end the recording. If this is an Attract-mode
// recording, end it no matter what the user does (any key or mouse).
//
if (key == KA_ESC || AllowAttract) {
GameActive = 0;
return;
}
}
//------------------------------------------------------------------------
// If we're in "Attact" mode, and the user moves the mouse, stop the
// playback.
//------------------------------------------------------------------------
if (AllowAttract && Frame > 0 &&
(mx != Get_Mouse_X() || my != Get_Mouse_Y())) {
GameActive = 0;
return;
}
mx = Get_Mouse_X();
my = Get_Mouse_Y();
//------------------------------------------------------------------------
// Compute the Game's CRC
//------------------------------------------------------------------------
Compute_Game_CRC();
CRC[Frame & 0x001f] = GameCRC;
//------------------------------------------------------------------------
// Don't read anything the first time through (since the Queue_AI_Network
// routine didn't write anything the first time through); do this after the
// CRC is computed, since we'll still need a CRC for Frame 0.
//------------------------------------------------------------------------
if (Frame==0 && GameToPlay!=GAME_NORMAL) {
return;
}
//------------------------------------------------------------------------
// Only process every 'FrameSendRate' frames
//------------------------------------------------------------------------
testframe = ((Frame + (FrameSendRate - 1)) / FrameSendRate) * FrameSendRate;
if (GameToPlay != GAME_NORMAL &&
CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
if (Frame != testframe) {
return;
}
}
//------------------------------------------------------------------------
// Read the DoList from disk
//------------------------------------------------------------------------
ok = 1;
if (RecordFile.Read (&numevents, sizeof(numevents)) ==
sizeof(numevents)) {
for (i = 0; i < numevents; i++) {
if (RecordFile.Read (&event, sizeof(EventClass)) ==
sizeof(EventClass)) {
event.IsExecuted = 0;
DoList.Add (event);
}
else {
ok = 0;
break;
}
}
}
else {
ok = 0;
}
if (!ok) {
GameActive = 0;
return;
}
//------------------------------------------------------------------------
// Execute the DoList; if an error occurs, bail out.
//------------------------------------------------------------------------
if (GameToPlay == GAME_NORMAL) {
max_houses = 1;
base_house = PlayerPtr->Class->House;
}
else {
max_houses = MPlayerMax;
base_house = HOUSE_MULTI1;
}
if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) {
GameActive = 0;
return;
}
//------------------------------------------------------------------------
// Clean out the DoList
//------------------------------------------------------------------------
Clean_DoList(NULL);
} /* end of Queue_Playback */
/***************************************************************************
* Compute_Game_CRC -- Computes a CRC value of the entire game. *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 05/09/1995 BRR : Created. *
*=========================================================================*/
static void Compute_Game_CRC(void)
{
int i;
InfantryClass *infp;
UnitClass *unitp;
BuildingClass *bldgp;
//ObjectClass *objp;
GameCRC = 0;
//------------------------------------------------------------------------
// Infantry
//------------------------------------------------------------------------
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
}
//------------------------------------------------------------------------
// Units
//------------------------------------------------------------------------
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
(int)unitp->SecondaryFacing);
}
//------------------------------------------------------------------------
// Buildings
//------------------------------------------------------------------------
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
}
#if(0)
//------------------------------------------------------------------------
// Map Layers
//------------------------------------------------------------------------
for (i = 0; i < LAYER_COUNT; i++) {
for (j = 0; j < Map.Layer[i].Count(); j++) {
objp = Map.Layer[i][j];
Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
}
}
//------------------------------------------------------------------------
// Logic Layers
//------------------------------------------------------------------------
for (i = 0; i < Logic.Count(); i++) {
objp = Logic[i];
Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
}
#endif
//------------------------------------------------------------------------
// A random #
//------------------------------------------------------------------------
Add_CRC(&GameCRC, rand());
} /* end of Compute_Game_CRC */
/***************************************************************************
* Add_CRC -- Adds a value to a CRC *
* *
* INPUT: *
* crc ptr to crc *
* val value to add *
* *
* OUTPUT: *
* none *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 05/09/1995 BRR : Created. *
*=========================================================================*/
void Add_CRC(unsigned long *crc, unsigned long val)
{
int hibit;
if ( (*crc) & 0x80000000) {
hibit = 1;
}
else {
hibit = 0;
}
(*crc) <<= 1;
(*crc) += val;
(*crc) += hibit;
} /* end of Add_CRC */
/***************************************************************************
* Print_CRCs -- Prints a data file for finding Sync Bugs *
* *
* INPUT: *
* none. *
* *
* OUTPUT: *
* none *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 05/09/1995 BRR : Created. *
*=========================================================================*/
void Print_CRCs(EventClass *ev)
{
ev=ev;
int i; //,j;
InfantryClass *infp;
UnitClass *unitp;
BuildingClass *bldgp;
//ObjectClass *objp;
FILE *fp;
int rnd;
HouseClass *housep;
//HousesType house;
int color;
Mono_Clear_Screen();
Mono_Set_Cursor (0,0);
char filename[80];
sprintf (filename, "CRC%02d.TXT", Frame & 0x1f);
fp = fopen(filename, "wt"); //"OUT.TXT","wt");
if (fp==NULL) {
return;
}
for (i = 0; i < 32; i++) {
fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI1);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi1: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI2);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi2: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI3);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi3: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI4);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi4: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI5);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi5: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
housep = HouseClass::As_Pointer (HOUSE_MULTI6);
if (housep) {
color = housep->RemapColor;
fprintf(fp,"Multi6: IsHuman:%d Color:%s\n",housep->IsHuman,
ColorNames[color]);
}
//------------------------------------------------------------------------
// Multi1 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI1 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI1) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi1 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi2 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI2 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI2) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi2 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi3 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI3 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI3) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi3 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi4 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI4 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI4) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi4 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi5 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI5 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI5) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi5 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi6 Infantry
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI6 INFANTRY -------------------\n");
for (i = 0; i < Infantry.Count(); i++) {
infp = (InfantryClass *)Infantry.Active_Ptr(i);
if (infp->Owner()==HOUSE_MULTI6) {
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
infp->Class->Type);
}
}
Mono_Printf("Multi6 Infantry:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi1 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI1 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI1) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi1 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi2 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI2 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI2) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi2 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi3 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI3 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI3) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi3 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi4 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI4 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI4) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi4 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi5 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI5 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI5) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi5 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi6 Units
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI6 UNITS -------------------\n");
for (i = 0; i < Units.Count(); i++) {
unitp = (UnitClass *)Units.Active_Ptr(i);
if (unitp->Owner()==HOUSE_MULTI6) {
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
fprintf(fp,
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n",
unitp->Coord,(int)unitp->PrimaryFacing,
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
unitp->Class->Type);
}
}
Mono_Printf("Multi6 Units:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi1 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI1 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI1) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi1 Buildings:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi2 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI2 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI2) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi2 Buildings:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi3 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI3 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI3) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi3 Buildings:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi4 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI4 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI4) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi4 Buildings:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi5 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI5 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI5) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi5 Buildings:%d\n",GameCRC);
}
//------------------------------------------------------------------------
// Multi6 Buildings
//------------------------------------------------------------------------
if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
GameCRC = 0;
fprintf(fp,"-------------------- MULTI6 BUILDINGS -------------------\n");
for (i = 0; i < Buildings.Count(); i++) {
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
if (bldgp->Owner()==HOUSE_MULTI6) {
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n",
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
bldgp->Class->Type);
}
}
Mono_Printf("Multi6 Buildings:%d\n",GameCRC);
}
#if (0)
//------------------------------------------------------------------------
// Map Layers
//------------------------------------------------------------------------
GameCRC = 0;
for (i = 0; i < LAYER_COUNT; i++) {
fprintf(fp,">>>> MAP LAYER %d <<<<\n",i);
for (j = 0; j < Map.Layer[i].Count(); j++) {
objp = Map.Layer[i][j];
Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
fprintf(fp,"Object %d: %x ",j,objp->Coord);
if (objp->What_Am_I() == RTTI_AIRCRAFT)
fprintf(fp,"Aircraft (Type:%d) ",
(AircraftType)(*((AircraftClass *)objp)));
else if (objp->What_Am_I() == RTTI_ANIM)
fprintf(fp,"Anim (Type:%d) ",
(AnimType)(*((AnimClass *)objp)));
else if (objp->What_Am_I() == RTTI_BUILDING)
fprintf(fp,"Building (Type:%d) ",
(StructType)(*((BuildingClass *)objp)));
else if (objp->What_Am_I() == RTTI_BULLET)
fprintf(fp,"Bullet (Type:%d) ",
(BulletType)(*((BulletClass *)objp)));
else if (objp->What_Am_I() == RTTI_INFANTRY)
fprintf(fp,"Infantry (Type:%d) ",
(InfantryType)(*((InfantryClass *)objp)));
else if (objp->What_Am_I() == RTTI_OVERLAY)
fprintf(fp,"Overlay (Type:%d) ",
(OverlayType)(*((OverlayClass *)objp)));
else if (objp->What_Am_I() == RTTI_SMUDGE)
fprintf(fp,"Smudge (Type:%d) ",
(SmudgeType)(*((SmudgeClass *)objp)));
else if (objp->What_Am_I() == RTTI_TEMPLATE)
fprintf(fp,"Template (Type:%d) ",
(TemplateType)(*((TemplateClass *)objp)));
else if (objp->What_Am_I() == RTTI_TERRAIN)
fprintf(fp,"Terrain (Type:%d) ",
(TerrainType)(*((TerrainClass *)objp)));
else if (objp->What_Am_I() == RTTI_UNIT)
fprintf(fp,"Unit (Type:%d) ",
(UnitType)(*((UnitClass *)objp)));
house = objp->Owner();
if (house!=HOUSE_NONE) {
housep = HouseClass::As_Pointer (house);
fprintf(fp,"Owner: %s\n",housep->Class->IniName);
}
else {
fprintf(fp,"Owner: NONE\n");
}
}
}
Mono_Printf("Map Layers:%d\n",GameCRC);
//------------------------------------------------------------------------
// Logic Layers
//------------------------------------------------------------------------
GameCRC = 0;
fprintf(fp,">>>> LOGIC LAYER <<<<\n");
for (i = 0; i < Logic.Count(); i++) {
objp = Logic[i];
Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
fprintf(fp,"Object %d: %x ",i,objp->Coord);
if (objp->What_Am_I() == RTTI_AIRCRAFT)
fprintf(fp,"Aircraft (Type:%d) ",
(AircraftType)(*((AircraftClass *)objp)));
else if (objp->What_Am_I() == RTTI_ANIM)
fprintf(fp,"Anim (Type:%d) ",
(AnimType)(*((AnimClass *)objp)));
else if (objp->What_Am_I() == RTTI_BUILDING)
fprintf(fp,"Building (Type:%d) ",
(StructType)(*((BuildingClass *)objp)));
else if (objp->What_Am_I() == RTTI_BULLET)
fprintf(fp,"Bullet (Type:%d) ",
(BulletType)(*((BulletClass *)objp)));
else if (objp->What_Am_I() == RTTI_INFANTRY)
fprintf(fp,"Infantry (Type:%d) ",
(InfantryType)(*((InfantryClass *)objp)));
else if (objp->What_Am_I() == RTTI_OVERLAY)
fprintf(fp,"Overlay (Type:%d) ",
(OverlayType)(*((OverlayClass *)objp)));
else if (objp->What_Am_I() == RTTI_SMUDGE)
fprintf(fp,"Smudge (Type:%d) ",
(SmudgeType)(*((SmudgeClass *)objp)));
else if (objp->What_Am_I() == RTTI_TEMPLATE)
fprintf(fp,"Template (Type:%d) ",
(TemplateType)(*((TemplateClass *)objp)));
else if (objp->What_Am_I() == RTTI_TERRAIN)
fprintf(fp,"Terrain (Type:%d) ",
(TerrainType)(*((TerrainClass *)objp)));
else if (objp->What_Am_I() == RTTI_UNIT)
fprintf(fp,"Unit (Type:%d) ",
(UnitType)(*((UnitClass *)objp)));
house = objp->Owner();
if (house!=HOUSE_NONE) {
housep = HouseClass::As_Pointer (house);
fprintf(fp,"Owner: %s\n",housep->Class->IniName);
}
else {
fprintf(fp,"Owner: NONE\n");
}
}
Mono_Printf("Logic:%d\n",GameCRC);
#endif
//------------------------------------------------------------------------
// Random # generator, frame #
//------------------------------------------------------------------------
rnd = rand();
Mono_Printf("Random Number:%d\n",rnd);
fprintf(fp,"\nRandom Number:%d\n",rnd);
Mono_Printf("My Frame:%d\n",Frame);
fprintf(fp,"My Frame:%d\n",Frame);
#if (0)
fprintf(fp,"-------------- Offending event: ----------------\n");
fprintf(fp,"Type: %d\n",ev->Type);
fprintf(fp,"Frame: %d\n",ev->Frame);
fprintf(fp,"MPlayerID: %04x\n",ev->MPlayerID);
if (ev->Type == EventClass::FRAMEINFO) {
fprintf(fp,"CRC: %x\n",ev->Data.FrameInfo.CRC);
fprintf(fp,"CommandCount: %x\n",ev->Data.FrameInfo.CommandCount);
fprintf(fp,"Delay: %x\n",ev->Data.FrameInfo.Delay);
}
#endif
fclose(fp);
} /* end of Print_CRCs */
/***************************************************************************
* Init_Queue_Mono -- inits mono display *
* *
* This routine steals control of the mono screen away from the rest of *
* the engine, by setting the global IsMono; if IsMono is set, the other *
* routines in this module turn off the Mono display when they're done *
* with it, so the rest of the engine won't over-write what we're writing. *
* *
* INPUT: *
* net ptr to connection manager *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Init_Queue_Mono(ConnManClass *net)
{
#if(SHOW_MONO)
//------------------------------------------------------------------------
// Set 'IsMono' so we can steal the mono screen from the engine
//------------------------------------------------------------------------
if (Frame==0 && MonoClass::Is_Enabled()) {
IsMono = true;
}
//------------------------------------------------------------------------
// Enable mono output for our stuff; we must Disable it before we return
// control to the engine.
//------------------------------------------------------------------------
if (IsMono)
MonoClass::Enable();
if (net->Num_Connections() > 0) {
//.....................................................................
// Network mono debugging screen
//.....................................................................
if (NetMonoMode==0) {
if (Frame==0 || NewMonoMode) {
net->Configure_Debug (0, sizeof (CommHeaderType),
sizeof(EventClass::EventType), EventClass::EventNames, 0);
net->Mono_Debug_Print (0,1);
NewMonoMode = 0;
}
else {
net->Mono_Debug_Print (0,0);
}
}
//.....................................................................
// Flow control debugging output
//.....................................................................
else {
if (NewMonoMode) {
Mono_Clear_Screen();
Mono_Printf(" Queue AI:\n"); // flowcount[0]
Mono_Printf(" Build Packet Loop:\n"); // flowcount[1]
Mono_Printf(" Frame Sync:\n"); // flowcount[2]
Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3]
Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4]
Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5]
Mono_Printf(" DoList Execution:\n"); // flowcount[6]
Mono_Printf(" DoList Cleaning:\n"); // flowcount[7]
Mono_Printf("\n");
Mono_Printf(" Frame:\n");
Mono_Printf(" MPlayerMaxAhead:\n");
Mono_Printf(" their_recv:\n");
Mono_Printf(" their_sent:\n");
Mono_Printf(" my_sent:\n");
Mono_Printf(" DesiredFrameRate:\n");
NewMonoMode = 0;
}
}
}
#else
net = net;
#endif
} // end of Init_Queue_Mono
/***************************************************************************
* Update_Queue_Mono -- updates mono display *
* *
* INPUT: *
* net ptr to connection manager *
* flow_index index # for flow-count updates *
* -1: display *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Update_Queue_Mono(ConnManClass *net, int flow_index)
{
#if(SHOW_MONO)
static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//------------------------------------------------------------------------
// If 'NetMonoMode' is 1, display flowcount info
//------------------------------------------------------------------------
if (NetMonoMode==1) {
if (flow_index >= 0 && flow_index < 20) {
Mono_Set_Cursor(35,flow_index);
flowcount[flow_index]++;
Mono_Printf("%d",flowcount[flow_index]);
}
}
//------------------------------------------------------------------------
// Otherwise, display the connection debug screen
//------------------------------------------------------------------------
else {
net->Mono_Debug_Print (0,0);
}
#else
flow_index = flow_index;
net = net;
#endif
} // end of Update_Queue_Mono
/***************************************************************************
* Print_Framesync_Values -- displays frame-sync variables *
* *
* INPUT: *
* curframe current game Frame # *
* max_ahead max-ahead value *
* num_connections # connections *
* their_recv # commands I've received from my connections *
* their_sent # commands each connection claims to have sent *
* my_sent # commands I've sent *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 11/21/1995 BRR : Created. *
*=========================================================================*/
static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
int num_connections, unsigned short *their_recv,
unsigned short *their_sent, unsigned short my_sent)
{
#if(SHOW_MONO)
int i;
if (NetMonoMode==1) {
Mono_Set_Cursor(35,9);
Mono_Printf("%d",curframe);
Mono_Set_Cursor(35,10);
Mono_Printf("%d",max_ahead);
for (i = 0; i < num_connections; i++) {
Mono_Set_Cursor(35 + i*5,11);
Mono_Printf("%4d",(int)their_recv[i]);
}
for (i = 0; i < num_connections; i++) {
Mono_Set_Cursor(35 + i*5,12);
Mono_Printf("%4d",(int)their_sent[i]);
}
Mono_Set_Cursor(35,13);
Mono_Printf("%4d",(int)my_sent);
Mono_Set_Cursor(35,14);
Mono_Printf("%2d",(int)DesiredFrameRate);
}
#else
curframe = curframe;
max_ahead = max_ahead;
num_connections = num_connections;
their_recv = their_recv;
their_sent = their_sent;
my_sent = my_sent;
#endif
} // end of Print_Framesync_Values
/***************************************************************************
* Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk *
* *
* INPUT: *
* event ptr to event to print *
* *
* OUTPUT: *
* none. *
* *
* WARNINGS: *
* none. *
* *
* HISTORY: *
* 06/28/1996 BRR : Created. *
*=========================================================================*/
void Dump_Packet_Too_Late_Stuff(EventClass *event)
{
FILE *fp;
int i;
fp = fopen("toolate.txt", "wt");
if (!fp) {
return;
}
fprintf(fp,"--------- Event data: -------------------\n");
fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]);
fprintf(fp,"Frame: %d\n",event->Frame);
fprintf(fp,"ID: %d\n",event->ID);
fprintf(fp,"MPlayerID: %04x\n",event->MPlayerID);
for (i = 0; i < MPlayerCount; i++) {
if (event->MPlayerID == MPlayerID[i]) {
fprintf(fp,"Player's Name: %s",MPlayerNames[i]);
}
}
fprintf(fp,"----------- My data: ------------------\n");
fprintf(fp,"Frame:%d\n",Frame);
fprintf(fp,"MaxAhead:%d\n",MPlayerMaxAhead);
fclose(fp);
}
#endif //DEMO
/*************************** end of queue.cpp ******************************/