/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program 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. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine // NetMessageStream.cpp // Holds misc functions to encapsulate GameMessages into Command Packets to send // over the network. // Author: Matthew D. Campbell, July 2001 /* #include "stdlib.h" // VC++ wants this here, or gives compile error... #include "Common/GameType.h" #include "Common/MessageStream.h" #include "Common/GameEngine.h" #include "GameLogic/GameLogic.h" #include "GameNetwork/NetworkInterface.h" #include "GameNetwork/NetworkDefs.h" // The per-player pointers for the list of commands static CommandMsg *CommandHead[MAX_SLOTS] = { /// @todo: remove static initialization NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static CommandMsg *CommandTail[MAX_SLOTS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; /** * AddToNetCommandList adds a CommandMsg to a list of commands. * static Bool AddToNetCommandList(GameMessage *msg, UnsignedInt timestamp, CommandMsg *& CommandHead, CommandMsg *& CommandTail) { CommandMsg *cmdMsg = NEW CommandMsg(timestamp, msg); if (!cmdMsg) { DEBUG_LOG(("Alloc failed!\n")); return false; } if (CommandTail == NULL) { CommandHead = cmdMsg; CommandTail = cmdMsg; } else { cmdMsg->SetPrevCommandMsg(CommandTail); CommandTail->SetNextCommandMsg(cmdMsg); CommandTail = cmdMsg; } return true; } /** * AddToRemoteNetCommandList is used by TheNetwork to queue up commands recieved from other players. * Bool AddToNetCommandList(Int playerNum, GameMessage *msg, UnsignedInt timestamp) { if (playerNum < 0 || playerNum >= MAX_SLOTS) return false; DEBUG_LOG(("Adding msg to NetCommandList %d\n", playerNum)); return AddToNetCommandList(msg, timestamp, CommandHead[playerNum], CommandTail[playerNum]); } /** * GetCommandMsg returns a GameMessage (deleting its CommandMsg wrapper) that is valid * for the current frame, or NULL. * static GameMessage * GetCommandMsg(UnsignedInt timestamp, CommandMsg *& CommandHead, CommandMsg *& CommandTail) { if (!CommandHead) return NULL; if (CommandHead->GetTimestamp() < timestamp) { DEBUG_LOG(("Time is %d, yet message timestamp is %d!\n", timestamp, CommandHead->GetTimestamp())); return NULL; } if (CommandHead->GetTimestamp() != timestamp) return NULL; CommandMsg *theMsg = CommandHead; if (CommandHead->GetNextCommandMsg()) { CommandHead->GetNextCommandMsg()->SetPrevCommandMsg(NULL); CommandHead = CommandHead->GetNextCommandMsg(); } else { CommandHead = CommandTail = NULL; } GameMessage *msg = theMsg->GetGameMessage(); delete theMsg; return msg; } /** * GetCommandMsg returns a message from the command list. * GameMessage * GetCommandMsg(UnsignedInt timestamp, Int playerNum) { if (playerNum < 0 || playerNum >= MAX_SLOTS) return NULL; //DEBUG_LOG(("Adding msg to NetCommandList %d\n", playerNum)); return GetCommandMsg(timestamp, CommandHead[playerNum], CommandTail[playerNum]); } //==================================================================================== // The commandBuf & commandPacket hold the commands we're building up for the frame. static unsigned char commandBuf[sizeof(CommandPacket)+1]; static CommandPacket *commandPacket = (CommandPacket *)(commandBuf+1); /** * ClearCommandPacket clears the command packet at the start of the frame. * void ClearCommandPacket(UnsignedInt frame) { commandPacket->m_frame = frame; commandPacket->m_numCommands = 0; } /** * AddCommandToPacket creates a packet containing all move/attack/etc commands * for the current frame. * Bool AddCommandToPacket(const GameMessage *msg) { int messageSize = sizeofMessageHeader + sizeofMessageArg * msg->getArgumentCount(); // If we have too much, send what we have if (bytesUsed && (bytesUsed + sizeof(CommandPacketHeader) + messageSize >= MAX_MESSAGE_LEN)) { commandBuf[0] = MSGTYPE_PARTIALCOMMAND; if (!TheNetwork->queueSend(BROADCAST_CON, commandBuf, bytesUsed + sizeof(CommandPacketHeader) + 1, MSG_NEEDACK | MSG_SEQUENCED)) { //DEBUG_ASSERTCRASH(false, ("Too many commands in one frame! Some will be dropped.")); DEBUG_LOG(("Too many commands in one frame! Some will be dropped.")); return false; } commandBuf[0] = MSGTYPE_COMMANDCOUNT; commandPacket->header.m_numCommands = 0; bytesUsed = 0; } if (bytesUsed + sizeof(CommandPacketHeader) + messageSize >= MAX_MESSAGE_LEN) { //DEBUG_ASSERTCRASH(false, ("Too many commands in one frame! Some will be dropped.")); DEBUG_LOG(("Too many commands in one frame! Some will be dropped.")); return false; } // We have room, so add the message commandPacket->header.m_numCommands++; commandPacket->m_commands[bytesUsed++] = (unsigned char)msg->getType(); commandPacket->m_commands[bytesUsed++] = msg->getArgumentCount(); for (int i=0; igetArgumentCount(); ++i) { memcpy((unsigned char *)(commandPacket->m_commands + bytesUsed), (unsigned char *)msg->getArgument(i), sizeofMessageArg); bytesUsed += sizeofMessageArg; } //DEBUG_ASSERTCRASH(bytesUsed + sizeof(CommandPacketHeader) < MAX_MESSAGE_LEN, ("Memory overwrite constructing command packet!")); //DEBUG_LOG(("Memory overwrite constructing command packet!")); return true; } /** * TheNetwork calls GetCommandPacket to get commands to send. * CommandPacket *GetCommandPacket(void) { commandBuf[0] = MSGTYPE_COMMANDCOUNT; return commandPacket; } //==================================================================================== */