| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /*
- ** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "GameNetwork/NetCommandList.h"
- #include "GameNetwork/NetworkUtil.h"
- /**
- * Constructor.
- */
- NetCommandList::NetCommandList() {
- m_first = NULL;
- m_last = NULL;
- m_lastMessageInserted = NULL;
- }
- /**
- * Destructor.
- */
- NetCommandList::~NetCommandList() {
- reset();
- }
- /**
- * Append the given list of commands to this list.
- */
- void NetCommandList::appendList(NetCommandList *list) {
- if (list == NULL) {
- return;
- }
- // Need to do it this way because of the reference counting that needs to happen in appendMessage.
- NetCommandRef *msg = list->getFirstMessage();
- NetCommandRef *next = NULL;
- while (msg != NULL) {
- next = msg->getNext();
- NetCommandRef *temp = addMessage(msg->getCommand());
- if (temp != NULL) {
- temp->setRelay(msg->getRelay());
- }
- msg = next;
- }
- }
- /**
- * Return the first message in this list.
- */
- NetCommandRef *NetCommandList::getFirstMessage() {
- return m_first;
- }
- /**
- * Remove the given message from this list.
- */
- void NetCommandList::removeMessage(NetCommandRef *msg) {
- if (m_lastMessageInserted == msg) {
- m_lastMessageInserted = msg->getNext();
- }
- if (msg->getPrev() != NULL) {
- msg->getPrev()->setNext(msg->getNext());
- }
- if (msg->getNext() != NULL) {
- msg->getNext()->setPrev(msg->getPrev());
- }
- if (msg == m_first) {
- m_first = msg->getNext();
- }
- if (msg == m_last) {
- m_last = msg->getPrev();
- }
- msg->setNext(NULL);
- msg->setPrev(NULL);
- }
- /**
- * Initialize the list.
- */
- void NetCommandList::init() {
- reset();
- }
- /**
- * Reset the contents of this list.
- */
- void NetCommandList::reset() {
- NetCommandRef *temp = m_first;
- while (m_first != NULL) {
- temp = m_first->getNext();
- m_first->setNext(NULL);
- m_first->setPrev(NULL);
- m_first->deleteInstance();
- m_first = temp;
- }
- m_last = NULL;
- m_lastMessageInserted = NULL;
- }
- /**
- * Insert sorts msg. Assumes that all the previous message inserts were done using this function.
- * The message is sorted in based first on command type, then player id, and then command id.
- */
- NetCommandRef * NetCommandList::addMessage(NetCommandMsg *cmdMsg) {
- if (cmdMsg == NULL) {
- DEBUG_ASSERTCRASH(cmdMsg != NULL, ("NetCommandList::addMessage - command message was NULL"));
- return NULL;
- }
- // UnsignedInt id = cmdMsg->getID();
- NetCommandRef *msg = NEW_NETCOMMANDREF(cmdMsg);
- if (m_first == NULL) {
- // this is the first node, so we don't have to worry about ordering it.
- m_first = msg;
- m_last = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
- if (m_lastMessageInserted != NULL) {
- // Messages that are inserted in order should just be put in one right after the other.
- // So saving the placement of the last message inserted can give us a huge boost in
- // efficiency.
- NetCommandRef *theNext = m_lastMessageInserted->getNext();
- if ((m_lastMessageInserted->getCommand()->getNetCommandType() == msg->getCommand()->getNetCommandType()) &&
- (m_lastMessageInserted->getCommand()->getPlayerID() == msg->getCommand()->getPlayerID()) &&
- (m_lastMessageInserted->getCommand()->getID() < msg->getCommand()->getID()) &&
- ((theNext == NULL) || ((theNext->getCommand()->getNetCommandType() > msg->getCommand()->getNetCommandType()) ||
- (theNext->getCommand()->getPlayerID() > msg->getCommand()->getPlayerID()) ||
- (theNext->getCommand()->getID() > msg->getCommand()->getID())))) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_lastMessageInserted->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- if (theNext == NULL) {
- // this means that m_lastMessageInserted == m_last, so m_last should point to the msg that is being inserted.
- msg->setNext(m_lastMessageInserted->getNext());
- msg->setPrev(m_lastMessageInserted);
- m_lastMessageInserted->setNext(msg);
- m_lastMessageInserted = msg;
- m_last = msg;
- } else {
- msg->setNext(m_lastMessageInserted->getNext());
- msg->setPrev(m_lastMessageInserted);
- m_lastMessageInserted->setNext(msg);
- msg->getNext()->setPrev(msg);
- m_lastMessageInserted = msg;
- }
- return msg;
- }
- }
-
- if (msg->getCommand()->getNetCommandType() > m_last->getCommand()->getNetCommandType()) {
- // easy optimization for a command that goes at the end of the list
- // since they are likely to be added in order.
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- msg->setPrev(m_last);
- msg->setNext(NULL);
- m_last->setNext(msg);
- m_last = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
-
- if (msg->getCommand()->getNetCommandType() < m_first->getCommand()->getNetCommandType()) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- // The command goes at the head of the list.
- msg->setNext(m_first);
- msg->setPrev(NULL);
- m_first->setPrev(msg);
- m_first = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
-
-
- // Find the start of the command type we're looking for.
- NetCommandRef *tempmsg = m_first;
- while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() > tempmsg->getCommand()->getNetCommandType())) {
- tempmsg = tempmsg->getNext();
- }
- if (tempmsg == NULL) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- // message goes at the end of the list.
- msg->setPrev(m_last);
- msg->setNext(NULL);
- m_last->setNext(msg);
- m_last = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
- // Now find the player position. munkee.
- while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() > tempmsg->getCommand()->getPlayerID())) {
- tempmsg = tempmsg->getNext();
- }
- if (tempmsg == NULL) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- // message goes at the end of the list.
- msg->setPrev(m_last);
- msg->setNext(NULL);
- m_last->setNext(msg);
- m_last = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
- // Find the position within the player's section based on the command ID.
- // If the command type doesn't require a command ID, sort by whatever it should be sorted by.
- while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() == tempmsg->getCommand()->getPlayerID()) && (msg->getCommand()->getSortNumber() > tempmsg->getCommand()->getSortNumber())) {
- tempmsg = tempmsg->getNext();
- }
- if (tempmsg == NULL) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- // This message goes at the end of the list.
- msg->setPrev(m_last);
- msg->setNext(NULL);
- m_last->setNext(msg);
- m_last = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
- if (tempmsg == m_first) {
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- return NULL;
- }
- // This message goes at the head of the list.
- msg->setNext(m_first);
- msg->setPrev(NULL);
- m_first->setPrev(msg);
- m_first = msg;
- m_lastMessageInserted = msg;
- return msg;
- }
- // Make sure this command isn't already in the list.
- if (isEqualCommandMsg(tempmsg->getCommand(), msg->getCommand())) {
- // This command is already in the list, don't duplicate it.
- msg->deleteInstance();
- msg = NULL;
- return NULL;
- }
- // Insert message before tempmsg.
- msg->setNext(tempmsg);
- msg->setPrev(tempmsg->getPrev());
- msg->getPrev()->setNext(msg);
- tempmsg->setPrev(msg);
- m_lastMessageInserted = msg;
- return msg;
- }
- Int NetCommandList::length() {
- Int retval = 0;
- NetCommandRef *temp = m_first;
- while (temp != NULL) {
- ++retval;
- temp = temp->getNext();
- }
- return retval;
- }
- /**
- * This is really inefficient, but we can probably get away with it because
- * there shouldn't be too many messages for any given frame.
- */
- NetCommandRef * NetCommandList::findMessage(NetCommandMsg *msg) {
- NetCommandRef *retval = m_first;
- while ((retval != NULL) && (isEqualCommandMsg(retval->getCommand(), msg) == FALSE)) {
- retval = retval->getNext();
- }
- return retval;
- }
- NetCommandRef * NetCommandList::findMessage(UnsignedShort commandID, UnsignedByte playerID) {
- NetCommandRef *retval = m_first;
- while (retval != NULL) {
- if (DoesCommandRequireACommandID(retval->getCommand()->getNetCommandType())) {
- if ((retval->getCommand()->getID() == commandID) && (retval->getCommand()->getPlayerID() == playerID)) {
- return retval;
- }
- }
- retval = retval->getNext();
- }
- return retval;
- }
- Bool NetCommandList::isEqualCommandMsg(NetCommandMsg *msg1, NetCommandMsg *msg2) {
- if (DoesCommandRequireACommandID(msg1->getNetCommandType()) != DoesCommandRequireACommandID(msg2->getNetCommandType())) {
- return FALSE;
- }
- // At this point we know that the commands both do or do not require a command id.
- // Do or do not, there is no try.
- if (DoesCommandRequireACommandID(msg1->getNetCommandType())) {
- // Are the commands from the same player?
- if (msg1->getPlayerID() != msg2->getPlayerID()) {
- return FALSE;
- }
- // Do they have the same command ID?
- if (msg1->getID() != msg2->getID()) {
- return FALSE;
- }
- return TRUE;
- }
- // If we've gotten this far, we know that the commands do not require a command id.
- // So now our equality checking becomes type-specific.
- // Are they the same type?
- if (msg1->getNetCommandType() != msg2->getNetCommandType()) {
- return FALSE;
- }
- // Are they from the same player?
- if (msg1->getPlayerID() != msg2->getPlayerID()) {
- return FALSE;
- }
- // They are the same type and from the same player.
- // Time for the type specific stuff.
- if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) {
- NetAckStage1CommandMsg *ack1 = (NetAckStage1CommandMsg *)msg1;
- NetAckStage1CommandMsg *ack2 = (NetAckStage1CommandMsg *)msg2;
-
- if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
- return FALSE;
- }
- if (ack1->getCommandID() != ack2->getCommandID()) {
- return FALSE;
- }
- return TRUE;
- }
- // They are the same type and from the same player.
- // Time for the type specific stuff.
- if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) {
- NetAckStage2CommandMsg *ack1 = (NetAckStage2CommandMsg *)msg1;
- NetAckStage2CommandMsg *ack2 = (NetAckStage2CommandMsg *)msg2;
-
- if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
- return FALSE;
- }
- if (ack1->getCommandID() != ack2->getCommandID()) {
- return FALSE;
- }
- return TRUE;
- }
- // They are the same type and from the same player.
- // Time for the type specific stuff.
- if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
- NetAckBothCommandMsg *ack1 = (NetAckBothCommandMsg *)msg1;
- NetAckBothCommandMsg *ack2 = (NetAckBothCommandMsg *)msg2;
-
- if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
- return FALSE;
- }
- if (ack1->getCommandID() != ack2->getCommandID()) {
- return FALSE;
- }
- return TRUE;
- }
- return FALSE;
- }
|