/*============================================================================= Name : CommandNetwork.c Purpose : Handles all of the packing/unpacking sending/receiving of Command packets and sync packets Created 7/30/1997 by gshaw Copyright Relic Entertainment, Inc. All rights reserved. =============================================================================*/ #include #include #include "switches.h" #include "types.h" #include "debug.h" #include "memory.h" #include "task.h" #include "queue.h" #include "universe.h" #include "univupdate.h" #include "CommandLayer.h" #include "netcheck.h" #include "globals.h" #include "utility.h" #include "CommandNetwork.h" #include "TitanInterfaceC.h" #include "HorseRace.h" #include "chatting.h" #include "mainswitches.h" #include "Captaincy.h" #include "titan.h" #include "TimeoutTimer.h" #include "titannet.h" #include "multiplayergame.h" // for mutex stuff #include "autodownloadmap.h" #include "LagPrint.h" #include "sensors.h" /*============================================================================= Private Defines: =============================================================================*/ #define HIGH_WATER_MARK 10 /*============================================================================= Private Variables: =============================================================================*/ udword syncPacketBeforeGameStarted = 0; udword packetBeforeGameStarted = 0; udword unknownPackets = 0; udword printedUnknownPackets = 0; udword sentPacketNumber = 0; udword receivedPacketNumber = 0; udword syncpkts = 0; udword cmdpkts = 0; udword syncoverruns = 0; udword cmdoverruns = 0; // Keep Alive variables #define ALIVESTATUS_ALIVE 0 #define ALIVESTATUS_QUERYINGDROPOUT 1 #define ALIVESTATUS_WAITQUERYDROPOUTLATER 2 #define ALIVESTATUS_DROPPEDOUT 3 #define KILLDROPPEDOUTPLAYER_VERIFY 0xfe389bca TTimer SendIAmAliveTimer; TTimer SendAliveStatusTimer; void *AliveTimeoutStatusesMutex = NULL; TTimer AliveTimeoutTimers[MAX_MULTIPLAYER_PLAYERS]; ubyte AliveStatuses[MAX_MULTIPLAYER_PLAYERS]; bool8 HaveKilledPlayerDueToDropout[MAX_MULTIPLAYER_PLAYERS]; bool KeepAliveCalledFirstTime = FALSE; // variables for printing out the message when a player has dropped out udword numPlayerDropped = 0; udword playersDropped[MAX_MULTIPLAYER_PLAYERS]; real32 printTimeout = 0.0f; /*============================================================================= Private functions: =============================================================================*/ void KeepAliveStartTimers(); void receivedIAmAlivePacket(HWPacketHeader *packet,udword sizeofPacket); void receivedAliveStatusPacket(AliveStatusPacket *packet,udword sizeofPacket); static void SendCaptainInfoPacket(void); void BroadCastHorseRacePacketFromCaptain(ubyte *packet, udword sizeofpacket); void BroadCastChatPacketFromCapatain(ChatPacket *packet, udword sizeofpacket); void clPlayerDropped(udword playerMask,udword verify); void clSendPlayerDropped(udword playerDroppedMask); /*============================================================================= Functions: =============================================================================*/ /*----------------------------------------------------------------------------- Name : CommandNetworkReset Description : resets the CommandNetwork Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void CommandNetworkReset(void) { sdword i; syncPacketBeforeGameStarted = 0; packetBeforeGameStarted = 0; unknownPackets = 0; printedUnknownPackets = 0; sentPacketNumber = 0; receivedPacketNumber = 0; syncpkts = 0; cmdpkts = 0; syncoverruns = 0; cmdoverruns = 0; printCaptainMessage = FALSE; numPlayerDropped = 0; for (i=0;itype == PACKETTYPE_SYNC); LockQueue(&ProcessSyncPktQ); Enqueue(&ProcessSyncPktQ,packet,sizeofPacket); UnLockQueue(&ProcessSyncPktQ); } /*----------------------------------------------------------------------------- Name : ReceivedCmdPacketCB Description : callback function which is called whenever a command packet is received. Inputs : packet, sizeofPacket Outputs : Return : ----------------------------------------------------------------------------*/ void ReceivedCmdPacketCB(ubyte *packet,udword sizeofPacket) { dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_COMMAND); if (IAmCaptain) { LockQueue(&ProcessCmdPktQ); Enqueue(&ProcessCmdPktQ,packet,sizeofPacket); UnLockQueue(&ProcessCmdPktQ); } // else throw packet away } /*----------------------------------------------------------------------------- Name : ReceivedSyncPacketCB Description : callback function which is called whenever a sync packet is received. Inputs : packet, sizeofPacket Outputs : Return : ----------------------------------------------------------------------------*/ void ReceivedRequestedSyncPacketCB(ubyte *packet,udword sizeofPacket) { dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_REQUESTEDSYNC); LockQueue(&ProcessRequestedSyncPktQ); Enqueue(&ProcessRequestedSyncPktQ,packet,sizeofPacket); UnLockQueue(&ProcessRequestedSyncPktQ); } void ReceivedRequestSyncPacketsPacketCB(ubyte *packet,udword sizeofPacket) { udword frompacketnum; udword topacketnum; udword i; HWPacketHeader *sendpacket; udword sendpacketsize; bool gotit; uword sendto; dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_REQUESTSYNCPKTS); frompacketnum = ((RequestSyncPacketsPacket *)packet)->packetheader.frame; topacketnum = ((RequestSyncPacketsPacket *)packet)->topacketnum; sendto = ((RequestSyncPacketsPacket *)packet)->packetheader.from; dbgAssert(frompacketnum <= topacketnum); for (i=frompacketnum;i<=topacketnum;i++) { gotit = GetSyncPktFromLastSyncPktsQ(i,&sendpacket,&sendpacketsize); if (gotit) { sendpacket->from = sigsPlayerIndex; // same sync pkt as before, but from me this time sendpacket->type = PACKETTYPE_REQUESTEDSYNC; // and this also now a requested sync pkt titanSendPointMessage(sendto,(ubyte *)sendpacket,sendpacketsize); } else { if (i == topacketnum) // always have to send the last packet requested for it to continue { HWPacketHeader fakelastsyncpkt; fakelastsyncpkt.type = PACKETTYPE_REQUESTEDSYNC; fakelastsyncpkt.from = sigsPlayerIndex; fakelastsyncpkt.frame = (udword)-1; fakelastsyncpkt.numberOfCommands = 0; titanSendPointMessage(sendto,(ubyte *)&fakelastsyncpkt,sizeof(HWPacketHeader)); } titanDebug("Warning: Do not have sync pkt %d to send by request to %d\n",i,sendto); } } } /*----------------------------------------------------------------------------- Name : ReceivedTransferCaptaincyPacketCB Description : callback function which is called whenever a captaincy packet is received. Inputs : packet, sizeofPacket Outputs : Return : ----------------------------------------------------------------------------*/ void ReceivedTransferCaptaincyPacketCB(ubyte *packet,udword sizeofPacket) { dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_TRANSFERCAPTAINCY); LockQueue(&ProcessCaptaincyPktQ); Enqueue(&ProcessCaptaincyPktQ,packet,sizeofPacket); UnLockQueue(&ProcessCaptaincyPktQ); } bool checkPlayersReady(void) { sdword i; bool ready = TRUE; dbgAssert((IAmCaptain) && (!multiPlayerGameUnderWay)); for (i=0;itype == PACKETTYPE_SYNC) { syncPacketBeforeGameStarted++; } else if (((HWPacketHeader *)packet)->type == PACKETTYPE_CHAT) { if (IAmCaptain) { BroadCastChatPacketFromCapatain((ChatPacket *)packet, sizeofPacket); } else recievedChatPacketCB(packet, sizeofPacket); } else if (((HWPacketHeader *)packet)->type == PACKETTYPE_FILE) { receivedFilePacketCB(packet,sizeofPacket); } return; } switch (((HWPacketHeader *)packet)->type) { case PACKETTYPE_TRANSFERCAPTAINCY: ReceivedTransferCaptaincyPacketCB(packet,sizeofPacket); break; case PACKETTYPE_SYNC: ReceivedSyncPacketCB(packet,sizeofPacket); break; case PACKETTYPE_COMMAND: ReceivedCmdPacketCB(packet,sizeofPacket); break; case PACKETTYPE_REQUESTEDSYNC: ReceivedRequestedSyncPacketCB(packet,sizeofPacket); break; case PACKETTYPE_REQUESTSYNCPKTS: ReceivedRequestSyncPacketsPacketCB(packet,sizeofPacket); break; case PACKETTYPE_DROPPINGOUTOFLOAD: if (!multiPlayerGameUnderWay) { PlayerDroppedOut(((HWPacketHeader *)packet)->frame,FALSE); } break; case PACKETTYPE_INGAMEQUITING: PlayerInGameQuited(((HWPacketHeader *)packet)->frame); break; case PACKETTYPE_NONCAPTAINREADY: if (!multiPlayerGameUnderWay) { sdword player = ((HWPacketHeader *)packet)->frame; // use frame field as player number if (player < sigsNumPlayers) { playersReadyToGo[player] = TRUE; } } break; case PACKETTYPE_CHAT: //assumption: If we revieved the packet, we were meant to see it if (IAmCaptain) { BroadCastChatPacketFromCapatain((ChatPacket *)packet, sizeofPacket); } else recievedChatPacketCB(packet, sizeofPacket); break; case PACKETTYPE_HORSERACE: if(IAmCaptain) { BroadCastHorseRacePacketFromCaptain(packet,sizeofPacket); } recievedHorsePacketCB(packet,sizeofPacket); break; case PACKETTYPE_IAMALIVE: if ((!CaptainNotDefined) && (IAmCaptain)) { receivedIAmAlivePacket((HWPacketHeader *)packet,sizeofPacket); } break; case PACKETTYPE_ALIVESTATUS: if ((!CaptainNotDefined) && (!IAmCaptain)) { receivedAliveStatusPacket((AliveStatusPacket *)packet,sizeofPacket); } break; case PACKETTYPE_FILE: receivedFilePacketCB(packet,sizeofPacket); break; case PACKETTYPE_LAGCHECK: lagRecievedPacketCB(packet,sizeofPacket); break; case PACKETTYPE_CHEATDETECT: { if (!blobSyncErr) { blobSyncErrFrame = ((HWPacketHeader *)packet)->frame; } blobSyncErr++; } break; default: unknownPackets++; break; } } /*----------------------------------------------------------------------------- Name : SetTargetID Description : sets the TargetID based on the target Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void SetTargetID(TargetID *targetID,SpaceObjRotImpTarg *target) { targetID->objtype = (uword)target->objtype; switch (target->objtype) { case OBJ_ShipType: targetID->objNumber = ((Ship *)target)->shipID.shipNumber; break; case OBJ_MissileType: targetID->objNumber = ((Missile *)target)->missileID.missileNumber; break; case OBJ_AsteroidType: case OBJ_NebulaType: case OBJ_GasType: case OBJ_DustType: targetID->objNumber = ((Resource *)target)->resourceID.resourceNumber; break; case OBJ_DerelictType: targetID->objNumber = ((Derelict *)target)->derelictID.derelictNumber; break; } } /*----------------------------------------------------------------------------- Name : convertNetSelectionToSelectCommand Description : converts a NetSelect type to a SelectCommand type. It converts all ShipID's to ShipPtr's. Any dead ShipID's or ships that don't belong to player from are rejected Inputs : Outputs : Return : returns the allocated SelectCommand. Caller must free it when done. ----------------------------------------------------------------------------*/ SelectCommand *convertNetSelectionToSelectCommand(NetSelection *netselection,bool considerInsideShips,uword from) { udword numShips = netselection->numShips; udword selectCommandSize = sizeofSelectCommand(numShips); SelectCommand *selectCommand; bool someShipsDied = FALSE; bool someShipsOrderedByWrongPlayer = FALSE; udword i; uword shipPlayerIndex; dbgAssert(numShips > 0); selectCommand = memAlloc(selectCommandSize,"conv. SelCom",0); selectCommand->numShips = numShips; for (i=0;iShipPtr[i] = ShipIDtoShip(netselection->ShipID[i],considerInsideShips)) == NULL) { someShipsDied = TRUE; } else { shipPlayerIndex = selectCommand->ShipPtr[i]->playerowner->playerIndex; if ((shipPlayerIndex >= 0) && (shipPlayerIndex < tpGameCreated.numPlayers)) // if it's a human ship if (shipPlayerIndex != from) // do further cheat check - only can control own ship { selectCommand->ShipPtr[i] = NULL; // reject ship someShipsDied = TRUE; someShipsOrderedByWrongPlayer = TRUE; } } } if (someShipsDied) { if (someShipsOrderedByWrongPlayer) dbgMessage("\nWARNING: Some ships ordered by wrong player!"); clRemoveShipFromSelection(selectCommand,NULL); // remove NULL ship pointers from selectCommand } return selectCommand; } /*----------------------------------------------------------------------------- Name : convertNetAttackSelectionToAttackCommand Description : converts a NetAttackSelection to a AttackCommand type. It converts all TargetID's to Target's. Inputs : Outputs : Return : returns the allocated AttackCommand. Caller must free it when done. ----------------------------------------------------------------------------*/ AttackCommand *convertNetAttackSelectionToAttackCommand(NetAttackSelection *netattackselection) { udword numTargets = netattackselection->numTargets; udword attackCommandSize = sizeofAttackCommand(numTargets); AttackCommand *attackCommand; bool someTargetsDied = FALSE; udword i; TargetID targetID; ShipID shipID; MissileID missileID; ResourceID resourceID; DerelictID derelictID; dbgAssert(numTargets > 0); attackCommand = memAlloc(attackCommandSize,"conv. AttCom",0); attackCommand->numTargets = numTargets; for (i=0;iTargetID[i]; switch (targetID.objtype) { case OBJ_ShipType: shipID.shipNumber = targetID.objNumber; attackCommand->TargetPtr[i] = (SpaceObjRotImpTarg *)ShipIDtoShip(shipID,FALSE); break; case OBJ_MissileType: missileID.missileNumber = targetID.objNumber; attackCommand->TargetPtr[i] = (SpaceObjRotImpTarg *)MissileIDtoMissilePtr(missileID); break; case OBJ_AsteroidType: case OBJ_NebulaType: case OBJ_GasType: case OBJ_DustType: resourceID.resourceNumber = targetID.objNumber; attackCommand->TargetPtr[i] = (SpaceObjRotImpTarg *)ResourceIDtoResourcePtr(resourceID); break; case OBJ_DerelictType: derelictID.derelictNumber = targetID.objNumber; attackCommand->TargetPtr[i] = (SpaceObjRotImpTarg *)DerelictIDToDerelictPtr(derelictID); break; } if (attackCommand->TargetPtr[i] == NULL) { someTargetsDied = TRUE; } } if (someTargetsDied) { dbgMessage("\nWARNING: Some targets died before order executed"); clRemoveTargetFromSelection(attackCommand,NULL); // remove NULL ship pointers from attackCommand } return attackCommand; } /*----------------------------------------------------------------------------- Name : packetSendToCaptain Description : sends packet to the captain (if not the captain, actually sends to captain across the network. If is the captain, fake sends it. Inputs : Outputs : Return : TRUE if packet memory should be freed. ----------------------------------------------------------------------------*/ bool packetSendToCaptain(ubyte *packet,udword sizeofPacket) { dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_COMMAND); if (playPackets) { return TRUE; } if (CaptainNotDefined) { return TRUE; } if (recordFakeSendPackets) { ReceivedCmdPacketCB(packet,sizeofPacket); // simulate fake transmit return TRUE; } if (IAmCaptain) { ReceivedCmdPacketCB(packet,sizeofPacket); // simulate fake transmit to captain return TRUE; } else { titanSendPointMessage(captainIndex,packet,sizeofPacket); // send message to captain return TRUE; } } /*----------------------------------------------------------------------------- Name : packetBroadcastSync Description : broadcasts the sync packet Inputs : Outputs : Return : TRUE if packet memory should be freed ----------------------------------------------------------------------------*/ bool packetBroadcastSync(ubyte *packet,udword sizeofPacket) { dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_SYNC); dbgAssert(IAmCaptain); if (playPackets) { return TRUE; } titanSendBroadcastMessage(packet,sizeofPacket); // send to everyone else ReceivedSyncPacketCB(packet,sizeofPacket); // and myself too. return TRUE; } /*----------------------------------------------------------------------------- Name : clSendMove Description : sends the Move command over the network Inputs : selectcom,from,to Outputs : Return : ----------------------------------------------------------------------------*/ void clSendMove(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetMoveCommand *move; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetMoveCommand(numShips); packet = memAlloc(sizeofPacket,"movepacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_MOVE | (sigsPlayerIndex<<8); move = (NetMoveCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); move->from = from; move->to = to; move->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendMpHyperspace Description : sends the Multiplayer hyperspace command over the network Inputs : selectcom,from,to Outputs : Return : ----------------------------------------------------------------------------*/ void clSendMpHyperspace(CommandLayer *comlayer,SelectCommand *selectcom,vector from,vector to) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetMoveCommand *move; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetMoveCommand(numShips); packet = memAlloc(sizeofPacket,"hyperspacepacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_MP_HYPERSPACE | (sigsPlayerIndex<<8); move = (NetMoveCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); move->from = from; move->to = to; move->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendAttack Description : sends the attack command over the network Inputs : selectcom,attackcom Outputs : Return : ----------------------------------------------------------------------------*/ void clSendAttack(CommandLayer *comlayer,SelectCommand *selectcom,AttackCommand *attackcom) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetAttackSelection *attack; NetSelection *selection; udword numShips = selectcom->numShips; udword numShipsToAttack = attackcom->numTargets; udword i; dbgAssert(numShips > 0); dbgAssert(numShipsToAttack > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetAttackCommand(numShipsToAttack,numShips); packet = memAlloc(sizeofPacket,"attackpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_ATTACK | (sigsPlayerIndex<<8); attack = (NetAttackSelection *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); attack->numTargets = (uword)numShipsToAttack; for (i=0;iTargetID[i],attackcom->TargetPtr[i]); } selection = (NetSelection *)(((ubyte *)attack) + sizeofNetAttackSelection(numShipsToAttack)); selection->numShips = (uword)numShips; for (i=0;iShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendSpecial Description : sends the special command over the network Inputs : selectcom,targetcom Outputs : Return : Notes : targetcom may be NULL ----------------------------------------------------------------------------*/ void clSendSpecial(CommandLayer *comlayer,SelectCommand *selectcom,SpecialCommand *targetcom) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetTargetsSelection *targets; NetSelection *selection; sdword numShips = selectcom->numShips; sdword numShipsToTarget; sdword i; dbgAssert(numShips > 0); if (targetcom == NULL) { numShipsToTarget = 0; } else { numShipsToTarget = targetcom->numTargets; } sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetSpecialCommand(numShipsToTarget,numShips); packet = memAlloc(sizeofPacket,"targetspacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_SPECIAL | (sigsPlayerIndex<<8); targets = (NetTargetsSelection *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); targets->numTargets = (uword)numShipsToTarget; for (i=0;iTargetID[i],targetcom->TargetPtr[i]); } selection = (NetSelection *)(((ubyte *)targets) + sizeofNetTargetsSelection(numShipsToTarget)); selection->numShips = (uword)numShips; for (i=0;iShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendFormation Description : sends the formation command over the network Inputs : selectcom, formation Outputs : Return : ----------------------------------------------------------------------------*/ void clSendFormation(CommandLayer *comlayer,SelectCommand *selectcom,TypeOfFormation formation) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetFormationCommand *formationcom; udword numShips = selectcom->numShips; udword i; if (selectcom->numShips < ABSOLUTE_MIN_SHIPS_IN_FORMATION) { #ifdef DEBUG_FORMATIONS dbgMessage("\nNot enough ships to do a formation"); #endif return; } sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetFormationCommand(numShips); packet = memAlloc(sizeofPacket,"formationpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_FORMATION | (sigsPlayerIndex<<8); formationcom = (NetFormationCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); formationcom->typeOfFormation = (uword)formation; formationcom->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendDock Description : sends the Dock command over the network Inputs : selectcom, dockType Outputs : Return : ----------------------------------------------------------------------------*/ void clSendDock(CommandLayer *comlayer,SelectCommand *selectcom,DockType dockType,ShipPtr dockwith) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetDockCommand *dock; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetDockCommand(numShips); packet = memAlloc(sizeofPacket,"dockpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_DOCK | (sigsPlayerIndex<<8); dock = (NetDockCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); if (dockwith != NULL) { dock->dockwithID = dockwith->shipID; } else { dock->dockwithID.shipNumber = MAX_ID_NUMBER; } dock->dockType = (uword)dockType; dock->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendLaunchMultipleShips Description : sends the Launch multiple ships command over the network Inputs : selectcom, launchFrom Outputs : Return : ----------------------------------------------------------------------------*/ void clSendLaunchMultipleShips(CommandLayer *comlayer,SelectCommand *selectcom,ShipPtr launchFrom) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetLaunchMultipleCommand *launch; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); dbgAssert(launchFrom); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetLaunchMultipleCommand(numShips); packet = memAlloc(sizeofPacket,"launchpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_LAUNCHMULTIPLE | (sigsPlayerIndex<<8); launch = (NetLaunchMultipleCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); launch->launchfromID = launchFrom->shipID; launch->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendMisc Description : sends a miscellaneous command of type miscCommand over the network Inputs : selectcom, miscCommand, miscData Outputs : Return : ----------------------------------------------------------------------------*/ void clSendMisc(CommandLayer *comlayer,SelectCommand *selectcom,uword miscCommand,udword miscData) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetMiscCommand *misc; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetMiscCommand(numShips); packet = memAlloc(sizeofPacket,"miscpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_MISC | (sigsPlayerIndex<<8); misc = (NetMiscCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); misc->miscCommand = miscCommand; misc->miscData = miscData; misc->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } #ifdef GOD_LIKE_SYNC_CHECKING void clSendGodSync(GodSyncCheckSums *checksumStruct,sdword playIndex,udword type) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; GodSyncCommand *misc; udword numShips = 0; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeof(GodSyncCommand); packet = memAlloc(sizeofPacket,"godpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_GODSYNC | (sigsPlayerIndex<<8); misc = (GodSyncCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); misc->type = type; misc->frame = universe.univUpdateCounter; misc->from=playIndex; //copy check misc->godChecksum=*checksumStruct; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } #endif void clSendRuTransfer(CommandLayer *comlayer,sdword fromIndex,sdword toIndex, sdword resourceUnits,ubyte flags) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetRUTransferferCommand *ruTransfer; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeof(NetRUTransferferCommand); packet = memAlloc(sizeofPacket,"rtranspacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_RUTRANSFER | (sigsPlayerIndex<<8); ruTransfer = (NetRUTransferferCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); ruTransfer->resourceUnits = resourceUnits; ruTransfer->to = (ubyte)toIndex; ruTransfer->from = (ubyte)fromIndex; ruTransfer->flags = flags; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendAutoLaunch Description : sends the autolaunch command over the network (for turning autolaunch on/off) Inputs : OnOff, playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void clSendAutoLaunch(udword OnOff,udword playerIndex) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetAutolaunchCommand *autolaunch; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetAutolaunchCommand; packet = memAlloc(sizeofPacket,"alaunchpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_AUTOLAUNCH | (sigsPlayerIndex<<8); autolaunch = (NetAutolaunchCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); autolaunch->OnOff = (uword)OnOff; autolaunch->playerIndex = (uword)playerIndex; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendSetAlliance Description : Sets an alliance based on a player bitmask recieved. Inputs : player bitmask Outputs : Return : void ----------------------------------------------------------------------------*/ void clSendSetAlliance(udword AllianceType, uword playerone, uword playertwo) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetAllianceCommand *allianceptr; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetAllianceCommand; packet = memAlloc(sizeofPacket,"aliancepacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_ALLIANCEINFO | (sigsPlayerIndex<<8); allianceptr = (NetAllianceCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); allianceptr->AllianceMsgType = AllianceType; allianceptr->CurAlliance = playerone; allianceptr->NewAlliance = playertwo; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } void clSendResearch(udword type, udword playernum, udword labnum, udword tech) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetResearchCommand *researchptr; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetResearchCommand; packet = memAlloc(sizeofPacket,"researchpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_RESEARCHINFO | (sigsPlayerIndex<<8); researchptr = (NetResearchCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); researchptr->ResearchMsgType = (ubyte) type; researchptr->playerindex = (ubyte) playernum; researchptr->labnum = (ubyte) labnum; researchptr->tech = (ubyte) tech; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clSendPlayerDropped(udword playerDroppedMask) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetPlayerDroppedCommand *PlayerDroppedptr; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetPlayerDroppedCommand; packet = memAlloc(sizeofPacket,"droppacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_PLAYERDROPPED | (sigsPlayerIndex<<8); PlayerDroppedptr = (NetPlayerDroppedCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); PlayerDroppedptr->playerDroppedMask = playerDroppedMask; if (IAmCaptain) PlayerDroppedptr->verify = playerDroppedMask ^ KILLDROPPEDOUTPLAYER_VERIFY; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendCreateShip Description : sends the create ship command over the network Inputs : shipType, shipRace, playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void clSendCreateShip(CommandLayer *comlayer,ShipType shipType,ShipRace shipRace,uword playerIndex,ShipPtr creator) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetCreateShipCommand *create; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetCreateShipCommand; packet = memAlloc(sizeofPacket,"createpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_CREATESHIP | (sigsPlayerIndex<<8); create = (NetCreateShipCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); create->shipType = (uword)shipType; create->shipRace = (uword)shipRace; create->playerIndex = playerIndex; create->creatorID = creator->shipID; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } void clSendDeterministicBuild(udword command, CommandLayer* comlayer, sdword numShips, ShipType shipType, ShipRace shipRace, uword playerIndex, ShipPtr creator) { udword sizeofPacket; HWPacketHeader* packet; HWCommandHeader* commandheader; NetDeterministicBuildCommand* create; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetDeterministicBuildCommand; packet = memAlloc(sizeofPacket, "dbuildpacket", 0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader*)(((ubyte*)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_DETERMINISTICBUILD | (sigsPlayerIndex<<8); create = (NetDeterministicBuildCommand*)(((ubyte*)commandheader) + sizeof(HWCommandHeader)); create->subCommand = (ubyte)command; create->numShips = (uword)numShips; create->shipType = (uword)shipType; create->shipRace = (uword)shipRace; create->playerIndex = playerIndex; create->creatorID = creator->shipID; if (packetSendToCaptain((ubyte*)packet, sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendBuildShip Description : sends the build ship command over the network Inputs : shipType, shipRace, playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void clSendBuildShip(CommandLayer *comlayer,ShipType shipType,ShipRace shipRace,uword playerIndex,ShipPtr creator) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetCreateShipCommand *create; sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetCreateShipCommand; packet = memAlloc(sizeofPacket,"buildpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_BUILDSHIP | (sigsPlayerIndex<<8); create = (NetCreateShipCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); create->shipType = (uword)shipType; create->shipRace = (uword)shipRace; create->playerIndex = playerIndex; create->creatorID = creator->shipID; if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendCollectResource Description : sends the CollectResource command over the network Inputs : selectcom, resource Outputs : Return : ----------------------------------------------------------------------------*/ void clSendCollectResource(CommandLayer *comlayer,SelectCommand *selectcom,ResourcePtr resource) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetCollectResourceCommand *collect; udword numShips = selectcom->numShips; udword i; dbgAssert(numShips > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetCollectResourceCommand(numShips); packet = memAlloc(sizeofPacket,"collectpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_COLLECTRESOURCE | (sigsPlayerIndex<<8); collect = (NetCollectResourceCommand *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); if (resource == NULL) { collect->resourceID.resourceNumber = MAX_ID_NUMBER; } else { collect->resourceID = resource->resourceID; } collect->selection.numShips = (uword)numShips; for (i=0;iselection.ShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clSendProtect Description : sends protect command over network Inputs : selectcom, protectcom Outputs : Return : ----------------------------------------------------------------------------*/ void clSendProtect(CommandLayer *comlayer,SelectCommand *selectcom,ProtectCommand *protectcom) { udword sizeofPacket; HWPacketHeader *packet; HWCommandHeader *commandheader; NetProtectSelection *protect; NetSelection *selection; udword numShips = selectcom->numShips; udword numShipsToProtect = protectcom->numShips; udword i; dbgAssert(numShips > 0); dbgAssert(numShipsToProtect > 0); sizeofPacket = sizeof(HWPacketHeader) + sizeof(HWCommandHeader) + sizeofNetProtectCommand(numShipsToProtect,numShips); packet = memAlloc(sizeofPacket,"protectpacket",0); packet->type = PACKETTYPE_COMMAND; packet->from = (uword)sigsPlayerIndex; packet->frame = 0; packet->numberOfCommands = 1; commandheader = (HWCommandHeader *)(((ubyte *)packet) + sizeof(HWPacketHeader)); commandheader->commandType = COMMANDTYPE_PROTECT | (sigsPlayerIndex<<8); protect = (NetProtectSelection *)(((ubyte *)commandheader) + sizeof(HWCommandHeader)); protect->numShips = (uword)numShipsToProtect; for (i=0;iShipID[i] = (protectcom->ShipPtr[i])->shipID; } selection = (NetSelection *)(((ubyte *)protect) + sizeofNetProtectSelection(numShipsToProtect)); selection->numShips = (uword)numShips; for (i=0;iShipID[i] = (selectcom->ShipPtr[i])->shipID; } if (packetSendToCaptain((ubyte *)packet,sizeofPacket)) { memFree(packet); } } /*----------------------------------------------------------------------------- Name : clProcessSyncPacket Description : Given the sync packet, it parses it and executes any commands inside it by passing them to the command layer. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void clProcessSyncPacket(CommandLayer *comlayer,ubyte *packet,udword sizeofPacket) { ubyte *curpacket; udword numberOfCommandsLeft; uword commandType; uword from; dbgAssert(((HWPacketHeader *)packet)->type == PACKETTYPE_SYNC); if (!playPackets) if (((HWPacketHeader *)packet)->frame != receivedPacketNumber) { dbgMessagef("\nExpected pkt %d got %d",receivedPacketNumber,((HWPacketHeader *)packet)->frame); pktSyncErr++; } receivedPacketNumber++; if (recordPackets) { if (OrigRecordPacketFileName[0]) { recPackRecordPacketFilename(packet,sizeofPacket,OrigRecordPacketFileName); } recPackRecordPacket(packet,sizeofPacket); } netcheckShow((HWPacketHeader *)packet); #ifdef DEBUG_GAME_STATS if(statLogOn) { gameStatsUpdateLogFile(universe.univUpdateCounter); } #endif numberOfCommandsLeft = (udword)((HWPacketHeader *)packet)->numberOfCommands; curpacket = packet + sizeof(HWPacketHeader); while (numberOfCommandsLeft > 0) { commandType = ((HWCommandHeader *)curpacket)->commandType; from = (commandType >> 8) & 255; commandType &= 255; curpacket += sizeof(HWCommandHeader); switch (commandType) { case COMMANDTYPE_MOVE: { NetMoveCommand *movecom = (NetMoveCommand *)curpacket; SelectCommand *moveselection = convertNetSelectionToSelectCommand(&movecom->selection,FALSE,from); clMove(comlayer,moveselection,movecom->from,movecom->to); memFree(moveselection); curpacket += sizeofNetMoveCommand(movecom->selection.numShips); } break; case COMMANDTYPE_MP_HYPERSPACE: { NetMoveCommand *movecom = (NetMoveCommand *)curpacket; SelectCommand *moveselection = convertNetSelectionToSelectCommand(&movecom->selection,FALSE,from); clMpHyperspace(comlayer,moveselection,movecom->from,movecom->to); memFree(moveselection); curpacket += sizeofNetMoveCommand(movecom->selection.numShips); } break; case COMMANDTYPE_ATTACK: { NetAttackSelection *netattackselection = (NetAttackSelection *)curpacket; NetSelection *netselection = (NetSelection *)(curpacket + sizeofNetAttackSelection(netattackselection->numTargets)); AttackCommand *attackattack = convertNetAttackSelectionToAttackCommand(netattackselection); SelectCommand *attackselection = convertNetSelectionToSelectCommand(netselection,FALSE,from); clAttack(comlayer,attackselection,attackattack); memFree(attackattack); memFree(attackselection); curpacket += sizeofNetAttackCommand(netattackselection->numTargets,netselection->numShips); } break; case COMMANDTYPE_SPECIAL: { NetTargetsSelection *nettargetsselection = (NetTargetsSelection *)curpacket; sdword nettargetsselectionNumShips = nettargetsselection->numTargets; NetSelection *netselection = (NetSelection *)(curpacket + sizeofNetTargetsSelection(nettargetsselectionNumShips)); SelectCommand *selectcom = convertNetSelectionToSelectCommand(netselection,FALSE,from); SpecialCommand *targetcom; if (nettargetsselectionNumShips > 0) { targetcom = convertNetAttackSelectionToAttackCommand(nettargetsselection); } else { targetcom = NULL; } clSpecial(comlayer,selectcom,targetcom); memFree(selectcom); if (targetcom != NULL) memFree(targetcom); curpacket += sizeofNetSpecialCommand(nettargetsselectionNumShips,netselection->numShips); } break; case COMMANDTYPE_FORMATION: { NetFormationCommand *formationcom = (NetFormationCommand *)curpacket; SelectCommand *formselection = convertNetSelectionToSelectCommand(&formationcom->selection,FALSE,from); clFormation(comlayer,formselection,(TypeOfFormation)formationcom->typeOfFormation); memFree(formselection); curpacket += sizeofNetFormationCommand(formationcom->selection.numShips); } break; case COMMANDTYPE_DOCK: { NetDockCommand *dockcom = (NetDockCommand *)curpacket; SelectCommand *dockselection = convertNetSelectionToSelectCommand(&dockcom->selection,FALSE,from); clDock(comlayer,dockselection,(DockType)dockcom->dockType,ShipIDtoShip(dockcom->dockwithID,FALSE)); memFree(dockselection); curpacket += sizeofNetDockCommand(dockcom->selection.numShips); } break; case COMMANDTYPE_LAUNCHMULTIPLE: { NetLaunchMultipleCommand *launchcom = (NetLaunchMultipleCommand *)curpacket; SelectCommand *launchselection = convertNetSelectionToSelectCommand(&launchcom->selection,TRUE,from); clLaunchMultipleShips(comlayer,launchselection,ShipIDtoShip(launchcom->launchfromID,FALSE)); memFree(launchselection); curpacket += sizeofNetLaunchMultipleCommand(launchcom->selection.numShips); } break; case COMMANDTYPE_MISC: { NetMiscCommand *misccom = (NetMiscCommand *)curpacket; SelectCommand *miscselection = convertNetSelectionToSelectCommand(&misccom->selection,FALSE,from); switch (misccom->miscCommand) { case MISCCOMMAND_HALT: clHalt(comlayer,miscselection); break; case MISCCOMMAND_SCUTTLE: clScuttle(comlayer,miscselection); break; case MISCCOMMAND_TACTICS: clSetTactics(comlayer,miscselection,(TacticsType)misccom->miscData); break; case MISCCOMMAND_KAMIKAZE: clSetKamikaze(comlayer,miscselection); break; case MISCCOMMAND_PARADE: clSetMilitaryParade(comlayer,miscselection); break; default: dbgAssert(FALSE); break; } memFree(miscselection); curpacket += sizeofNetMiscCommand(misccom->selection.numShips); } break; #ifdef GOD_LIKE_SYNC_CHECKING case COMMANDTYPE_GODSYNC: // This is a special SYNC debug feature such that when this command is recieved the game will halt { GodSyncCommand *gp=(GodSyncCommand *)curpacket; switch(((GodSyncCommand *)curpacket)->type) { case GOD_COMMAND_HALTONSYNC: if(syncDumpOn) { //user wants an int 3 syncDebugDump("syncDump.txt",universe.univUpdateCounter,TRUE); //_asm int 3; dbgNonFatal(DBG_Loc,"SYNC Error Detected crash to debuger?"); } break; case GOD_COMMAND_GOD_HALTONSYNC: if(syncDumpOn) { netReceivedSyncFromNonCaptain(&gp->godChecksum,gp->frame,gp->from); } break; } curpacket += sizeof(GodSyncCommand); } break; #endif case COMMANDTYPE_AUTOLAUNCH: { NetAutolaunchCommand *launchcom = (NetAutolaunchCommand *)curpacket; clAutoLaunch((udword)launchcom->OnOff,(udword)launchcom->playerIndex); curpacket += sizeofNetAutolaunchCommand; } break; case COMMANDTYPE_ALLIANCEINFO: { NetAllianceCommand *alliancecom = (NetAllianceCommand *)curpacket; clSetAlliance(alliancecom->AllianceMsgType, alliancecom->CurAlliance, alliancecom->NewAlliance); curpacket += sizeofNetAllianceCommand; } break; case COMMANDTYPE_PLAYERDROPPED: { NetPlayerDroppedCommand *droppedcom = (NetPlayerDroppedCommand *)curpacket; clPlayerDropped(droppedcom->playerDroppedMask,droppedcom->verify); curpacket += sizeofNetPlayerDroppedCommand; } break; case COMMANDTYPE_CREATESHIP: { NetCreateShipCommand *createcom = (NetCreateShipCommand *)curpacket; clCreateShip(comlayer,(ShipType)createcom->shipType,(ShipRace)createcom->shipRace,createcom->playerIndex,ShipIDtoShip(createcom->creatorID,FALSE)); curpacket += sizeofNetCreateShipCommand; } break; case COMMANDTYPE_DETERMINISTICBUILD: { NetDeterministicBuildCommand* buildcom = (NetDeterministicBuildCommand*)curpacket; clDeterministicBuild(buildcom->subCommand, comlayer, buildcom->numShips, (ShipType)buildcom->shipType, (ShipRace)buildcom->shipRace, buildcom->playerIndex, ShipIDtoShip(buildcom->creatorID, FALSE)); curpacket += sizeofNetDeterministicBuildCommand; } break; case COMMANDTYPE_BUILDSHIP: { NetCreateShipCommand *createcom = (NetCreateShipCommand *)curpacket; clBuildShip(comlayer,(ShipType)createcom->shipType,(ShipRace)createcom->shipRace,createcom->playerIndex,ShipIDtoShip(createcom->creatorID,FALSE)); curpacket += sizeofNetCreateShipCommand; } break; case COMMANDTYPE_COLLECTRESOURCE: { NetCollectResourceCommand *collectcom = (NetCollectResourceCommand *)curpacket; SelectCommand *collectselection = convertNetSelectionToSelectCommand(&collectcom->selection,FALSE,from); clCollectResource(comlayer,collectselection,ResourceIDtoResourcePtr(collectcom->resourceID)); memFree(collectselection); curpacket += sizeofNetCollectResourceCommand(collectcom->selection.numShips); } break; case COMMANDTYPE_PROTECT: { NetProtectSelection *netprotectselection = (NetProtectSelection *)curpacket; NetSelection *netselection = (NetSelection *)(curpacket + sizeofNetProtectSelection(netprotectselection->numShips)); ProtectCommand *protectprotect = convertNetSelectionToSelectCommand(netprotectselection,FALSE,from); SelectCommand *protectselection = convertNetSelectionToSelectCommand(netselection,FALSE,from); clProtect(comlayer,protectselection,protectprotect); memFree(protectprotect); memFree(protectselection); curpacket += sizeofNetProtectCommand(netprotectselection->numShips,netselection->numShips); } break; case COMMANDTYPE_RUTRANSFER: { NetRUTransferferCommand *rucommand = (NetRUTransferferCommand *)curpacket; clRUTransfer(comlayer,rucommand->to,rucommand->from,rucommand->resourceUnits,rucommand->flags); curpacket+=sizeof(NetRUTransferferCommand); } break; case COMMANDTYPE_RESEARCHINFO: { NetResearchCommand *researchcommand = (NetResearchCommand *)curpacket; clSetResearch((udword)researchcommand->ResearchMsgType, (udword)researchcommand->playerindex, (udword)researchcommand->labnum, (udword)researchcommand->tech); curpacket+=sizeofNetResearchCommand; } break; default: dbgAssert(FALSE); break; } numberOfCommandsLeft--; } } #define LastSyncPktsQ_NUMBER 1024 #define LastSyncPktsQ_MASK 1023 typedef struct { udword frame; HWPacketHeader *packet; udword length; } CircularQEntry; typedef struct { udword head; CircularQEntry circularQEntry[LastSyncPktsQ_NUMBER]; } LastSyncPktsQ; LastSyncPktsQ lastSyncPktsQ; void InitLastSyncPktsQ(void) { memset(&lastSyncPktsQ,0,sizeof(lastSyncPktsQ)); } void CloseLastSyncPktsQ(void) { sdword i; for (i=0;ipacket) { memFree(entry->packet); } dbgAssert(packet); entry->frame = frame; entry->packet = packet; entry->length = length; lastSyncPktsQ.head = (lastSyncPktsQ.head+1) & LastSyncPktsQ_MASK; } bool GetSyncPktFromLastSyncPktsQ(udword frame,HWPacketHeader **packet,udword *size) { sdword i; dbgAssert(frame != 0); for (i=0;i 0); copypacket = memAlloc(sizeofPacket,"cp(copypacket)",Pyrophoric); memcpy(copypacket,packet,sizeofPacket); UnLockQueue(&ProcessRequestedSyncPktQ); dbgAssert(((HWPacketHeader *)copypacket)->type == PACKETTYPE_REQUESTEDSYNC); ((HWPacketHeader *)copypacket)->type = PACKETTYPE_SYNC; if (((HWPacketHeader *)copypacket)->frame == explicitlyRequestingTo) explicitlyRequestingPackets = FALSE; else if (((HWPacketHeader *)copypacket)->frame == ((udword)-1)) { explicitlyRequestingPackets = FALSE; // we received fake sync packet because the one we wanted // wasn't present. memFree(copypacket); return NO_PACKET; } goto gotsyncpkt; } } LockQueue(&ProcessSyncPktQ); numPackets = queueNumberEntries(ProcessSyncPktQ); if (numPackets == 0) { UnLockQueue(&ProcessSyncPktQ); return NO_PACKET; } else { sizeofPacket = Peekqueue(&ProcessSyncPktQ,&packet); dbgAssert(sizeofPacket > 0); for (;;) { if (((HWPacketHeader *)packet)->from != receiveSyncPacketsFrom) { goto getnextpacket; } if (((HWPacketHeader *)packet)->frame < receivedPacketNumber) { dbgMessagef("\nDiscarding packet %d already got it",((HWPacketHeader *)packet)->frame); goto getnextpacket; } if (((HWPacketHeader *)packet)->frame == receivedPacketNumber) { sizeofPacket = Dequeue(&ProcessSyncPktQ,&packet); // actually dequeue it dbgAssert(sizeofPacket > 0); break; } // frame is > receivedPacketNumber, don't dequeue it, if (!explicitlyRequestingPackets) // and request packets receivedPacketNumber..frame-1 (but only once) { explicitlyRequestingFrom = receivedPacketNumber; explicitlyRequestingTo = ((HWPacketHeader *)packet)->frame - 1; UnLockQueue(&ProcessSyncPktQ); SendRequestSyncPkts(explicitlyRequestingFrom,explicitlyRequestingTo); explicitlyRequestingPackets = TRUE; } else { UnLockQueue(&ProcessSyncPktQ); } return NO_PACKET; getnextpacket: sizeofPacket = Dequeue(&ProcessSyncPktQ,&packet); // actually dequeue it dbgAssert(sizeofPacket > 0); numPackets = queueNumberEntries(ProcessSyncPktQ); if (numPackets == 0) { UnLockQueue(&ProcessSyncPktQ); return NO_PACKET; } sizeofPacket = Peekqueue(&ProcessSyncPktQ,&packet); dbgAssert(sizeofPacket > 0); } copypacket = memAlloc(sizeofPacket,"cp(copypacket)",Pyrophoric); memcpy(copypacket,packet,sizeofPacket); UnLockQueue(&ProcessSyncPktQ); gotsyncpkt: clProcessSyncPacket(comlayer,copypacket,sizeofPacket); EnterIntoLastSyncPktsQ(((HWPacketHeader *)copypacket)->frame,(HWPacketHeader *)copypacket,sizeofPacket); // last sync packets may be used to recover game if this player takes over as captain if (numPackets > HIGH_WATER_MARK) { return TOO_MANY_PACKETS; } else { return PACKET_READY; } } } typedef struct { char *qdata; udword qsizeof; } QInfo; /*----------------------------------------------------------------------------- Name : captainServerTask Description : This is the captain server task, which every so many Hz takes any command packets received, and merges them into a sync packet which it transmits. Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ #pragma optimize("gy", off) //turn on stack frame (we need ebp for this function) void captainServerTask(void) { static QInfo *qinfos; static QInfo *curqinfo; static udword qTotalNumberEntries; static udword numCommands; static udword j; static HWPacketHeader *packet; static HWPacketHeader *thispacket; static ubyte *curPacketPtr; static udword packetlength; static udword datalength; static udword totalCommands; taskYield(0); for(;;) { taskStackSaveCond(0); if ( (recordFakeSendPackets) || ((multiPlayerGame) && (gameIsRunning) && (IAmCaptain) && (multiPlayerGameUnderWay)) ) { if (playPackets|recordFakeSendPackets) { if (universePause) { goto donecap; } } numCommands = 0; LockQueue(&ProcessCmdPktQ); qTotalNumberEntries = queueNumberEntries(ProcessCmdPktQ); if (qTotalNumberEntries == 0) { qinfos = NULL; } else { qinfos = memAlloc(sizeof(QInfo)*qTotalNumberEntries,"qinfos",0); } while (queueNumberEntries(ProcessCmdPktQ) > 0) { curqinfo = &qinfos[numCommands]; curqinfo->qsizeof = Dequeue(&ProcessCmdPktQ,(ubyte **)&curqinfo->qdata); dbgAssert(curqinfo->qsizeof > 0); dbgAssert(curqinfo->qdata != NULL); dbgAssert(numCommands < qTotalNumberEntries); numCommands++; } if (numCommands == 0) { packetlength = sizeof(HWPacketHeader); packet = memAlloc(packetlength,"emptysyncpkt",0); packet->numberOfCommands = 0; } else if (numCommands == 1) { packetlength = qinfos[0].qsizeof; packet = memAlloc(packetlength,"onesync",0); memcpy(packet,qinfos[0].qdata,packetlength); dbgAssert(packet->type == PACKETTYPE_COMMAND); packet->numberOfCommands = 1; } else { // received more than 1 command packet, so we must concatenate them into one sync packet packetlength = sizeof(HWPacketHeader); totalCommands = 0; for (j=0;jtype == PACKETTYPE_COMMAND); dbgAssert(datalength > 0); dbgAssert(thispacket->numberOfCommands > 0); packetlength += datalength; totalCommands += thispacket->numberOfCommands; } packet = memAlloc(packetlength,"concatsyncpkt",0); packet->type = PACKETTYPE_SYNC; packet->numberOfCommands = (uword)totalCommands; curPacketPtr = ((ubyte *)packet) + sizeof(HWPacketHeader); for (j=0;jtype = PACKETTYPE_SYNC; packet->from = (uword)sigsPlayerIndex; packet->frame = sentPacketNumber++; #if SYNC_CHECK netcheckFillInChecksum(packet); #endif if (recordFakeSendPackets) { ReceivedSyncPacketCB((ubyte *)packet,packetlength); // and myself too. memFree(packet); } else { if (packetBroadcastSync((ubyte *)packet,packetlength)) { memFree(packet); } } donecap:; } taskStackRestoreCond(); taskYield(0); } taskExit(); } #pragma optimize("", on) /*----------------------------------------------------------------------------- Name : SendTransferCaptaincyPacket Description : sends a TransferCaptaincyPacket of subtype subtype with custominfo Inputs : playerIndex, >= 0 for player, -1 for broadcast, -2 for broadcast including myself subtype, custominfo Outputs : Return : none ----------------------------------------------------------------------------*/ void SendTransferCaptaincyPacket(sdword playerIndex,uword subtype,udword misc,CaptaincyCustomInfo *custominfo) { TransferCaptaincyPacket packet; sdword size; if (sigsNumPlayers < 2) { return; } packet.packetheader.type = PACKETTYPE_TRANSFERCAPTAINCY; packet.packetheader.from = (uword)sigsPlayerIndex; packet.packetheader.frame = misc; packet.packetheader.numberOfCommands = 0; dbgAssert(subtype < XFERCAP_NUM); packet.subtype = subtype; if (custominfo) { packet.custominfo = *custominfo; packet.subtype |= XFERCAP_CUSTOMINFO_PRESENT; size = sizeof(TransferCaptaincyPacket); } else { size = sizeof(TransferCaptaincyPacketSmall); } if (playerIndex >= 0) { dbgAssert(playerIndex < sigsNumPlayers); titanAnyoneSendPointMessage(playerIndex,(ubyte *)&packet,size); } else { // -1 or lower titanAnyoneSendBroadcastMessage((ubyte *)&packet,size); if (playerIndex == -2) { // even send to myself! ReceivedTransferCaptaincyPacketCB((ubyte *)&packet,size); } } } /*----------------------------------------------------------------------------- Name : SendNonCaptainReadyPacket Description : sends the "Ready to play packet (I am not the captain)" packet to indicate user is ready to play. Inputs : playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void SendNonCaptainReadyPacket(udword playerIndex) { HWPacketHeader packet; dbgAssert(!IAmCaptain); dbgAssert(!CaptainNotDefined); packet.type = PACKETTYPE_NONCAPTAINREADY; packet.from = (uword)playerIndex; packet.frame = playerIndex; packet.numberOfCommands = 0; titanSendPointMessage(captainIndex,(ubyte *)&packet,sizeof(HWPacketHeader)); } /*----------------------------------------------------------------------------- Name : SendDroppingOutOfLoad Description : sends the "I'm dropping out of loading" packet to everyone else Inputs : my playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void SendDroppingOutOfLoad(udword playerIndex) { HWPacketHeader packet; packet.type = PACKETTYPE_DROPPINGOUTOFLOAD; packet.from = (uword)playerIndex; packet.frame = playerIndex; packet.numberOfCommands = 0; titanSendBroadcastMessage((ubyte *)&packet,sizeof(HWPacketHeader)); } /*----------------------------------------------------------------------------- Name : SendCheatDetect Description : Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void SendCheatDetect(void) { HWPacketHeader packet; packet.type = PACKETTYPE_CHEATDETECT; packet.from = (uword)sigsPlayerIndex; packet.frame = blobSyncErrFrame; packet.numberOfCommands = 0; titanSendBroadcastMessage((ubyte *)&packet,sizeof(HWPacketHeader)); } /*----------------------------------------------------------------------------- Name : SendInGameQuittingPacket Description : sends the "In Game I'm quitting" packet to everyone else Inputs : my playerIndex Outputs : Return : ----------------------------------------------------------------------------*/ void SendInGameQuittingPacket(udword playerIndex) { HWPacketHeader packet; packet.type = PACKETTYPE_INGAMEQUITING; packet.from = (uword)playerIndex; packet.frame = playerIndex; packet.numberOfCommands = 0; titanSendBroadcastMessage((ubyte *)&packet,sizeof(HWPacketHeader)); } void SendRequestSyncPkts(udword frompacketnum,udword topacketnum) { RequestSyncPacketsPacket packet; dbgAssert(!IAmCaptain); dbgAssert(!CaptainNotDefined); dbgAssert(topacketnum >= frompacketnum); packet.packetheader.type = PACKETTYPE_REQUESTSYNCPKTS; packet.packetheader.from = (uword)sigsPlayerIndex; packet.packetheader.frame = frompacketnum; packet.packetheader.numberOfCommands = 0; packet.topacketnum = topacketnum; titanSendPointMessage(captainIndex,(ubyte *)&packet,sizeof(RequestSyncPacketsPacket)); } void SendLagPacket(udword to, ubyte *packet) { titanSendPointMessage(to, packet, sizeof(LagPacket)); } /*----------------------------------------------------------------------------- Name : SendHorseRacePacket Description : Sends A packet Update Regarding a players horse race status Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void SendHorseRacePacket(ubyte *packet,udword sizeofpacket) { if(!multiPlayerGame) return; if (CaptainNotDefined) return; if(IAmCaptain) { titanSendBroadcastMessage(packet,sizeofpacket); // send to everyone else recievedHorsePacketCB(packet,sizeofpacket); // I receive it too } else { titanSendPointMessage(captainIndex,packet,sizeofpacket); } } /*----------------------------------------------------------------------------- Name : BroadCastHorseRacePacket Description : Sends HorseRace status to EVERYONE except captain about EVERYONE Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void BroadCastHorseRacePacketFromCaptain(ubyte *packet, udword sizeofpacket) { titanSendBroadcastMessage(packet,sizeofpacket); } /*----------------------------------------------------------------------------- Name : SendChatPacketPacket Description : Sends a packet to all users flagged as TRUE with a packet of CHAT data Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void SendChatPacketPacket(ChatPacket *packet, udword sizeofpacket,udword users) { sdword i; if(!multiPlayerGame) return; if (CaptainNotDefined) { return; } dbgAssert(!(PLAYER_MASK(sigsPlayerIndex) & users)); //shouldn't be sending a message to local player...thats dumb packet->bounced = FALSE; if(IAmCaptain) { for (i=0;iusers,PLAYER_MASK(i))) { titanSendPointMessage(i, (ubyte *)packet, sizeofpacket); } } } } else { titanSendPointMessage(captainIndex,(ubyte *)packet,sizeofpacket); } } void BroadCastChatPacketFromCapatain(ChatPacket *packet, udword sizeofpacket) { sdword i; if (CaptainNotDefined) { return; } dbgAssert(IAmCaptain); if (packet->bounced) { goto justreceiveit; } packet->bounced = TRUE; for (i=0;iusers,PLAYER_MASK(i))) { titanSendPointMessage(i, (ubyte *)packet, sizeofpacket); } } } justreceiveit: if (bitTest(packet->users,PLAYER_MASK(captainIndex))) { recievedChatPacketCB((ubyte *)packet, sizeofpacket); } } /*----------------------------------------------------------------------------- Name : netCheck Description : prints various network statistics Inputs : Outputs : Return : ----------------------------------------------------------------------------*/ void netCheck(void) { udword rsyncpkts; udword rcmdpkts; udword rsyncoverruns; udword rcmdoverruns; if (unknownPackets != printedUnknownPackets) { printedUnknownPackets = unknownPackets; dbgMessagef("\nUnknown packets %d",printedUnknownPackets); } rsyncpkts = queueNumberEntries(ProcessSyncPktQ); rcmdpkts = queueNumberEntries(ProcessCmdPktQ); rsyncoverruns = queueNumberOverruns(ProcessSyncPktQ); rcmdoverruns = queueNumberOverruns(ProcessCmdPktQ); if (syncpkts != rsyncpkts) { syncpkts = rsyncpkts; } if (cmdpkts != rcmdpkts) { cmdpkts = rcmdpkts; } if (syncoverruns != rsyncoverruns) { syncoverruns = rsyncoverruns; dbgMessagef("\nSync Q Overruns: %d",syncoverruns); } if (cmdoverruns != rcmdoverruns) { cmdoverruns = rcmdoverruns; dbgMessagef("\nCmd Q Overruns: %d",cmdoverruns); } } void CaptaincyChangedNotify(void) { KeepAliveStartTimers(); // restart keepalive timers after captaincy changes } /*============================================================================= Keep Alive functions =============================================================================*/ void UpdateDroppedOutStatuses(); void SendKeepAlivePacket(); void BroadcastAliveStatusPacket(); void KeepAliveReset(void) { sdword i; TTimerInit(&SendIAmAliveTimer); TTimerInit(&SendAliveStatusTimer); LockMutex(AliveTimeoutStatusesMutex); for (i=0;ipacketheader.type == PACKETTYPE_ALIVESTATUS); LockMutex(AliveTimeoutStatusesMutex); memcpy(AliveStatuses,packet->alivestatus,sizeof(AliveStatuses)); #if 0 // if captain tells us we're dead, we're dead- and will be receiving a kill player message if (AliveStatuses[sigsPlayerIndex] == ALIVESTATUS_DROPPEDOUT) { sdword i; // if they tell me I'm dead then I'm alive but THEY are all dead for (i=0;itype == PACKETTYPE_IAMALIVE); aliveplayer = packet->from; if (aliveplayer < sigsNumPlayers) { LockMutex(AliveTimeoutStatusesMutex); if (AliveStatuses[aliveplayer] == ALIVESTATUS_ALIVE) // keep alive alive players { TTimerReset(&AliveTimeoutTimers[aliveplayer]); } UnLockMutex(AliveTimeoutStatusesMutex); } } void clPlayerDropped(udword playerMask,udword verify) { sdword i; bool aPlayerDied = FALSE; if ((playerMask ^ verify) != KILLDROPPEDOUTPLAYER_VERIFY) { return; } for (i=0;i