/*
** 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;
}
//====================================================================================
*/