4561 lines
186 KiB
C++
4561 lines
186 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: /counterstrike/QUEUE.CPP 6 3/14/97 5:12p Steve_tall $ */
|
||
|
/***************************************************************************
|
||
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
||
|
* *
|
||
|
* Project Name : Command & Conquer *
|
||
|
* *
|
||
|
* File Name : QUEUE.CPP *
|
||
|
* *
|
||
|
* Programmer : Bill R. Randolph *
|
||
|
* *
|
||
|
* Start Date : 11/28/95 *
|
||
|
* *
|
||
|
* Last Update : October 14, 1996 [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 *
|
||
|
* Check_Mirror -- Checks mirror memory *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
#include "function.h"
|
||
|
|
||
|
#ifdef WOLAPI_INTEGRATION
|
||
|
//#include "WolDebug.h"
|
||
|
#include "WolapiOb.h"
|
||
|
extern WolapiObject* pWolapi;
|
||
|
|
||
|
bool bReconnectDialogCancelled;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/********************************** Defines *********************************/
|
||
|
#define SHOW_MONO 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
|
||
|
//---------------------------------------------------------------------------
|
||
|
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[8] = {
|
||
|
"Yellow",
|
||
|
"LtBlue",
|
||
|
"Red",
|
||
|
"Green",
|
||
|
"Orange",
|
||
|
"Grey",
|
||
|
"Blue",
|
||
|
"Brown"
|
||
|
};
|
||
|
|
||
|
//...........................................................................
|
||
|
// 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
|
||
|
//...........................................................................
|
||
|
int NetMonoMode = 1;
|
||
|
int NewMonoMode = 1;
|
||
|
static int IsMono = 0;
|
||
|
|
||
|
//---------------------------------------------------------------------------
|
||
|
// 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;
|
||
|
|
||
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98
|
||
|
extern void Enable_Secret_Units(void);
|
||
|
#endif
|
||
|
|
||
|
/********************************* Prototypes *******************************/
|
||
|
//...........................................................................
|
||
|
// Main multiplayer queue logic
|
||
|
//...........................................................................
|
||
|
static void Queue_AI_Normal(void);
|
||
|
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); //, int init);
|
||
|
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(CDTimerClass<SystemTimerClass> *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);
|
||
|
|
||
|
//...........................................................................
|
||
|
// Packet compression/decompression:
|
||
|
//...........................................................................
|
||
|
static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
|
||
|
int num_cmds, int cap);
|
||
|
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);
|
||
|
static int Breakup_Receive_Packet(void *buf, int bufsize );
|
||
|
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, CDTimerClass<FrameTimerClass> *skip_crc,
|
||
|
// ConnManClass *net, TCountDownTimerClass *skip_crc,
|
||
|
long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
|
||
|
static void Clean_DoList(ConnManClass *net);
|
||
|
static void Queue_Record(void);
|
||
|
static void Queue_Playback(void);
|
||
|
|
||
|
//...........................................................................
|
||
|
// Debugging:
|
||
|
//...........................................................................
|
||
|
static void Compute_Game_CRC(void);
|
||
|
void Add_CRC(unsigned long *crc, unsigned long val);
|
||
|
static void Print_CRCs(EventClass *ev);
|
||
|
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);
|
||
|
|
||
|
extern void Keyboard_Process(KeyNumType &input);
|
||
|
void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net,
|
||
|
long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
|
||
|
void Check_Mirror(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(TargetClass whom, MissionType mission, TARGET target, TARGET destination)
|
||
|
{
|
||
|
if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) {
|
||
|
return(false);
|
||
|
} else {
|
||
|
return(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
* Queue_Mission -- Queue a mega mission event, formation override for common speed. *
|
||
|
* *
|
||
|
* 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). *
|
||
|
* *
|
||
|
* speed -- The override speed for this unit. *
|
||
|
* *
|
||
|
* maxspeed -- The override maximum speed for this unit. *
|
||
|
* *
|
||
|
* OUTPUT: Was the mission request queued successfully? *
|
||
|
* *
|
||
|
* WARNINGS: none *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 09/21/1995 JLB : Created. *
|
||
|
*=============================================================================================*/
|
||
|
bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed)
|
||
|
{
|
||
|
if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) {
|
||
|
return(false);
|
||
|
} else {
|
||
|
return(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* 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)
|
||
|
{
|
||
|
if (Session.Play) {
|
||
|
Queue_Playback();
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
|
||
|
switch (Session.Type) {
|
||
|
|
||
|
case GAME_SKIRMISH:
|
||
|
case GAME_NORMAL:
|
||
|
Queue_AI_Normal();
|
||
|
break;
|
||
|
|
||
|
case GAME_MODEM:
|
||
|
case GAME_NULL_MODEM:
|
||
|
case GAME_IPX:
|
||
|
case GAME_INTERNET:
|
||
|
case GAME_TEN:
|
||
|
case GAME_MPATH:
|
||
|
Queue_AI_Multiplayer();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} /* 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())) {
|
||
|
;
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add(OutList.First());
|
||
|
#endif
|
||
|
OutList.Next();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Save the DoList to disk, if we're in "Record" mode
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Session.Record) {
|
||
|
Queue_Record();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Execute the DoList; if an error occurs, bail out.
|
||
|
//------------------------------------------------------------------------
|
||
|
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 */
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* 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 *
|
||
|
* 'Session.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 + Session.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 order of the arrays their_frame[] etc is the same order the *
|
||
|
* connections are created in. The Sender's ID is passed to *
|
||
|
* Connection_Index() to obtain the array index. *
|
||
|
* *
|
||
|
* 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 properties: *
|
||
|
* - 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 'Session.MaxAhead' must be *
|
||
|
* divisible by 'n'; also, the minimum value for 'Session.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 Session.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 *
|
||
|
* Session.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 *
|
||
|
* Session.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)
|
||
|
{
|
||
|
if(Session.Type == GAME_SKIRMISH) return;
|
||
|
|
||
|
return;
|
||
|
#if (0)//PG
|
||
|
//........................................................................
|
||
|
// Enums:
|
||
|
//........................................................................
|
||
|
enum {
|
||
|
MIXFILE_RESEND_DELTA = 120, // ticks b/w resends
|
||
|
MIXFILE_TIMEOUT = 3600*2, // timeout waiting for mixfiles.
|
||
|
FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog
|
||
|
FRAMESYNC_TIMEOUT = (15*60), // timeout waiting for frame sync packet
|
||
|
};
|
||
|
|
||
|
int timeout_factor = (Session.Type == 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
|
||
|
//........................................................................
|
||
|
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
|
||
|
|
||
|
//........................................................................
|
||
|
// Timing variables
|
||
|
//........................................................................
|
||
|
static CDTimerClass<FrameTimerClass> skip_crc; // to delay the CRC check
|
||
|
// static TCountDownTimerClass skip_crc; // to delay the CRC check
|
||
|
|
||
|
//........................................................................
|
||
|
// 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 (0)//PG
|
||
|
if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
|
||
|
multi_packet_buf = NullModem.BuildBuf;
|
||
|
multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType);
|
||
|
net = &NullModem;
|
||
|
} else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
|
||
|
multi_packet_buf = Session.MetaPacket;
|
||
|
multi_packet_max = Session.MetaSize;
|
||
|
net = &Ipx;
|
||
|
}
|
||
|
#endif
|
||
|
#if(TEN)
|
||
|
else if (Session.Type == GAME_TEN) {
|
||
|
multi_packet_buf = Session.TenPacket;
|
||
|
multi_packet_max = Session.TenSize;
|
||
|
net = Ten;
|
||
|
}
|
||
|
#endif
|
||
|
#if(MPATH)
|
||
|
else if (Session.Type == GAME_MPATH) {
|
||
|
multi_packet_buf = Session.MPathPacket;
|
||
|
multi_packet_max = Session.MPathSize;
|
||
|
net = MPath;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Debug stuff
|
||
|
//------------------------------------------------------------------------
|
||
|
Init_Queue_Mono(net);
|
||
|
Update_Queue_Mono (net, 0);
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Compute the Game's CRC
|
||
|
//------------------------------------------------------------------------
|
||
|
Compute_Game_CRC();
|
||
|
CRC[Frame & 0x001f] = GameCRC;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 || Session.LoadGame) {
|
||
|
//.....................................................................
|
||
|
// Initialize static locals
|
||
|
//.....................................................................
|
||
|
for (i = 0; i < MAX_PLAYERS - 1; i++) {
|
||
|
their_frame[i] = -1;
|
||
|
their_sent[i] = 0;
|
||
|
their_recv[i] = 0;
|
||
|
}
|
||
|
my_sent = 0;
|
||
|
#ifdef FIXIT_MULTI_SAVE
|
||
|
skip_crc = 32;
|
||
|
#else
|
||
|
skip_crc = Frame + 32;
|
||
|
#endif // FIXIT_MULTI_SAVE
|
||
|
for (i = 0; i < 32; i++)
|
||
|
CRC[i] = 0;
|
||
|
|
||
|
//.....................................................................
|
||
|
// If we've loaded a saved game:
|
||
|
// - If this game was saved as the result of a lost connection, clear
|
||
|
// the CRC value so it will always match the other system's
|
||
|
// - Otherwise, use the GameCRC value, so we'll compare save-game files
|
||
|
// rather than scenario INI files
|
||
|
//.....................................................................
|
||
|
if (Session.LoadGame) {
|
||
|
if (Session.EmergencySave)
|
||
|
ScenarioCRC = 0;
|
||
|
else
|
||
|
ScenarioCRC = GameCRC;
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// 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) {
|
||
|
#ifdef WIN32
|
||
|
if (Session.Type == GAME_INTERNET){
|
||
|
Register_Game_End_Time();
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
|
||
|
if (rc == RC_NOT_RESPONDING) {
|
||
|
WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
|
||
|
}
|
||
|
else if (rc == RC_SCENARIO_MISMATCH) {
|
||
|
WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
|
||
|
}
|
||
|
else if (rc == RC_DOLIST_FULL) {
|
||
|
WWMessageBox().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 (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
Process_Send_Period(net);//, 1);
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// Turn off our special load-game flags
|
||
|
//.....................................................................
|
||
|
if (Session.LoadGame) {
|
||
|
Session.EmergencySave = false;
|
||
|
Session.LoadGame = false;
|
||
|
}
|
||
|
|
||
|
} // 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 (Session.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 (Session.Am_I_Master()) {
|
||
|
Generate_Real_Timing_Event(net, my_sent);
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// For the older protocols, do the old broken timing handling.
|
||
|
//
|
||
|
Generate_Timing_Event(net, my_sent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Only process every 'FrameSendRate' frames
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
if (!Process_Send_Period(net)) { //, 0)) {
|
||
|
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,
|
||
|
Session.MaxAhead, 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.
|
||
|
//------------------------------------------------------------------------
|
||
|
#ifdef FIXIT_VERSION_3
|
||
|
int iFramesyncTimeout;
|
||
|
if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 )
|
||
|
// Shortened resync timeout for non-2 player games.
|
||
|
iFramesyncTimeout = 5 * 60; // One minute.
|
||
|
else
|
||
|
iFramesyncTimeout = FRAMESYNC_TIMEOUT;
|
||
|
|
||
|
rc = Wait_For_Players (0, net,
|
||
|
(Session.MaxAhead << 3),
|
||
|
MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
|
||
|
iFramesyncTimeout * (2*timeout_factor),
|
||
|
multi_packet_buf, my_sent, their_frame,
|
||
|
their_sent, their_recv);
|
||
|
#else
|
||
|
rc = Wait_For_Players (0, net,
|
||
|
(Session.MaxAhead << 3),
|
||
|
MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ),
|
||
|
FRAMESYNC_TIMEOUT* (2*timeout_factor),
|
||
|
multi_packet_buf, my_sent, their_frame,
|
||
|
their_sent, their_recv);
|
||
|
#endif
|
||
|
|
||
|
if (rc != RC_NORMAL) {
|
||
|
#ifdef WIN32
|
||
|
if (Session.Type == GAME_INTERNET){
|
||
|
Register_Game_End_Time();
|
||
|
#ifdef WOLAPI_INTEGRATION
|
||
|
// New rule - if you cancel a waiting to reconnect dialog, you lose.
|
||
|
bReconnectDialogCancelled = ( rc == RC_CANCEL );
|
||
|
#endif
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
if (rc == RC_NOT_RESPONDING) {
|
||
|
WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
|
||
|
}
|
||
|
else if (rc == RC_SCENARIO_MISMATCH) {
|
||
|
WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
|
||
|
}
|
||
|
else if (rc == RC_DOLIST_FULL) {
|
||
|
WWMessageBox().Process(TXT_QUEUE_FULL);
|
||
|
}
|
||
|
Stop_Game();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Save the DoList to disk, if we're in "Record" mode
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Session.Record) {
|
||
|
Queue_Record();
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Execute the DoList; if an error occurs, bail out.
|
||
|
//------------------------------------------------------------------------
|
||
|
if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc,
|
||
|
their_frame, their_sent, their_recv)) {
|
||
|
#ifdef WIN32
|
||
|
if (Session.Type == GAME_INTERNET){
|
||
|
Register_Game_End_Time();
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
Stop_Game();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Clean out the DoList
|
||
|
//------------------------------------------------------------------------
|
||
|
Clean_DoList(net);
|
||
|
|
||
|
if (IsMono) {
|
||
|
MonoClass::Disable();
|
||
|
}
|
||
|
#endif
|
||
|
} // 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
|
||
|
int message_limit; // max # messages we'll read each frame
|
||
|
|
||
|
//........................................................................
|
||
|
// Variables used only if 'first_time':
|
||
|
//........................................................................
|
||
|
int num_ready; // # players signalling ready
|
||
|
|
||
|
//........................................................................
|
||
|
// Timing variables
|
||
|
//........................................................................
|
||
|
CDTimerClass<SystemTimerClass> retry_timer; // time between FRAMESYNC packet resends
|
||
|
CDTimerClass<SystemTimerClass> dialog_timer; // time to pop up a dialog
|
||
|
CDTimerClass<SystemTimerClass> 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 = resend_delta; // time to retry
|
||
|
dialog_timer = dialog_time; // time to show dlg
|
||
|
timeout_timer = timeout; // time to bail out
|
||
|
|
||
|
while (1) {
|
||
|
Keyboard->Check();
|
||
|
|
||
|
Update_Queue_Mono (net, 2);
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// Resend a frame-sync packet if longer than one propagation 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) {
|
||
|
retry_timer = resend_delta; // 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 && SpecialDialog==SDLG_NONE) {
|
||
|
if (reconnect_dlg == 0 && first_time == 0) {
|
||
|
FILE *fp;
|
||
|
int i;
|
||
|
HouseClass *housep;
|
||
|
|
||
|
fp = fopen("recon.txt","wt");
|
||
|
if (fp) {
|
||
|
fprintf(fp,"# Connections: %d\n",net->Num_Connections());
|
||
|
fprintf(fp," My Frame #: %d\n",Frame);
|
||
|
for (i = 0; i < net->Num_Connections(); i++) {
|
||
|
housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i)));
|
||
|
fprintf(fp,"%15s: Their Sent:%d Their Recv:%d Their Frame:%d\n",
|
||
|
housep->IniName, their_sent[i], their_recv[i], their_frame[i]);
|
||
|
}
|
||
|
fclose(fp);
|
||
|
}
|
||
|
|
||
|
#ifdef WOLAPI_INTEGRATION
|
||
|
// "Reconnecting" dialog is about to be shown.
|
||
|
// At this point, begin wolapi "disconnect pinging", if appropriate.
|
||
|
if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament )
|
||
|
pWolapi->Init_DisconnectPinging();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (Process_Reconnect_Dialog(&timeout_timer, their_frame, // (Returns immediately.)
|
||
|
net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) {
|
||
|
return (RC_CANCEL);
|
||
|
}
|
||
|
reconnect_dlg = 1;
|
||
|
|
||
|
#ifdef WOLAPI_INTEGRATION
|
||
|
// Continue wolapi "disconnect pinging", if appropriate.
|
||
|
if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging )
|
||
|
pWolapi->Pump_DisconnectPinging();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// Exit if too much time goes by (the other system has crashed or
|
||
|
// bailed)
|
||
|
//---------------------------------------------------------------------
|
||
|
if (!timeout_timer) {
|
||
|
//..................................................................
|
||
|
// 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 = resend_delta;
|
||
|
dialog_timer = dialog_time;
|
||
|
timeout_timer = timeout;
|
||
|
}
|
||
|
#ifdef FIXIT_MULTI_SAVE
|
||
|
#ifdef FIXIT_VERSION_3
|
||
|
else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) {
|
||
|
#else
|
||
|
else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) &&
|
||
|
PlayingAgainstVersion != VERSION_RED_ALERT_104) {
|
||
|
#endif
|
||
|
if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING,
|
||
|
TXT_YES, TXT_NO, TXT_NONE) == 0) {
|
||
|
Session.EmergencySave = 1;
|
||
|
//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
|
||
|
//Print_CRCs(NULL);
|
||
|
//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
|
||
|
// Scen.RandomNumber.Count1,
|
||
|
// Scen.RandomNumber.Count2,
|
||
|
// Scen.RandomNumber.Seed);
|
||
|
Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
|
||
|
//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
|
||
|
// Scen.RandomNumber.Count1,
|
||
|
// Scen.RandomNumber.Count2,
|
||
|
// Scen.RandomNumber.Seed);
|
||
|
Session.EmergencySave = 0;
|
||
|
}
|
||
|
return (RC_CANCEL);
|
||
|
}
|
||
|
#endif // FIXIT_MULTI_SAVE
|
||
|
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.
|
||
|
// (This feature is disabled for Ten; we need to keep the TCP buffers
|
||
|
// clear, so we read all the packets we can every time.)
|
||
|
//---------------------------------------------------------------------
|
||
|
messages_this_loop = 0;
|
||
|
message_limit = 5;
|
||
|
|
||
|
if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
|
||
|
message_limit = 9999;
|
||
|
}
|
||
|
|
||
|
while ( (messages_this_loop++ < message_limit) &&
|
||
|
net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
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 (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
|
||
|
rc = Process_Serial_Packet(multi_packet_buf, first_time);
|
||
|
//...............................................................
|
||
|
// SERIAL packet received & processed
|
||
|
//...............................................................
|
||
|
if (rc == RC_SERIAL_PROCESSED) {
|
||
|
net->Service();
|
||
|
retry_timer = resend_delta;
|
||
|
dialog_timer = dialog_time;
|
||
|
timeout_timer = timeout;
|
||
|
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) {
|
||
|
#ifdef FIXIT_MULTI_SAVE
|
||
|
#ifndef FIXIT_VERSION_3
|
||
|
if (PlayingAgainstVersion != VERSION_RED_ALERT_104){
|
||
|
#endif
|
||
|
if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP,
|
||
|
TXT_YES, TXT_NO, TXT_NONE) == 0) {
|
||
|
Session.EmergencySave = 1;
|
||
|
//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC);
|
||
|
//Print_CRCs(NULL);
|
||
|
//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n",
|
||
|
// Scen.RandomNumber.Count1,
|
||
|
// Scen.RandomNumber.Count2,
|
||
|
// Scen.RandomNumber.Seed);
|
||
|
Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
|
||
|
//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n",
|
||
|
// Scen.RandomNumber.Count1,
|
||
|
// Scen.RandomNumber.Count2,
|
||
|
// Scen.RandomNumber.Seed);
|
||
|
Session.EmergencySave = 0;
|
||
|
}
|
||
|
return (RC_CANCEL);
|
||
|
#ifndef FIXIT_VERSION_3
|
||
|
}else{
|
||
|
return (RC_NOT_RESPONDING);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#else
|
||
|
return (RC_NOT_RESPONDING);
|
||
|
#endif // FIXIT_MULTI_SAVE
|
||
|
}
|
||
|
//...............................................................
|
||
|
// 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, Session.MaxAhead, 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, Session.MaxAhead, 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) {
|
||
|
#ifdef WIN32
|
||
|
WWMouse->Erase_Mouse(&HidPage, TRUE);
|
||
|
#endif //WIN32
|
||
|
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;
|
||
|
|
||
|
//
|
||
|
// For now, TEN & MPATH don't measure the net's response time, so there's
|
||
|
// no point in adjusting our timing. Do nothing.
|
||
|
//
|
||
|
if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 (Session.Am_I_Master()) {
|
||
|
ev.Type = EventClass::RESPONSE_TIME;
|
||
|
//..................................................................
|
||
|
// For multi-frame compressed events, the MaxAhead must be an even
|
||
|
// multiple of the FrameSendRate.
|
||
|
//..................................................................
|
||
|
if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) +
|
||
|
(Session.FrameSendRate - 1)) / Session.FrameSendRate) *
|
||
|
Session.FrameSendRate), (Session.FrameSendRate * 2) );
|
||
|
}
|
||
|
//..................................................................
|
||
|
// For sending packets every frame, just use the 1-way connection
|
||
|
// response time.
|
||
|
//..................................................................
|
||
|
else {
|
||
|
if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
|
||
|
ev.Data.FrameInfo.Delay = max( (resp_time / 8),
|
||
|
MODEM_MIN_MAX_AHEAD );
|
||
|
} else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
|
||
|
ev.Data.FrameInfo.Delay = max( (resp_time / 8),
|
||
|
NETWORK_MIN_MAX_AHEAD );
|
||
|
}
|
||
|
else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
|
||
|
ev.Data.FrameInfo.Delay = max( (resp_time / 8),
|
||
|
MODEM_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 < Session.Players.Count(); i++) {
|
||
|
|
||
|
//
|
||
|
// If we haven't heard from all systems yet, bail out.
|
||
|
//
|
||
|
if (Session.Players[i]->Player.ProcessTime == -1) {
|
||
|
return;
|
||
|
}
|
||
|
if (Session.Players[i]->Player.ProcessTime > highest_ticks) {
|
||
|
highest_ticks = Session.Players[i]->Player.ProcessTime;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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) {
|
||
|
Session.DesiredFrameRate = 60;
|
||
|
} else {
|
||
|
Session.DesiredFrameRate = 60 / highest_ticks;
|
||
|
}
|
||
|
|
||
|
if (Options.GameSpeed == 0) {
|
||
|
specified_frame_rate = 60;
|
||
|
} else {
|
||
|
specified_frame_rate = 60 / Options.GameSpeed;
|
||
|
}
|
||
|
|
||
|
Session.DesiredFrameRate = MIN (Session.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 * Session.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 + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate;
|
||
|
maxahead = MAX (maxahead, (int)Session.FrameSendRate * 3);
|
||
|
|
||
|
ev.Type = EventClass::TIMING;
|
||
|
ev.Data.Timing.DesiredFrameRate = Session.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);
|
||
|
|
||
|
if (Session.Type == GAME_INTERNET) {
|
||
|
net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
|
||
|
}else{
|
||
|
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 (Session.Type == GAME_INTERNET) {
|
||
|
net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15);
|
||
|
}else{
|
||
|
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", Session.ProcessTicks,Session.ProcessFrames);
|
||
|
MonoClass::Disable();
|
||
|
}
|
||
|
|
||
|
avgticks = Session.ProcessTicks / Session.ProcessFrames;
|
||
|
|
||
|
ev.Type = EventClass::PROCESS_TIME;
|
||
|
ev.Data.ProcessTime.AverageTicks = avgticks;
|
||
|
OutList.Add(ev);
|
||
|
|
||
|
Session.ProcessTicks = 0;
|
||
|
Session.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) //, int init)
|
||
|
{
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 + (Session.FrameSendRate - 1)) /
|
||
|
Session.FrameSendRate) * Session.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 queued 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 * 64) - DoList.Count) {
|
||
|
cap = (MAX_EVENTS * 64) - DoList.Count;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 10/21/96 5:12PM - ST
|
||
|
//
|
||
|
if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){
|
||
|
cap = OutList.Count;
|
||
|
do_once = 0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Build our meta-packet & transmit it.
|
||
|
//------------------------------------------------------------------------
|
||
|
while (1) {
|
||
|
Keyboard->Check();
|
||
|
|
||
|
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
|
||
|
// Session.NumPlayers; no ACK is needed if we're just sending to someone
|
||
|
// who's left the game.
|
||
|
//.....................................................................
|
||
|
if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 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 (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) /
|
||
|
Session.FrameSendRate) * Session.FrameSendRate;
|
||
|
}
|
||
|
else {
|
||
|
packet.Frame = Frame + Session.MaxAhead;
|
||
|
}
|
||
|
packet.ID = PlayerPtr->ID;
|
||
|
packet.Data.FrameInfo.CRC = ScenarioCRC;
|
||
|
packet.Data.FrameInfo.CommandCount = cmd_count;
|
||
|
packet.Data.FrameInfo.Delay = Session.MaxAhead;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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]) {
|
||
|
|
||
|
if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) {
|
||
|
FILE *fp;
|
||
|
fp = fopen("badcount.txt","wt");
|
||
|
if (fp) {
|
||
|
fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]);
|
||
|
fprintf(fp,"Frame:%d ID:%d IsExec:%d\n",
|
||
|
event->Frame,
|
||
|
event->ID,
|
||
|
event->IsExecuted);
|
||
|
if (event->Type != EventClass::FRAMEINFO) {
|
||
|
fprintf(fp,"Wrong Event Type!\n");
|
||
|
} else {
|
||
|
fprintf(fp,"CRC:%x CommandCount:%d Delay:%d\n",
|
||
|
event->Data.FrameInfo.CRC,
|
||
|
event->Data.FrameInfo.CommandCount,
|
||
|
event->Data.FrameInfo.Delay);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
their_sent[index] = event->Data.FrameInfo.CommandCount;
|
||
|
}
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
if (event->Type == EventClass::FRAMESYNC) {
|
||
|
printf("(%d) Received FRAMESYNC: ", Frame);
|
||
|
} else {
|
||
|
printf("(%d) Received FRAMEINFO: ", Frame);
|
||
|
}
|
||
|
printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n",
|
||
|
event->Frame,
|
||
|
event->ID,
|
||
|
event->Data.FrameInfo.CRC,
|
||
|
event->Data.FrameInfo.CommandCount,
|
||
|
event->Data.FrameInfo.Delay);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 - 5/13/2019
|
||
|
SerialPacketType *serial_packet; // for parsing serial packets
|
||
|
int player_gone;
|
||
|
EventClass *event;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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->ScenarioInfo.Color, 0);
|
||
|
return (RC_PLAYER_LEFT);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Process an incoming message
|
||
|
//------------------------------------------------------------------------
|
||
|
if (serial_packet->Command == SERIAL_MESSAGE) {
|
||
|
if (!Session.Messages.Concat_Message(serial_packet->Name,
|
||
|
serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) {
|
||
|
#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Appears to do nothing
|
||
|
char *ptr = &serial_packet->Message.Message[0];
|
||
|
if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) {
|
||
|
Enable_Secret_Units();
|
||
|
}
|
||
|
#endif
|
||
|
Session.Messages.Add_Message (serial_packet->Name,
|
||
|
serial_packet->ID, serial_packet->Message.Message,
|
||
|
(PlayerColorType)serial_packet->ID,
|
||
|
TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE);
|
||
|
|
||
|
Sound_Effect(VOC_INCOMING_MESSAGE);
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// Save this message in our last-message buffer
|
||
|
//.....................................................................
|
||
|
if (strlen (serial_packet->Message.Message)) {
|
||
|
strcpy (Session.LastMessage, serial_packet->Message.Message);
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// Tell the map to do a partial update (just to force the
|
||
|
// messages to redraw).
|
||
|
//.....................................................................
|
||
|
//Map.Flag_To_Redraw(false);
|
||
|
Map.Flag_To_Redraw(true);
|
||
|
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) ||
|
||
|
(serial_packet->Command >= SERIAL_REQ_SCENARIO &&
|
||
|
serial_packet->Command <= SERIAL_NO_SCENARIO) ||
|
||
|
Session.NumPlayers == 1) {
|
||
|
return (RC_SERIAL_PROCESSED);
|
||
|
}
|
||
|
|
||
|
//........................................................................
|
||
|
// are we getting our own packets back??
|
||
|
//........................................................................
|
||
|
event = (EventClass *)multi_packet_buf;
|
||
|
|
||
|
if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED);
|
||
|
|
||
|
if (event->ID == PlayerPtr->ID) {
|
||
|
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 (Session.NumPlayers == 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 propagation 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(CDTimerClass<SystemTimerClass> *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 / 60;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 (Keyboard->Check()) {
|
||
|
if (Keyboard->Get() == 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 (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) {
|
||
|
if ( net->Num_Connections() ) {
|
||
|
if (!Reconnect_Modem()) {
|
||
|
#ifndef FIXIT_MULTI_SAVE
|
||
|
//...............................................................
|
||
|
// Set 'Session.EmergencySave', so when this game is loaded, we
|
||
|
// won't check the CRC of the game state (this system & the
|
||
|
// other may be on different frames, in which case the CRC
|
||
|
// won't match).
|
||
|
//...............................................................
|
||
|
Session.EmergencySave = 1;
|
||
|
//Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME));
|
||
|
Session.EmergencySave = 0;
|
||
|
#endif // FIXIT_MULTI_SAVE
|
||
|
return (0);
|
||
|
} else {
|
||
|
return (1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// For network, destroy the oldest connection
|
||
|
//------------------------------------------------------------------------
|
||
|
else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET ||
|
||
|
Session.Type == GAME_TEN || Session.Type == GAME_MPATH) {
|
||
|
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);
|
||
|
#ifdef WIN32
|
||
|
/*
|
||
|
** Send the game statistics packet now if the game is effectivly over
|
||
|
*/
|
||
|
if (Session.Players.Count() == 2 &&
|
||
|
Session.Type == GAME_INTERNET &&
|
||
|
!GameStatisticsPacketSent) {
|
||
|
Register_Game_End_Time();
|
||
|
ConnectionLost = true;
|
||
|
Send_Statistics_Packet(); // Disconnect, and I'll be the only one left.
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
|
||
|
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];
|
||
|
}
|
||
|
if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) {
|
||
|
Destroy_Connection(id,1);
|
||
|
}
|
||
|
#if(TEN)
|
||
|
else if (Session.Type == GAME_TEN) {
|
||
|
Destroy_TEN_Connection(id,1);
|
||
|
}
|
||
|
#endif
|
||
|
#if(MPATH)
|
||
|
else if (Session.Type == GAME_MPATH) {
|
||
|
Destroy_MPATH_Connection(id,1);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
Session.LoadGame = false;
|
||
|
Session.EmergencySave = false;
|
||
|
GameActive = 0;
|
||
|
if (IsMono) {
|
||
|
MonoClass::Disable();
|
||
|
}
|
||
|
#ifdef WIN32
|
||
|
if (Session.Type == GAME_INTERNET){
|
||
|
ConnectionLost = true;
|
||
|
Send_Statistics_Packet(); // Stop_Game()
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
|
||
|
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 (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) /
|
||
|
Session.FrameSendRate) * Session.FrameSendRate;
|
||
|
}
|
||
|
else {
|
||
|
finfo->Frame = Frame + frame_delay;
|
||
|
}
|
||
|
//........................................................................
|
||
|
// Fill in the rest of the event
|
||
|
//........................................................................
|
||
|
finfo->ID = PlayerPtr->ID;
|
||
|
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 (Session.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 (Session.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)) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
if (OutList.First().Type==EventClass::ADDPLAYER) {
|
||
|
ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size;
|
||
|
}
|
||
|
else {
|
||
|
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 = PlayerPtr->ID;
|
||
|
|
||
|
//.....................................................................
|
||
|
// 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);
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add(OutList.First());
|
||
|
#endif
|
||
|
|
||
|
//.....................................................................
|
||
|
// Add event to the send packet
|
||
|
//.....................................................................
|
||
|
if (OutList.First().Type==EventClass::ADDPLAYER) {
|
||
|
memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
|
||
|
size += sizeof(EventClass);
|
||
|
memcpy ( ((char *)buf) + size,
|
||
|
OutList.First().Data.Variable.Pointer,
|
||
|
OutList.First().Data.Variable.Size);
|
||
|
size += OutList.First().Data.Variable.Size;
|
||
|
}
|
||
|
else {
|
||
|
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));
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
printf("\n(%d) Building Send Packet\n", Frame);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Loop until there are no more events, we've processed our max # of
|
||
|
// events, or the buffer is full.
|
||
|
//------------------------------------------------------------------------
|
||
|
while (OutList.Count && (num < cap)) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
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.
|
||
|
// (The other data elements in the event, Frame, ID, etc, are stored
|
||
|
// in the packet header.)
|
||
|
//.....................................................................
|
||
|
if (eventtype==EventClass::ADDPLAYER) {
|
||
|
storedsize = datasize + sizeof (EventClass::EventType) +
|
||
|
OutList.First().Data.Variable.Size;
|
||
|
}
|
||
|
else {
|
||
|
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) {
|
||
|
#if (0)//PG
|
||
|
if (Debug_Print_Events) {
|
||
|
printf(" adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n",
|
||
|
OutList.First().Data.MegaMission.Whom.As_TARGET(),
|
||
|
OutList.First().Data.MegaMission.Whom,
|
||
|
MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission),
|
||
|
OutList.First().Data.MegaMission.Target.As_TARGET(),
|
||
|
OutList.First().Data.MegaMission.Target,
|
||
|
OutList.First().Data.MegaMission.Destination.As_TARGET(),
|
||
|
OutList.First().Data.MegaMission.Destination);
|
||
|
}
|
||
|
#endif
|
||
|
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 {
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
printf(" New MEGAMISSION run:\n");
|
||
|
}
|
||
|
|
||
|
*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) {
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
printf(" New MEGAMISSION run:\n");
|
||
|
}
|
||
|
|
||
|
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 (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
OutList.First().Frame = ((Frame + frame_delay +
|
||
|
(Session.FrameSendRate - 1)) / Session.FrameSendRate) *
|
||
|
Session.FrameSendRate;
|
||
|
}
|
||
|
else {
|
||
|
OutList.First().Frame = Frame + frame_delay;
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// Set the event's ID
|
||
|
//.....................................................................
|
||
|
OutList.First().ID = PlayerPtr->ID;
|
||
|
|
||
|
//.....................................................................
|
||
|
// 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;
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add(OutList.First());
|
||
|
#endif
|
||
|
|
||
|
//---------------------------------------------------------------------
|
||
|
// 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;
|
||
|
|
||
|
//..................................................................
|
||
|
// Variable-sized packets: Copy the packet Size & the buffer
|
||
|
//..................................................................
|
||
|
case (EventClass::ADDPLAYER):
|
||
|
*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
|
||
|
|
||
|
memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
|
||
|
&OutList.First().Data.Variable.Size, datasize );
|
||
|
size += (datasize + sizeof(EventClass::EventType));
|
||
|
|
||
|
memcpy ( ((char *)buf) + size,
|
||
|
OutList.First().Data.Variable.Pointer,
|
||
|
OutList.First().Data.Variable.Size);
|
||
|
size += OutList.First().Data.Variable.Size;
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
printf("\n");
|
||
|
}
|
||
|
|
||
|
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 (Session.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) ) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
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;
|
||
|
|
||
|
//..................................................................
|
||
|
// Special processing for variable-sized events
|
||
|
//..................................................................
|
||
|
if (event->Type == EventClass::ADDPLAYER) {
|
||
|
event->Data.Variable.Pointer = new char[event->Data.Variable.Size];
|
||
|
memcpy (event->Data.Variable.Pointer,
|
||
|
((char *)buf) + sizeof(EventClass),
|
||
|
event->Data.Variable.Size);
|
||
|
|
||
|
pos += event->Data.Variable.Size;
|
||
|
leftover -= event->Data.Variable.Size;
|
||
|
}
|
||
|
|
||
|
if (!DoList.Add( *event )) {
|
||
|
if (event->Type == EventClass::ADDPLAYER) {
|
||
|
delete [] event->Data.Variable.Pointer;
|
||
|
}
|
||
|
return (-1);
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add(*event);
|
||
|
#endif
|
||
|
|
||
|
//..................................................................
|
||
|
// 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
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 (leftover >= (datasize + (int)sizeof(EventClass::EventType)) ) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
//.....................................................................
|
||
|
// 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;
|
||
|
|
||
|
//...............................................................
|
||
|
// 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::ADDPLAYER):
|
||
|
|
||
|
memcpy ( &eventdata.Data.Variable.Size,
|
||
|
((char *)buf) + pos + sizeof(EventClass::EventType),
|
||
|
datasize );
|
||
|
|
||
|
eventdata.Data.Variable.Pointer =
|
||
|
new char[eventdata.Data.Variable.Size];
|
||
|
memcpy (eventdata.Data.Variable.Pointer,
|
||
|
((char *)buf) + pos + sizeof(EventClass::EventType) + datasize,
|
||
|
eventdata.Data.Variable.Size);
|
||
|
|
||
|
pos += eventdata.Data.Variable.Size;
|
||
|
leftover -= eventdata.Data.Variable.Size;
|
||
|
|
||
|
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) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
if ( !DoList.Add( eventdata ) ) {
|
||
|
return (-1);
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add( eventdata );
|
||
|
#endif
|
||
|
|
||
|
//......................................................
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
if ( !DoList.Add( eventdata ) ) {
|
||
|
if (eventdata.Type == EventClass::ADDPLAYER) {
|
||
|
delete [] eventdata.Data.Variable.Pointer;
|
||
|
}
|
||
|
return (-1);
|
||
|
}
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add( eventdata );
|
||
|
#endif
|
||
|
|
||
|
//..................................................................
|
||
|
// 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
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* 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 max_houses, HousesType base_house,
|
||
|
ConnManClass *net, CDTimerClass<FrameTimerClass> *skip_crc,
|
||
|
long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
|
||
|
{
|
||
|
HousesType house;
|
||
|
HouseClass *hptr;
|
||
|
int i,j,k;
|
||
|
int index;
|
||
|
int check_crc;
|
||
|
|
||
|
Check_Mirror();
|
||
|
|
||
|
#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.
|
||
|
//
|
||
|
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;
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList[j].Frame = NewMaxAheadFrame2;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Execute the DoList. Events must be executed in the same order on all
|
||
|
// systems; so, execute them in the order of the HouseClass array. This
|
||
|
// array is stored in the same order on all systems.
|
||
|
//------------------------------------------------------------------------
|
||
|
for (i = 0; i < max_houses; i++) {
|
||
|
//.....................................................................
|
||
|
// Convert our index into a HousesType value
|
||
|
//.....................................................................
|
||
|
house = (HousesType)(i + base_house);
|
||
|
hptr = 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 (!hptr) {
|
||
|
continue;
|
||
|
}
|
||
|
if (!hptr->IsHuman) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
//.....................................................................
|
||
|
// Loop through all events
|
||
|
//.....................................................................
|
||
|
for (j = 0; j < DoList.Count; j++) {
|
||
|
|
||
|
if (net)
|
||
|
Update_Queue_Mono (net, 6);
|
||
|
|
||
|
//..................................................................
|
||
|
// If this event was from the currently-executing player ID, and it's
|
||
|
// time to execute it, execute it.
|
||
|
//..................................................................
|
||
|
if (DoList[j].ID == hptr->ID && (unsigned) Frame >= DoList[j].Frame &&
|
||
|
!DoList[j].IsExecuted) {
|
||
|
|
||
|
//...............................................................
|
||
|
// Error if it's too late to execute this packet!
|
||
|
// (Hack: disable this check for solo or skirmish mode.)
|
||
|
//...............................................................
|
||
|
if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type !=
|
||
|
EventClass::FRAMEINFO && Session.Type != GAME_NORMAL &&
|
||
|
Session.Type != GAME_SKIRMISH) {
|
||
|
|
||
|
#if(TEN)
|
||
|
Send_TEN_Packet_Too_Late();
|
||
|
#endif // TEN
|
||
|
|
||
|
#if(MPATH)
|
||
|
//Send_MPATH_Packet_Too_Late();
|
||
|
#endif // MPATH
|
||
|
|
||
|
Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame,
|
||
|
their_sent, their_recv);
|
||
|
WWMessageBox().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) {
|
||
|
|
||
|
#ifdef WIN32
|
||
|
if (DoList[j].Type==EventClass::EXIT) {
|
||
|
/*
|
||
|
** Flag that this house lost because it quit.
|
||
|
*/
|
||
|
HousesType quithouse;
|
||
|
HouseClass *quithptr;
|
||
|
|
||
|
for (int player = 0; player < max_houses ; player++) {
|
||
|
quithouse = (HousesType)(player + base_house);
|
||
|
quithptr = HouseClass::As_Pointer(quithouse);
|
||
|
if (!quithptr) {
|
||
|
continue;
|
||
|
}
|
||
|
if (quithptr->ID == DoList[j].ID) {
|
||
|
quithptr->IsGiverUpper = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Send the game statistics packet now since the game is effectivly over
|
||
|
*/
|
||
|
if (Session.Players.Count() == 2 &&
|
||
|
Session.Type == GAME_INTERNET &&
|
||
|
!GameStatisticsPacketSent) {
|
||
|
Register_Game_End_Time();
|
||
|
Send_Statistics_Packet(); // Event - player aborted, and there were only 2 left.
|
||
|
}
|
||
|
}
|
||
|
#endif //WIN32
|
||
|
|
||
|
if (Debug_Print_Events) {
|
||
|
if (DoList[j].Type==EventClass::EXIT) {
|
||
|
printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n",
|
||
|
Frame,
|
||
|
DoList[j].ID,
|
||
|
(HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName,
|
||
|
DoList[j].Frame);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (DoList[j].ID == PlayerPtr->ID) {
|
||
|
DoList[j].Execute();
|
||
|
} else if (DoList[j].Type==EventClass::EXIT) {
|
||
|
//............................................................
|
||
|
// If this EXIT event isn't from myself, destroy the connection
|
||
|
// for that player. The HousesType for this event is the
|
||
|
// connection ID.
|
||
|
//............................................................
|
||
|
if (Session.Type == GAME_MODEM ||
|
||
|
Session.Type == GAME_NULL_MODEM) {
|
||
|
//PG Destroy_Null_Connection( house, 0 );
|
||
|
} else if ((Session.Type == GAME_IPX ||
|
||
|
Session.Type == GAME_INTERNET ||
|
||
|
Session.Type == GAME_TEN ||
|
||
|
Session.Type == GAME_MPATH) && net) {
|
||
|
index = net->Connection_Index (house);
|
||
|
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];
|
||
|
}
|
||
|
if (Session.Type == GAME_IPX ||
|
||
|
Session.Type == GAME_INTERNET) {
|
||
|
Destroy_Connection(house,0);
|
||
|
}
|
||
|
#if(TEN)
|
||
|
else if (Session.Type == GAME_TEN) {
|
||
|
Destroy_TEN_Connection(house,0);
|
||
|
}
|
||
|
#endif // TEN
|
||
|
#if(MPATH)
|
||
|
else if (Session.Type == GAME_MPATH) {
|
||
|
Destroy_MPATH_Connection(house,0);
|
||
|
}
|
||
|
#endif // MPATH
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Special case for recording playback: turn the house over
|
||
|
// to the computer.
|
||
|
//
|
||
|
if (Session.Play && DoList[j].Type==EventClass::EXIT) {
|
||
|
hptr->IsHuman = false;
|
||
|
hptr->IQ = Rule.MaxIQ;
|
||
|
hptr->Computer_Paranoid();
|
||
|
strcpy (hptr->IniName,Text_String(TXT_COMPUTER));
|
||
|
Session.NumPlayers--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//...............................................................
|
||
|
// For a FRAMEINFO event, check the CRC value.
|
||
|
//...............................................................
|
||
|
else if (DoList[j].Type == EventClass::FRAMEINFO) {
|
||
|
//............................................................
|
||
|
// Skip the CRC check if we're less than 32 frames into the game;
|
||
|
// this will prevent a newly-loaded modem game from instantly
|
||
|
// going out of sync, if the games were saved at different
|
||
|
// frame numbers.
|
||
|
//............................................................
|
||
|
if (!skip_crc || *skip_crc == 0) {
|
||
|
check_crc = 1;
|
||
|
}
|
||
|
else {
|
||
|
check_crc = 0;
|
||
|
}
|
||
|
if (check_crc
|
||
|
&& 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(TEN)
|
||
|
Send_TEN_Out_Of_Sync();
|
||
|
#endif // TEN
|
||
|
|
||
|
#if(MPATH)
|
||
|
//Send_MPATH_Out_Of_Sync();
|
||
|
#endif // MPATH
|
||
|
#if (0)//PG
|
||
|
if (WWMessageBox().Process (TXT_OUT_OF_SYNC,
|
||
|
TXT_CONTINUE, TXT_STOP) == 0) {
|
||
|
if (Session.Type == GAME_MODEM ||
|
||
|
Session.Type == GAME_NULL_MODEM) {
|
||
|
//PG Destroy_Null_Connection( house, -1 );
|
||
|
Shutdown_Modem();
|
||
|
Session.Type = GAME_NORMAL;
|
||
|
}
|
||
|
else if ((Session.Type == GAME_IPX ||
|
||
|
Session.Type == GAME_INTERNET) && net) {
|
||
|
while (net->Num_Connections()) {
|
||
|
Keyboard->Check();
|
||
|
Destroy_Connection (net->Connection_ID(0), -1);
|
||
|
}
|
||
|
}
|
||
|
#if(TEN)
|
||
|
else if (Session.Type == GAME_TEN && net) {
|
||
|
while (net->Num_Connections()) {
|
||
|
Destroy_TEN_Connection (net->Connection_ID(0), -1);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
#if(MPATH)
|
||
|
else if (Session.Type == GAME_MPATH && net) {
|
||
|
while (net->Num_Connections()) {
|
||
|
Destroy_MPATH_Connection (net->Connection_ID(0), -1);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
Map.Flag_To_Redraw(true);
|
||
|
}
|
||
|
else {
|
||
|
return (0);
|
||
|
}
|
||
|
#endif
|
||
|
return (1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//...............................................................
|
||
|
// Execute other commands
|
||
|
//...............................................................
|
||
|
else {
|
||
|
DoList[j].Execute();
|
||
|
}
|
||
|
|
||
|
//...............................................................
|
||
|
// Mark this event as executed.
|
||
|
//...............................................................
|
||
|
DoList[j].IsExecuted = 1;
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList[j].IsExecuted = 1;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
|
||
|
Keyboard->Check();
|
||
|
|
||
|
if (net)
|
||
|
Update_Queue_Mono (net, 7);
|
||
|
|
||
|
//.....................................................................
|
||
|
// 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();
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Next();
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // end of Clean_DoList
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* 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.
|
||
|
//------------------------------------------------------------------------
|
||
|
Session.RecordFile.Write (&j,sizeof(j));
|
||
|
for (i = 0; i < DoList.Count; i++) {
|
||
|
if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
|
||
|
Session.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 (Keyboard->Check()) {
|
||
|
key = Keyboard->Get();
|
||
|
if (key == KA_ESC || Session.Attract) {
|
||
|
GameActive = 0;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// If we're in "Attract" mode, and the user moves the mouse, stop the
|
||
|
// playback.
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Session.Attract && 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;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// If we've reached the CRC print frame, do so & exit
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Frame >= Session.TrapPrintCRC) {
|
||
|
Print_CRCs(NULL);
|
||
|
Prog_End("Queue_Playback reached CRC print frame", true);
|
||
|
Emergency_Exit(0);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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 && Session.Type!=GAME_NORMAL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Only process every 'FrameSendRate' frames
|
||
|
//------------------------------------------------------------------------
|
||
|
testframe = ((Frame + (Session.FrameSendRate - 1)) /
|
||
|
Session.FrameSendRate) * Session.FrameSendRate;
|
||
|
if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) &&
|
||
|
Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
|
||
|
if (Frame != testframe) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Read the DoList from disk
|
||
|
//------------------------------------------------------------------------
|
||
|
ok = 1;
|
||
|
if (Session.RecordFile.Read (&numevents, sizeof(numevents)) ==
|
||
|
sizeof(numevents)) {
|
||
|
for (i = 0; i < numevents; i++) {
|
||
|
if (Session.RecordFile.Read (&event, sizeof(EventClass)) ==
|
||
|
sizeof(EventClass)) {
|
||
|
event.IsExecuted = 0;
|
||
|
DoList.Add (event);
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
MirrorList.Add(event);
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
ok = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
ok = 0;
|
||
|
}
|
||
|
|
||
|
if (!ok) {
|
||
|
GameActive = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Execute the DoList; if an error occurs, bail out.
|
||
|
//------------------------------------------------------------------------
|
||
|
if (Session.Type == GAME_NORMAL) {
|
||
|
max_houses = 1;
|
||
|
base_house = PlayerPtr->Class->House;
|
||
|
}
|
||
|
else {
|
||
|
max_houses = Session.MaxPlayers;
|
||
|
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,j;
|
||
|
VesselClass *vessp;
|
||
|
InfantryClass *infp;
|
||
|
UnitClass *unitp;
|
||
|
BuildingClass *bldgp;
|
||
|
ObjectClass *objp;
|
||
|
HouseClass *housep;
|
||
|
|
||
|
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);
|
||
|
Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
|
||
|
Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Shippies
|
||
|
//------------------------------------------------------------------------
|
||
|
for (i = 0; i < Vessels.Count(); i++) {
|
||
|
vessp = (VesselClass *)Vessels.Active_Ptr(i);
|
||
|
Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing);
|
||
|
Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom);
|
||
|
Add_CRC (&GameCRC, (int)vessp->Strength);
|
||
|
Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Buildings
|
||
|
//------------------------------------------------------------------------
|
||
|
for (i = 0; i < Buildings.Count(); i++) {
|
||
|
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
|
||
|
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Houses
|
||
|
//------------------------------------------------------------------------
|
||
|
for (i = 0; i < Houses.Count(); i++) {
|
||
|
housep = (HouseClass *)Houses.Active_Ptr(i);
|
||
|
Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
|
||
|
(int)housep->Drain);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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());
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// A random #
|
||
|
//------------------------------------------------------------------------
|
||
|
// Add_CRC(&GameCRC, Scen.RandomNumber.Seed);
|
||
|
Add_CRC(&GameCRC, Scen.RandomNumber);
|
||
|
|
||
|
} /* 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: *
|
||
|
* ev -- event to display *
|
||
|
* *
|
||
|
* OUTPUT: *
|
||
|
* none *
|
||
|
* *
|
||
|
* WARNINGS: *
|
||
|
* none *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 05/09/1995 BRR : Created. *
|
||
|
*=========================================================================*/
|
||
|
static void Print_CRCs(EventClass *ev)
|
||
|
{
|
||
|
int i,j;
|
||
|
InfantryClass *infp;
|
||
|
UnitClass *unitp;
|
||
|
VesselClass *vesselp;
|
||
|
BuildingClass *bldgp;
|
||
|
ObjectClass *objp;
|
||
|
FILE *fp;
|
||
|
HouseClass *housep;
|
||
|
HousesType house;
|
||
|
int color;
|
||
|
|
||
|
Mono_Clear_Screen();
|
||
|
Mono_Set_Cursor (0,0);
|
||
|
fp = fopen("OUT.TXT","wt");
|
||
|
if (fp==NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 32; i++) {
|
||
|
fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Houses
|
||
|
//
|
||
|
for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
|
||
|
GameCRC = 0;
|
||
|
housep = HouseClass::As_Pointer (house);
|
||
|
if (housep) {
|
||
|
HousesType actlike = housep->ActLike;
|
||
|
color = housep->RemapColor;
|
||
|
fprintf(fp,"%s: IsHuman:%d Color:%s ID:%d ActLike:%s\n",
|
||
|
housep->IniName,
|
||
|
housep->IsHuman,
|
||
|
ColorNames[color],
|
||
|
housep->ID,
|
||
|
HouseClass::As_Pointer(actlike)->Class->Name());
|
||
|
Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power +
|
||
|
(int)housep->Drain);
|
||
|
Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Infantry
|
||
|
//
|
||
|
for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
|
||
|
housep = HouseClass::As_Pointer (house);
|
||
|
if (housep) {
|
||
|
GameCRC = 0;
|
||
|
fprintf(fp,"-------------------- %s Infantry -------------------\n",
|
||
|
housep->Class->Name());
|
||
|
for (i = 0; i < Infantry.Count(); i++) {
|
||
|
infp = (InfantryClass *)Infantry.Active_Ptr(i);
|
||
|
if (infp->Owner()==house) {
|
||
|
Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
|
||
|
Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom);
|
||
|
Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom);
|
||
|
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x Speed:%d NavCom:%x\n",
|
||
|
infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
|
||
|
infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom);
|
||
|
}
|
||
|
}
|
||
|
Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Units
|
||
|
//
|
||
|
for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
|
||
|
housep = HouseClass::As_Pointer (house);
|
||
|
if (housep) {
|
||
|
GameCRC = 0;
|
||
|
fprintf(fp,"-------------------- %s Units -------------------\n",
|
||
|
housep->Class->Name());
|
||
|
for (i = 0; i < Units.Count(); i++) {
|
||
|
unitp = (UnitClass *)Units.Active_Ptr(i);
|
||
|
if (unitp->Owner()==house) {
|
||
|
Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
|
||
|
(int)unitp->SecondaryFacing);
|
||
|
fprintf(fp,
|
||
|
"COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d Tgt:%x\n",
|
||
|
unitp->Coord,(int)unitp->PrimaryFacing,
|
||
|
(int)unitp->SecondaryFacing,unitp->Get_Mission(),
|
||
|
unitp->Class->Type, unitp->As_Target());
|
||
|
}
|
||
|
}
|
||
|
Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Vessels
|
||
|
//
|
||
|
for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
|
||
|
housep = HouseClass::As_Pointer (house);
|
||
|
if (housep) {
|
||
|
GameCRC = 0;
|
||
|
fprintf(fp,"-------------------- %s Vessels -------------------\n",
|
||
|
housep->Class->Name());
|
||
|
for (i = 0; i < Vessels.Count(); i++) {
|
||
|
vesselp = (VesselClass *)Vessels.Active_Ptr(i);
|
||
|
if (vesselp->Owner()==house) {
|
||
|
Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing);
|
||
|
Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom);
|
||
|
Add_CRC (&GameCRC, (int)vesselp->Strength);
|
||
|
Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom);
|
||
|
fprintf(fp,
|
||
|
"COORD:%x Facing:%d Mission:%d Strength:%d Type:%d Tgt:%x\n",
|
||
|
vesselp->Coord,(int)vesselp->PrimaryFacing,
|
||
|
vesselp->Get_Mission(), vesselp->Strength,
|
||
|
vesselp->Class->Type, vesselp->As_Target());
|
||
|
}
|
||
|
}
|
||
|
Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Buildings
|
||
|
//
|
||
|
for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) {
|
||
|
housep = HouseClass::As_Pointer (house);
|
||
|
if (housep) {
|
||
|
GameCRC = 0;
|
||
|
fprintf(fp,"-------------------- %s Buildings -------------------\n",
|
||
|
housep->Class->Name());
|
||
|
for (i = 0; i < Buildings.Count(); i++) {
|
||
|
bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
|
||
|
if (bldgp->Owner()==house) {
|
||
|
Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
|
||
|
fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x\n",
|
||
|
bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
|
||
|
bldgp->Class->Type, bldgp->As_Target());
|
||
|
}
|
||
|
}
|
||
|
Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Animations
|
||
|
//
|
||
|
AnimClass *animp;
|
||
|
fprintf(fp,"-------------------- Animations -------------------\n");
|
||
|
for (i = 0; i < Anims.Count(); i++) {
|
||
|
animp = (AnimClass *)Anims.Active_Ptr(i);
|
||
|
fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n",
|
||
|
animp->xObject,
|
||
|
animp->OwnerHouse,
|
||
|
animp->Loops);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// 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)));
|
||
|
else if (objp->What_Am_I() == RTTI_VESSEL)
|
||
|
fprintf(fp,"Vessel (Type:%d) ",
|
||
|
(VesselType)(*((VesselClass *)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:%x \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:%x \n",GameCRC);
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Random # generator, frame #
|
||
|
//------------------------------------------------------------------------
|
||
|
Mono_Printf("Random Number:%x \n",Scen.RandomNumber.Seed);
|
||
|
#ifdef RANDOM_COUNT
|
||
|
fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n",
|
||
|
Scen.RandomNumber.Seed,
|
||
|
Scen.RandomNumber.Count1,
|
||
|
Scen.RandomNumber.Count2);
|
||
|
#else
|
||
|
fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed);
|
||
|
#endif
|
||
|
|
||
|
Mono_Printf("My Frame:%d \n",Frame);
|
||
|
fprintf(fp,"My Frame:%d\n",Frame);
|
||
|
|
||
|
if (ev) {
|
||
|
fprintf(fp,"\n");
|
||
|
fprintf(fp,"Offending event:\n");
|
||
|
fprintf(fp," Type: %d\n",ev->Type);
|
||
|
fprintf(fp," Frame: %d\n",ev->Frame);
|
||
|
fprintf(fp," ID: %x\n",ev->ID);
|
||
|
fprintf(fp," CRC: %x\n",ev->Data.FrameInfo.CRC);
|
||
|
fprintf(fp," CommandCount: %d\n",ev->Data.FrameInfo.CommandCount);
|
||
|
fprintf(fp," Delay: %d\n",ev->Data.FrameInfo.Delay);
|
||
|
}
|
||
|
|
||
|
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 || Session.LoadGame) && 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 || Session.LoadGame || NewMonoMode) {
|
||
|
net->Configure_Debug (0, sizeof (CommHeaderType),
|
||
|
sizeof(EventClass::EventType), EventClass::EventNames, 0, 27);
|
||
|
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(" Session.MaxAhead:\n");
|
||
|
Mono_Printf(" their_recv:\n");
|
||
|
Mono_Printf(" their_sent:\n");
|
||
|
Mono_Printf(" my_sent:\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);
|
||
|
}
|
||
|
#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, ConnManClass *net,
|
||
|
long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
int i;
|
||
|
HousesType house;
|
||
|
|
||
|
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);
|
||
|
|
||
|
for (i = 0; i < Session.Players.Count(); i++) {
|
||
|
if (event->ID == Session.Players[i]->Player.ID) {
|
||
|
fprintf(fp,"Player's Name: %s",Session.Players[i]->Name);
|
||
|
}
|
||
|
}
|
||
|
fprintf(fp,"\n");
|
||
|
|
||
|
fprintf(fp,"--------------------- My data: ---------------------\n");
|
||
|
fprintf(fp,"My Frame:%d\n",Frame);
|
||
|
fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead);
|
||
|
|
||
|
if (net) {
|
||
|
fprintf(fp,"-------------------- Frame Stats: ------------------\n");
|
||
|
fprintf(fp,"Name ID TheirFrame TheirSent TheirRecv\n");
|
||
|
for (i = 0; i < net->Num_Connections(); i++) {
|
||
|
house = (HousesType)(net->Connection_ID(i));
|
||
|
fprintf(fp,"%12s %2d %6d %6d %6d\n",
|
||
|
(HouseClass::As_Pointer(house))->IniName,
|
||
|
net->Connection_ID(i),
|
||
|
their_frame[i],
|
||
|
their_sent[i],
|
||
|
their_recv[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fclose(fp);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Check_Mirror -- Checks mirror memory *
|
||
|
* *
|
||
|
* INPUT: *
|
||
|
* none. *
|
||
|
* *
|
||
|
* OUTPUT: *
|
||
|
* none. *
|
||
|
* *
|
||
|
* WARNINGS: *
|
||
|
* none. *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 10/14/1996 BRR : Created. *
|
||
|
*=========================================================================*/
|
||
|
void Check_Mirror(void)
|
||
|
{
|
||
|
#ifdef MIRROR_QUEUE
|
||
|
int i;
|
||
|
char txt[80];
|
||
|
unsigned long *ptr;
|
||
|
int found_5s = 0;
|
||
|
|
||
|
ptr = (unsigned long *)(DoList.Get_Array());
|
||
|
for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
|
||
|
sizeof(unsigned long); i++) {
|
||
|
if (ptr[i] == 0x55555555) {
|
||
|
sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i]));
|
||
|
WWMessageBox().Process (txt);
|
||
|
found_5s = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ptr = (unsigned long *)(MirrorList.Get_Array());
|
||
|
for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
|
||
|
sizeof(unsigned long); i++) {
|
||
|
if (ptr[i] == 0x55555555) {
|
||
|
sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i]));
|
||
|
WWMessageBox().Process (txt);
|
||
|
found_5s = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ptr = (unsigned long *)(DoList.Get_Array());
|
||
|
for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
|
||
|
sizeof(unsigned long); i++) {
|
||
|
if (ptr[i] == 0xAAAAAAAA) {
|
||
|
sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i]));
|
||
|
WWMessageBox().Process (txt);
|
||
|
found_5s = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ptr = (unsigned long *)(MirrorList.Get_Array());
|
||
|
for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) /
|
||
|
sizeof(unsigned long); i++) {
|
||
|
if (ptr[i] == 0xAAAAAAAA) {
|
||
|
sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i]));
|
||
|
WWMessageBox().Process (txt);
|
||
|
found_5s = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < DoList.Count; i++) {
|
||
|
if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) {
|
||
|
sprintf(txt,"Queue Memory Trashed! Head:%d Tail:%d, Addr:%p or %p",
|
||
|
DoList.Get_Head(),
|
||
|
DoList.Get_Tail(),
|
||
|
DoList.Get_Array() + i,
|
||
|
MirrorList.Get_Array() + i);
|
||
|
WWMessageBox().Process (txt);
|
||
|
Prog_End("Check_Mirror", true);
|
||
|
Emergency_Exit(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (found_5s) {
|
||
|
//Prog_End();
|
||
|
Emergency_Exit(0);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
} // end of Check_Mirror
|
||
|
|
||
|
|
||
|
/*************************** end of queue.cpp ******************************/
|