From f066518c6655e6f0a1a8dd2add41955b49994027 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:47:25 +0100 Subject: [PATCH 01/17] feat(network): Exchange client patch identification --- .../GameEngine/Include/GameNetwork/GameInfo.h | 4 + .../GameEngine/Include/GameNetwork/LANAPI.h | 20 ++++ .../Include/GameNetwork/LANPlayer.h | 5 +- .../Source/GameNetwork/GameInfo.cpp | 7 ++ .../GameEngine/Source/GameNetwork/LANAPI.cpp | 20 ++++ .../Source/GameNetwork/LANAPIhandlers.cpp | 106 ++++++++++++++++++ 6 files changed, 161 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h index 923641a3b1a..3124a9517a4 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h @@ -125,6 +125,9 @@ class GameSlot void mute( Bool isMuted ) { m_isMuted = isMuted; } Bool isMuted( void ) const { return m_isMuted; } + + void setPatchVersion(UnsignedInt patchVersion) { m_patchVersion = patchVersion; } + UnsignedInt getPatchVersion() const { return m_patchVersion; } protected: SlotState m_state; Bool m_isAccepted; @@ -143,6 +146,7 @@ class GameSlot FirewallHelperClass::FirewallBehaviorType m_NATBehavior; ///< The NAT behavior for this slot's player. UnsignedInt m_lastFrameInGame; // only valid for human players Bool m_disconnected; // only valid for human players + UnsignedInt m_patchVersion; ///< Community made product version }; /** diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h index d968b0db3d2..bd97cc7fbdf 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h @@ -277,6 +277,13 @@ class LANAPI : public LANAPIInterface void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); void handleInActive( LANMessage *msg, UnsignedInt senderIP ); + void handlePatchInfo(Int messageType, UnsignedInt senderIP, UnicodeString gameName); + void handleGameRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); + void handleGameAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); + void handleLobbyRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); + void handleLobbyAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); + void handleMatchRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); + void handleMatchAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); }; @@ -313,6 +320,14 @@ struct LANMessage MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey. MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address + + // Community patch + MSG_GAME_REQUEST_PATCH_INFO = 1000, + MSG_GAME_ACKNOWLEDGE_PATCH_INFO, + MSG_LOBBY_REQUEST_PATCH_INFO, + MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO, + MSG_MATCH_REQUEST_PATCH_INFO, + MSG_MATCH_ACKNOWLEDGE_PATCH_INFO, } messageType; WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience @@ -407,6 +422,11 @@ struct LANMessage char options[m_lanMaxOptionsLength+1]; } GameOptions; + struct + { + WideChar gameName[g_lanGameNameLength + 1]; + UnsignedInt patchVersion; + } PatchInfo; }; }; #pragma pack(pop) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h index 5c0f146d8a1..0cc6b515e49 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h @@ -35,7 +35,7 @@ class LANPlayer { public: - LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; } + LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; m_patchVersion = 0; } // Access functions inline UnicodeString getName( void ) { return m_name; } @@ -52,6 +52,8 @@ class LANPlayer inline void setNext( LANPlayer *next ) { m_next = next; } inline UnsignedInt getIP( void ) { return m_IP; } inline void setIP( UnsignedInt IP ) { m_IP = IP; } + inline void setPatchVersion(UnsignedInt patchVersion) { m_patchVersion = patchVersion; } + inline UnsignedInt getPatchVersion() const { return m_patchVersion; } protected: UnicodeString m_name; ///< Player name @@ -60,4 +62,5 @@ class LANPlayer UnsignedInt m_lastHeard; ///< The last time we heard from this player (for timeout purposes) LANPlayer *m_next; ///< Linked list pointer UnsignedInt m_IP; ///< Player's IP + UnsignedInt m_patchVersion; ///< Community made product version }; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp index 8e8bab5b06b..2b63d661756 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -73,6 +73,7 @@ void GameSlot::reset() m_origPlayerTemplate = -1; m_origStartPos = -1; m_origColor = -1; + m_patchVersion = 0; } void GameSlot::saveOffOriginalInfo( void ) @@ -1484,7 +1485,13 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) //DEBUG_LOG(("ParseAsciiStringToGameInfo - game options all good, setting info")); for(Int i = 0; igetConstSlot(i)->getState() == SLOT_PLAYER && newSlot[i].getState() == SLOT_PLAYER) + newSlot[i].setPatchVersion(game->getConstSlot(i)->getPatchVersion()); + game->setSlot(i,newSlot[i]); + } game->setMap(mapName); game->setMapCRC(mapCRC); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp index 87963c5beff..22f694e66ae 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -425,6 +425,26 @@ void LANAPI::update( void ) handleInActive( msg, senderIP ); break; + // TheSuperHackers @feature Caball009 06/11/2025 Exchange patch information with other players. + case LANMessage::MSG_GAME_REQUEST_PATCH_INFO: + handleGameRequestPatchInfo(msg, senderIP); + break; + case LANMessage::MSG_GAME_ACKNOWLEDGE_PATCH_INFO: + handleGameAcknowledgePatchInfo(msg, senderIP); + break; + case LANMessage::MSG_LOBBY_REQUEST_PATCH_INFO: + handleLobbyRequestPatchInfo(msg, senderIP); + break; + case LANMessage::MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO: + handleLobbyAcknowledgePatchInfo(msg, senderIP); + break; + case LANMessage::MSG_MATCH_REQUEST_PATCH_INFO: + handleMatchRequestPatchInfo(msg, senderIP); + break; + case LANMessage::MSG_MATCH_ACKNOWLEDGE_PATCH_INFO: + handleMatchAcknowledgePatchInfo(msg, senderIP); + break; + default: DEBUG_LOG(("Unknown LAN message type %d", msg->messageType)); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 0e5675b5c95..ceb9fcfc40b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -36,10 +36,107 @@ #include "Common/GlobalData.h" #include "Common/QuotedPrintable.h" #include "Common/UserPreferences.h" +#include "Common/version.h" #include "GameNetwork/LANAPI.h" #include "GameNetwork/LANAPICallbacks.h" #include "GameClient/MapUtil.h" +void LANAPI::handlePatchInfo(Int messageType, UnsignedInt senderIP, UnicodeString gameName) +{ + LANMessage msg; + fillInLANMessage(&msg); + msg.messageType = (LANMessage::Type)messageType; + wcslcpy(msg.PatchInfo.gameName, gameName.str(), ARRAY_SIZE(msg.PatchInfo.gameName)); + msg.PatchInfo.patchVersion = TheVersion->getVersionNumber(); + + sendMessage(&msg, senderIP); +} + +void LANAPI::handleGameRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!AmIHost()) + return; + + // acknowledge as game host a request for patch information by a player in the lobby + handlePatchInfo(LANMessage::MSG_GAME_ACKNOWLEDGE_PATCH_INFO, senderIP, m_currentGame->getName()); +} + +void LANAPI::handleGameAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!m_inLobby) + return; + + LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName)); + if (!game) + return; + + if (game->getIP(0) != senderIP) + return; + + // a game host has acknowledged our request for patch information + game->getSlot(0)->setPatchVersion(msg->PatchInfo.patchVersion); + + // update game list with colored names + OnGameList(m_games); +} + +void LANAPI::handleLobbyRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!m_inLobby) + return; + + // acknowledge a request for patch information by a fellow player in the lobby + handlePatchInfo(LANMessage::MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO, senderIP, UnicodeString()); +} + +void LANAPI::handleLobbyAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!m_inLobby) + return; + + LANPlayer *player = LookupPlayer(senderIP); + if (!player) + return; + + // a fellow player in the lobby has acknowledged our request for patch information + player->setPatchVersion(msg->PatchInfo.patchVersion); + + // update player list with colored names + OnPlayerList(m_lobbyPlayers); +} + +void LANAPI::handleMatchRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!m_currentGame) + return; + + // acknowledge a request for patch information by a fellow player in the game + handlePatchInfo(LANMessage::MSG_MATCH_ACKNOWLEDGE_PATCH_INFO, senderIP, m_currentGame->getName()); + + // treat request for patch information as acknowledgement + handleMatchAcknowledgePatchInfo(msg, senderIP); +} + +void LANAPI::handleMatchAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +{ + if (!m_currentGame) + return; + + for (Int i = 0; i < MAX_SLOTS; ++i) + { + if (!m_currentGame->getLANSlot(i)->isHuman() || m_currentGame->getIP(i) != senderIP) + continue; + + // a fellow player in the game has acknowledged our request for patch information + m_currentGame->getSlot(i)->setPatchVersion(msg->PatchInfo.patchVersion); + + // update player list with colored names + lanUpdateSlotList(); + + break; + } +} + void LANAPI::handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ) { if (m_inLobby) @@ -139,6 +236,9 @@ void LANAPI::handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP ) game = NEW LANGameInfo; game->setName(UnicodeString(msg->GameInfo.gameName)); addGame(game); + + // TheSuperHackers @feature Caball009 06/11/2025 Request a game host to send patch information. + handlePatchInfo(LANMessage::MSG_GAME_REQUEST_PATCH_INFO, senderIP, game->getName()); } Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options)); game->setGameInProgress(msg->GameInfo.inProgress); @@ -165,6 +265,9 @@ void LANAPI::handleLobbyAnnounce( LANMessage *msg, UnsignedInt senderIP ) { player = NEW LANPlayer; player->setIP(senderIP); + + // TheSuperHackers @feature Caball009 06/11/2025 Request a player in the lobby to send patch information. + handlePatchInfo(LANMessage::MSG_LOBBY_REQUEST_PATCH_INFO, senderIP, UnicodeString()); } else { @@ -401,6 +504,9 @@ void LANAPI::handleJoinAccept( LANMessage *msg, UnsignedInt senderIP ) OnGameJoin(RET_OK, m_currentGame); //DEBUG_ASSERTCRASH(false, ("setting host to %ls@%ls", m_currentGame->getLANSlot(0)->getUser()->getLogin().str(), // m_currentGame->getLANSlot(0)->getUser()->getHost().str())); + + // TheSuperHackers @feature Caball009 06/11/2025 Request players in the match to send patch information. + handlePatchInfo(LANMessage::MSG_MATCH_REQUEST_PATCH_INFO, 0, m_currentGame->getName()); } m_pendingAction = ACT_NONE; m_expiration = 0; From c4e3f1d4419a6354076fcf448a9c91a1cdd3a3a4 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:49:20 +0100 Subject: [PATCH 02/17] feat(client): Distinguish clients based on patch identification --- .../Include/GameNetwork/LANAPICallbacks.h | 4 +++ .../GameClient/GUI/GUICallbacks/Diplomacy.cpp | 32 +++++++++++++------ .../GameEngine/Source/GameNetwork/GUIUtil.cpp | 7 ++++ .../Source/GameNetwork/LANAPICallbacks.cpp | 28 +++++++++------- .../Source/GameNetwork/LANGameInfo.cpp | 7 +++- 5 files changed, 56 insertions(+), 22 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h index 394b75dedd8..30d12fbfece 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h @@ -68,6 +68,10 @@ extern const Color chatSystemColor; extern const Color chatSystemColor; extern const Color acceptTrueColor; extern const Color acceptFalseColor; +extern const Color gameColorPatchVersion; +extern const Color gameInProgressColorPatchVersion; +extern const Color playerColorPatchVersion; +extern const Color playerGrayedColorPatchVersion; void lanUpdateSlotList( void ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index da73cce360b..fb1ba90c64b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -36,6 +36,7 @@ #include "Common/PlayerList.h" #include "Common/PlayerTemplate.h" #include "Common/Recorder.h" +#include "Common/version.h" #include "GameClient/AnimateWindowManager.h" #include "GameClient/Diplomacy.h" #include "GameClient/DisconnectMenu.h" @@ -532,24 +533,28 @@ void PopulateInGameDiplomacyPopup( void ) if (staticTextStatus[rowNum]) { staticTextStatus[rowNum]->winHide(FALSE); + + Color frontColor = 0; + UnicodeString text; + if (isInGame) { if (isAlive) { - staticTextStatus[rowNum]->winSetEnabledTextColors( aliveColor, backColor ); - GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerAlive")); + frontColor = aliveColor; + text = TheGameText->fetch("GUI:PlayerAlive"); } else { if (isObserver) { - staticTextStatus[rowNum]->winSetEnabledTextColors( observerInGameColor, backColor ); - GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerObserver")); + frontColor = observerInGameColor; + text = TheGameText->fetch("GUI:PlayerObserver"); } else { - staticTextStatus[rowNum]->winSetEnabledTextColors( deadColor, backColor ); - GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerDead")); + frontColor = deadColor; + text = TheGameText->fetch("GUI:PlayerDead"); } } } @@ -558,15 +563,22 @@ void PopulateInGameDiplomacyPopup( void ) // not in game if (isObserver) { - staticTextStatus[rowNum]->winSetEnabledTextColors( observerGoneColor, backColor ); - GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerObserverGone")); + frontColor = observerGoneColor; + text = TheGameText->fetch("GUI:PlayerObserverGone"); } else { - staticTextStatus[rowNum]->winSetEnabledTextColors( goneColor, backColor ); - GadgetStaticTextSetText(staticTextStatus[rowNum], TheGameText->fetch("GUI:PlayerGone")); + frontColor = goneColor; + text = TheGameText->fetch("GUI:PlayerGone"); } } + + // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using a patched client version. + if (slot->isHuman() && (TheGameInfo->getLocalSlotNum() == slotNum || slot->getPatchVersion() > 0)) + text.format(L"%s [%s]", text.str(), TheVersion->getUnicodeGitTagOrHash().str()); + + staticTextStatus[rowNum]->winSetEnabledTextColors(frontColor, backColor); + GadgetStaticTextSetText(staticTextStatus[rowNum], text); } slotNumInRow[rowNum++] = slotNum; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp index 40eab1badfe..243b012c2e5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp @@ -423,6 +423,13 @@ void UpdateSlotList( GameInfo *myGame, GameWindow *comboPlayer[], } if(slot->isHuman()) { + if (i == myGame->getLocalSlotNum() || myGame->getSlot(i)->getPatchVersion() > 0) + { + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. + GadgetComboBoxSetEnabledTextColors(comboPlayer[i], playerColorPatchVersion, GameMakeColor(0, 0, 0, 255)); + GadgetComboBoxSetDisabledTextColors(comboPlayer[i], playerGrayedColorPatchVersion, GameMakeColor(0, 0, 0, 255)); + } + UnicodeString newName = slot->getName(); UnicodeString oldName = GadgetComboBoxGetText(comboPlayer[i]); if (comboPlayer[i] && newName.compare(oldName)) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index 90e344331d5..ea9046d4680 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp @@ -52,16 +52,20 @@ extern Bool LANbuttonPushed; //Colors used for the chat dialogs -const Color playerColor = GameMakeColor(255,255,255,255); -const Color gameColor = GameMakeColor(255,255,255,255); -const Color gameInProgressColor = GameMakeColor(128,128,128,255); -const Color chatNormalColor = GameMakeColor(50,215,230,255); -const Color chatActionColor = GameMakeColor(255,0,255,255); -const Color chatLocalNormalColor = GameMakeColor(255,128,0,255); -const Color chatLocalActionColor = GameMakeColor(128,255,255,255); -const Color chatSystemColor = GameMakeColor(255,255,255,255); -const Color acceptTrueColor = GameMakeColor(0,255,0,255); -const Color acceptFalseColor = GameMakeColor(255,0,0,255); +const Color playerColor = GameMakeColor(255,255,255,255); +const Color gameColor = GameMakeColor(255,255,255,255); +const Color gameInProgressColor = GameMakeColor(128,128,0,255); +const Color chatNormalColor = GameMakeColor(50,215,230,255); +const Color chatActionColor = GameMakeColor(255,0,255,255); +const Color chatLocalNormalColor = GameMakeColor(255,128,0,255); +const Color chatLocalActionColor = GameMakeColor(128,255,255,255); +const Color chatSystemColor = GameMakeColor(255,255,255,255); +const Color acceptTrueColor = GameMakeColor(0,255,0,255); +const Color acceptFalseColor = GameMakeColor(255,0,0,255); +const Color gameColorPatchVersion = GameMakeColor(255,255,0,255); +const Color gameInProgressColorPatchVersion = GameMakeColor(192,192,0,255); +const Color playerColorPatchVersion = gameColorPatchVersion; +const Color playerGrayedColorPatchVersion = gameInProgressColorPatchVersion; UnicodeString LANAPIInterface::getErrorStringFromReturnType( ReturnType ret ) @@ -640,7 +644,9 @@ void LANAPI::OnPlayerList( LANPlayer *playerList ) LANPlayer *player = m_lobbyPlayers; while (player) { - Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), playerColor, -1, -1); + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. + const Color color = (player->getPatchVersion() > 0 || m_localIP == player->getIP()) ? playerColorPatchVersion : playerColor; + const Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), color, -1, -1); GadgetListBoxSetItemData(listboxPlayers, (void *)player->getIP(),addedIndex, 0 ); if (selectedIP == player->getIP()) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp index 055482fc14b..a7f2ed69f35 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp @@ -226,7 +226,12 @@ void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList ) { txtGName.concat(L"]"); } - Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, (gameList->isGameInProgress())?gameInProgressColor:gameColor, -1, -1); + + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for games that are using a patched client version. + const Color color = (gameList->getSlot(0)->getPatchVersion() > 0) + ? ((gameList->isGameInProgress()) ? gameInProgressColorPatchVersion : gameColorPatchVersion) + : ((gameList->isGameInProgress()) ? gameInProgressColor : gameColor); + const Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, color, -1, -1); GadgetListBoxSetItemData(gameListbox, (void *)gameList, addedIndex, 0 ); if (selectedPtr == gameList) From f0b846051c038e901fda04a1e6a55f9e6cfdd7a9 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 8 Nov 2025 20:16:37 +0100 Subject: [PATCH 03/17] Addressed code review comments. Renamed 'patch' to 'product' and added more information to the product information that's exchanged between clients. --- .../GameEngine/Include/GameNetwork/GameInfo.h | 6 +- .../GameEngine/Include/GameNetwork/LANAPI.h | 38 ++++++----- .../Include/GameNetwork/LANPlayer.h | 8 +-- .../GameClient/GUI/GUICallbacks/Diplomacy.cpp | 2 +- .../GameEngine/Source/GameNetwork/GUIUtil.cpp | 2 +- .../Source/GameNetwork/GameInfo.cpp | 4 +- .../GameEngine/Source/GameNetwork/LANAPI.cpp | 38 +++++++---- .../Source/GameNetwork/LANAPICallbacks.cpp | 2 +- .../Source/GameNetwork/LANAPIhandlers.cpp | 67 ++++++++++--------- .../Source/GameNetwork/LANGameInfo.cpp | 2 +- 10 files changed, 93 insertions(+), 76 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h index 3124a9517a4..c64f94da08c 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h @@ -126,8 +126,8 @@ class GameSlot void mute( Bool isMuted ) { m_isMuted = isMuted; } Bool isMuted( void ) const { return m_isMuted; } - void setPatchVersion(UnsignedInt patchVersion) { m_patchVersion = patchVersion; } - UnsignedInt getPatchVersion() const { return m_patchVersion; } + void setProductVersion(UnsignedInt productVersion) { m_productVersion = productVersion; } + UnsignedInt getProductVersion() const { return m_productVersion; } protected: SlotState m_state; Bool m_isAccepted; @@ -146,7 +146,7 @@ class GameSlot FirewallHelperClass::FirewallBehaviorType m_NATBehavior; ///< The NAT behavior for this slot's player. UnsignedInt m_lastFrameInGame; // only valid for human players Bool m_disconnected; // only valid for human players - UnsignedInt m_patchVersion; ///< Community made product version + UnsignedInt m_productVersion; ///< Community made product version }; /** diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h index bd97cc7fbdf..28d3045289c 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h @@ -198,6 +198,7 @@ class LANAPI : public LANAPIInterface // Misc utility functions virtual LANGameInfo * LookupGame( UnicodeString gameName ); ///< return a pointer to a game we know about virtual LANGameInfo * LookupGameByListOffset( Int offset ); ///< return a pointer to a game we know about + virtual LANGameInfo * LookupGameByHost( UnsignedInt hostIP ); ///< return a pointer to a game we know about virtual LANPlayer * LookupPlayer( UnsignedInt playerIP ); ///< return a pointer to a player we know about virtual Bool SetLocalIP( UnsignedInt localIP ); ///< For multiple NIC machines virtual void SetLocalIP( AsciiString localIP ); ///< For multiple NIC machines @@ -277,13 +278,13 @@ class LANAPI : public LANAPIInterface void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); void handleInActive( LANMessage *msg, UnsignedInt senderIP ); - void handlePatchInfo(Int messageType, UnsignedInt senderIP, UnicodeString gameName); - void handleGameRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); - void handleGameAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); - void handleLobbyRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); - void handleLobbyAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); - void handleMatchRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP); - void handleMatchAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP); + void sendProductInfoMessage(Int messageType, UnsignedInt senderIP); + void handleGameProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); + void handleGameProductInfoResponse(LANMessage *msg, UnsignedInt senderIP); + void handleLobbyProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); + void handleLobbyProductInfoResponse(LANMessage *msg, UnsignedInt senderIP); + void handleMatchProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); + void handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderIP); }; @@ -321,13 +322,13 @@ struct LANMessage MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address - // Community patch - MSG_GAME_REQUEST_PATCH_INFO = 1000, - MSG_GAME_ACKNOWLEDGE_PATCH_INFO, - MSG_LOBBY_REQUEST_PATCH_INFO, - MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO, - MSG_MATCH_REQUEST_PATCH_INFO, - MSG_MATCH_ACKNOWLEDGE_PATCH_INFO, + // Community Product + MSG_GAME_REQUEST_PRODUCT_INFO = 1000, + MSG_GAME_RESPONSE_PRODUCT_INFO, + MSG_LOBBY_REQUEST_PRODUCT_INFO, + MSG_LOBBY_RESPONSE_PRODUCT_INFO, + MSG_MATCH_REQUEST_PRODUCT_INFO, + MSG_MATCH_RESPONSE_PRODUCT_INFO, } messageType; WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience @@ -424,9 +425,12 @@ struct LANMessage struct { - WideChar gameName[g_lanGameNameLength + 1]; - UnsignedInt patchVersion; - } PatchInfo; + UnsignedInt exeHash; + UnsignedInt iniHash; + UnsignedInt productVersion; + Char gitTagOrHash[33]; + WideChar productName[129]; + } ProductInfo; }; }; #pragma pack(pop) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h index 0cc6b515e49..1aaf56d64bb 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h @@ -35,7 +35,7 @@ class LANPlayer { public: - LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; m_patchVersion = 0; } + LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; m_productVersion = 0; } // Access functions inline UnicodeString getName( void ) { return m_name; } @@ -52,8 +52,8 @@ class LANPlayer inline void setNext( LANPlayer *next ) { m_next = next; } inline UnsignedInt getIP( void ) { return m_IP; } inline void setIP( UnsignedInt IP ) { m_IP = IP; } - inline void setPatchVersion(UnsignedInt patchVersion) { m_patchVersion = patchVersion; } - inline UnsignedInt getPatchVersion() const { return m_patchVersion; } + inline void setProductVersion(UnsignedInt productVersion) { m_productVersion = productVersion; } + inline UnsignedInt getProductVersion() const { return m_productVersion; } protected: UnicodeString m_name; ///< Player name @@ -62,5 +62,5 @@ class LANPlayer UnsignedInt m_lastHeard; ///< The last time we heard from this player (for timeout purposes) LANPlayer *m_next; ///< Linked list pointer UnsignedInt m_IP; ///< Player's IP - UnsignedInt m_patchVersion; ///< Community made product version + UnsignedInt m_productVersion; ///< Community made product version }; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index fb1ba90c64b..e8143d092fb 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -574,7 +574,7 @@ void PopulateInGameDiplomacyPopup( void ) } // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using a patched client version. - if (slot->isHuman() && (TheGameInfo->getLocalSlotNum() == slotNum || slot->getPatchVersion() > 0)) + if (slot->isHuman() && (TheGameInfo->getLocalSlotNum() == slotNum || slot->getProductVersion() > 0)) text.format(L"%s [%s]", text.str(), TheVersion->getUnicodeGitTagOrHash().str()); staticTextStatus[rowNum]->winSetEnabledTextColors(frontColor, backColor); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp index 243b012c2e5..05d6a589e3e 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp @@ -423,7 +423,7 @@ void UpdateSlotList( GameInfo *myGame, GameWindow *comboPlayer[], } if(slot->isHuman()) { - if (i == myGame->getLocalSlotNum() || myGame->getSlot(i)->getPatchVersion() > 0) + if (i == myGame->getLocalSlotNum() || myGame->getSlot(i)->getProductVersion() > 0) { // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. GadgetComboBoxSetEnabledTextColors(comboPlayer[i], playerColorPatchVersion, GameMakeColor(0, 0, 0, 255)); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp index 2b63d661756..737b5652906 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -73,7 +73,7 @@ void GameSlot::reset() m_origPlayerTemplate = -1; m_origStartPos = -1; m_origColor = -1; - m_patchVersion = 0; + m_productVersion = 0; } void GameSlot::saveOffOriginalInfo( void ) @@ -1488,7 +1488,7 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) { // retain the patch version if a slot is still occupied by the same player if (game->getConstSlot(i)->getState() == SLOT_PLAYER && newSlot[i].getState() == SLOT_PLAYER) - newSlot[i].setPatchVersion(game->getConstSlot(i)->getPatchVersion()); + newSlot[i].setProductVersion(game->getConstSlot(i)->getProductVersion()); game->setSlot(i,newSlot[i]); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp index 22f694e66ae..f7f56d1e662 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -425,24 +425,24 @@ void LANAPI::update( void ) handleInActive( msg, senderIP ); break; - // TheSuperHackers @feature Caball009 06/11/2025 Exchange patch information with other players. - case LANMessage::MSG_GAME_REQUEST_PATCH_INFO: - handleGameRequestPatchInfo(msg, senderIP); + // TheSuperHackers @feature Caball009 06/11/2025 Exchange product information with other players. + case LANMessage::MSG_GAME_REQUEST_PRODUCT_INFO: + handleGameProductInfoRequest(msg, senderIP); break; - case LANMessage::MSG_GAME_ACKNOWLEDGE_PATCH_INFO: - handleGameAcknowledgePatchInfo(msg, senderIP); + case LANMessage::MSG_GAME_RESPONSE_PRODUCT_INFO: + handleGameProductInfoResponse(msg, senderIP); break; - case LANMessage::MSG_LOBBY_REQUEST_PATCH_INFO: - handleLobbyRequestPatchInfo(msg, senderIP); + case LANMessage::MSG_LOBBY_REQUEST_PRODUCT_INFO: + handleLobbyProductInfoRequest(msg, senderIP); break; - case LANMessage::MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO: - handleLobbyAcknowledgePatchInfo(msg, senderIP); + case LANMessage::MSG_LOBBY_RESPONSE_PRODUCT_INFO: + handleLobbyProductInfoResponse(msg, senderIP); break; - case LANMessage::MSG_MATCH_REQUEST_PATCH_INFO: - handleMatchRequestPatchInfo(msg, senderIP); + case LANMessage::MSG_MATCH_REQUEST_PRODUCT_INFO: + handleMatchProductInfoRequest(msg, senderIP); break; - case LANMessage::MSG_MATCH_ACKNOWLEDGE_PATCH_INFO: - handleMatchAcknowledgePatchInfo(msg, senderIP); + case LANMessage::MSG_MATCH_RESPONSE_PRODUCT_INFO: + handleMatchProductInfoResponse(msg, senderIP); break; default: @@ -1132,6 +1132,18 @@ LANGameInfo * LANAPI::LookupGameByListOffset( Int offset ) return theGame; // NULL means we didn't find anything. } +LANGameInfo * LANAPI::LookupGameByHost( UnsignedInt hostIP ) +{ + LANGameInfo* theGame = m_games; + + while (theGame && theGame->getHostIP() != hostIP) + { + theGame = theGame->getNext(); + } + + return theGame; // NULL means we didn't find anything. +} + void LANAPI::removeGame( LANGameInfo *game ) { LANGameInfo *g = m_games; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index ea9046d4680..dafd83bd0a9 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp @@ -645,7 +645,7 @@ void LANAPI::OnPlayerList( LANPlayer *playerList ) while (player) { // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. - const Color color = (player->getPatchVersion() > 0 || m_localIP == player->getIP()) ? playerColorPatchVersion : playerColor; + const Color color = (player->getProductVersion() > 0 || m_localIP == player->getIP()) ? playerColorPatchVersion : playerColor; const Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), color, -1, -1); GadgetListBoxSetItemData(listboxPlayers, (void *)player->getIP(),addedIndex, 0 ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index ceb9fcfc40b..79b524da675 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -41,55 +41,56 @@ #include "GameNetwork/LANAPICallbacks.h" #include "GameClient/MapUtil.h" -void LANAPI::handlePatchInfo(Int messageType, UnsignedInt senderIP, UnicodeString gameName) +void LANAPI::sendProductInfoMessage(Int messageType, UnsignedInt senderIP) { LANMessage msg; fillInLANMessage(&msg); msg.messageType = (LANMessage::Type)messageType; - wcslcpy(msg.PatchInfo.gameName, gameName.str(), ARRAY_SIZE(msg.PatchInfo.gameName)); - msg.PatchInfo.patchVersion = TheVersion->getVersionNumber(); + + msg.ProductInfo.exeHash = TheGlobalData->m_exeCRC; + msg.ProductInfo.iniHash = TheGlobalData->m_iniCRC; + msg.ProductInfo.productVersion = TheVersion->getVersionNumber(); + strlcpy(msg.ProductInfo.gitTagOrHash, TheVersion->getAsciiGitTagOrHash().str(), ARRAY_SIZE(msg.ProductInfo.gitTagOrHash)); + wcslcpy(msg.ProductInfo.productName, TheVersion->getUnicodeProductString().str(), ARRAY_SIZE(msg.ProductInfo.productName)); sendMessage(&msg, senderIP); } -void LANAPI::handleGameRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleGameProductInfoRequest(LANMessage *msg, UnsignedInt senderIP) { if (!AmIHost()) return; - // acknowledge as game host a request for patch information by a player in the lobby - handlePatchInfo(LANMessage::MSG_GAME_ACKNOWLEDGE_PATCH_INFO, senderIP, m_currentGame->getName()); + // acknowledge as game host a request for product information by a player in the lobby + sendProductInfoMessage(LANMessage::MSG_GAME_RESPONSE_PRODUCT_INFO, senderIP); } -void LANAPI::handleGameAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleGameProductInfoResponse(LANMessage *msg, UnsignedInt senderIP) { if (!m_inLobby) return; - LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName)); + LANGameInfo *game = LookupGameByHost(senderIP); if (!game) return; - if (game->getIP(0) != senderIP) - return; - - // a game host has acknowledged our request for patch information - game->getSlot(0)->setPatchVersion(msg->PatchInfo.patchVersion); + // a game host has acknowledged our request for product information + game->getSlot(0)->setProductVersion(msg->ProductInfo.productVersion); // update game list with colored names OnGameList(m_games); } -void LANAPI::handleLobbyRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleLobbyProductInfoRequest(LANMessage *msg, UnsignedInt senderIP) { if (!m_inLobby) return; - // acknowledge a request for patch information by a fellow player in the lobby - handlePatchInfo(LANMessage::MSG_LOBBY_ACKNOWLEDGE_PATCH_INFO, senderIP, UnicodeString()); + // acknowledge a request for product information by a fellow player in the lobby + sendProductInfoMessage(LANMessage::MSG_LOBBY_RESPONSE_PRODUCT_INFO, senderIP); } -void LANAPI::handleLobbyAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleLobbyProductInfoResponse(LANMessage *msg, UnsignedInt senderIP) { if (!m_inLobby) return; @@ -98,26 +99,26 @@ void LANAPI::handleLobbyAcknowledgePatchInfo(LANMessage *msg, UnsignedInt sender if (!player) return; - // a fellow player in the lobby has acknowledged our request for patch information - player->setPatchVersion(msg->PatchInfo.patchVersion); + // a fellow player in the lobby has acknowledged our request for product information + player->setProductVersion(msg->ProductInfo.productVersion); // update player list with colored names OnPlayerList(m_lobbyPlayers); } -void LANAPI::handleMatchRequestPatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleMatchProductInfoRequest(LANMessage *msg, UnsignedInt senderIP) { if (!m_currentGame) return; - // acknowledge a request for patch information by a fellow player in the game - handlePatchInfo(LANMessage::MSG_MATCH_ACKNOWLEDGE_PATCH_INFO, senderIP, m_currentGame->getName()); + // acknowledge a request for product information by a fellow player in the game + sendProductInfoMessage(LANMessage::MSG_MATCH_RESPONSE_PRODUCT_INFO, senderIP); - // treat request for patch information as acknowledgement - handleMatchAcknowledgePatchInfo(msg, senderIP); + // treat request for product information as acknowledgement + handleMatchProductInfoResponse(msg, senderIP); } -void LANAPI::handleMatchAcknowledgePatchInfo(LANMessage *msg, UnsignedInt senderIP) +void LANAPI::handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderIP) { if (!m_currentGame) return; @@ -127,8 +128,8 @@ void LANAPI::handleMatchAcknowledgePatchInfo(LANMessage *msg, UnsignedInt sender if (!m_currentGame->getLANSlot(i)->isHuman() || m_currentGame->getIP(i) != senderIP) continue; - // a fellow player in the game has acknowledged our request for patch information - m_currentGame->getSlot(i)->setPatchVersion(msg->PatchInfo.patchVersion); + // a fellow player in the game has acknowledged our request for product information + m_currentGame->getSlot(i)->setProductVersion(msg->ProductInfo.productVersion); // update player list with colored names lanUpdateSlotList(); @@ -237,8 +238,8 @@ void LANAPI::handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP ) game->setName(UnicodeString(msg->GameInfo.gameName)); addGame(game); - // TheSuperHackers @feature Caball009 06/11/2025 Request a game host to send patch information. - handlePatchInfo(LANMessage::MSG_GAME_REQUEST_PATCH_INFO, senderIP, game->getName()); + // TheSuperHackers @feature Caball009 06/11/2025 Request a game host to send product information. + sendProductInfoMessage(LANMessage::MSG_GAME_REQUEST_PRODUCT_INFO, senderIP); } Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options)); game->setGameInProgress(msg->GameInfo.inProgress); @@ -266,8 +267,8 @@ void LANAPI::handleLobbyAnnounce( LANMessage *msg, UnsignedInt senderIP ) player = NEW LANPlayer; player->setIP(senderIP); - // TheSuperHackers @feature Caball009 06/11/2025 Request a player in the lobby to send patch information. - handlePatchInfo(LANMessage::MSG_LOBBY_REQUEST_PATCH_INFO, senderIP, UnicodeString()); + // TheSuperHackers @feature Caball009 06/11/2025 Request a player in the lobby to send product information. + sendProductInfoMessage(LANMessage::MSG_LOBBY_REQUEST_PRODUCT_INFO, senderIP); } else { @@ -505,8 +506,8 @@ void LANAPI::handleJoinAccept( LANMessage *msg, UnsignedInt senderIP ) //DEBUG_ASSERTCRASH(false, ("setting host to %ls@%ls", m_currentGame->getLANSlot(0)->getUser()->getLogin().str(), // m_currentGame->getLANSlot(0)->getUser()->getHost().str())); - // TheSuperHackers @feature Caball009 06/11/2025 Request players in the match to send patch information. - handlePatchInfo(LANMessage::MSG_MATCH_REQUEST_PATCH_INFO, 0, m_currentGame->getName()); + // TheSuperHackers @feature Caball009 06/11/2025 Request players in the match to send product information. + sendProductInfoMessage(LANMessage::MSG_MATCH_REQUEST_PRODUCT_INFO, 0); } m_pendingAction = ACT_NONE; m_expiration = 0; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp index a7f2ed69f35..3fa96829ca3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp @@ -228,7 +228,7 @@ void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList ) } // TheSuperHackers @feature Caball009 06/11/2025 Set special color for games that are using a patched client version. - const Color color = (gameList->getSlot(0)->getPatchVersion() > 0) + const Color color = (gameList->getSlot(0)->getProductVersion() > 0) ? ((gameList->isGameInProgress()) ? gameInProgressColorPatchVersion : gameColorPatchVersion) : ((gameList->isGameInProgress()) ? gameInProgressColor : gameColor); const Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, color, -1, -1); From fedb4a61f030a6b7293bd81d1e47afff77d1fcc6 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 9 Nov 2025 19:36:04 +0100 Subject: [PATCH 04/17] Renamed exe and ini variables. --- GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h | 4 ++-- .../Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h index 28d3045289c..e87046389d6 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h @@ -425,8 +425,8 @@ struct LANMessage struct { - UnsignedInt exeHash; - UnsignedInt iniHash; + UnsignedInt exeCRC; + UnsignedInt iniCRC; UnsignedInt productVersion; Char gitTagOrHash[33]; WideChar productName[129]; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 79b524da675..2c0dbf5b80d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -47,8 +47,8 @@ void LANAPI::sendProductInfoMessage(Int messageType, UnsignedInt senderIP) fillInLANMessage(&msg); msg.messageType = (LANMessage::Type)messageType; - msg.ProductInfo.exeHash = TheGlobalData->m_exeCRC; - msg.ProductInfo.iniHash = TheGlobalData->m_iniCRC; + msg.ProductInfo.exeCRC = TheGlobalData->m_exeCRC; + msg.ProductInfo.iniCRC = TheGlobalData->m_iniCRC; msg.ProductInfo.productVersion = TheVersion->getVersionNumber(); strlcpy(msg.ProductInfo.gitTagOrHash, TheVersion->getAsciiGitTagOrHash().str(), ARRAY_SIZE(msg.ProductInfo.gitTagOrHash)); wcslcpy(msg.ProductInfo.productName, TheVersion->getUnicodeProductString().str(), ARRAY_SIZE(msg.ProductInfo.productName)); From 87193b63b2872fb9463b18f3ae4b66caaf8f092a Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:07:10 +0100 Subject: [PATCH 05/17] Added product info setter functions. --- .../GameEngine/Include/GameNetwork/LANAPI.h | 3 +++ .../Source/GameNetwork/LANAPIhandlers.cpp | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h index e87046389d6..f2d3a296530 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h @@ -260,6 +260,9 @@ class LANAPI : public LANAPIInterface void addGame(LANGameInfo *game); AsciiString createSlotString( void ); + void setProductInfoFromLocalData(GameSlot *slot); + void setProductInfoFromMessage(LANMessage *msg, GameSlot *slot); + // Functions to handle incoming messages ----------------------------------- void handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ); void handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 2c0dbf5b80d..4dd08a28923 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -41,6 +41,30 @@ #include "GameNetwork/LANAPICallbacks.h" #include "GameClient/MapUtil.h" +void LANAPI::setProductInfoFromLocalData(GameSlot *slot) +{ + GameSlot::ProductInfo productInfo; + productInfo.exeCRC = TheGlobalData->m_exeCRC; + productInfo.iniCRC = TheGlobalData->m_iniCRC; + productInfo.productVersion = TheVersion->getVersionNumber(); + productInfo.gitTagOrHash = TheVersion->getAsciiGitTagOrHash(); + productInfo.productName = TheVersion->getUnicodeProductString(); + + slot->setProductInfo(productInfo); +} + +void LANAPI::setProductInfoFromMessage(LANMessage *msg, GameSlot *slot) +{ + GameSlot::ProductInfo productInfo; + productInfo.exeCRC = msg->ProductInfo.exeCRC; + productInfo.iniCRC = msg->ProductInfo.iniCRC; + productInfo.productVersion = msg->ProductInfo.productVersion; + productInfo.gitTagOrHash = msg->ProductInfo.gitTagOrHash; + productInfo.productName = msg->ProductInfo.productName; + + slot->setProductInfo(productInfo); +} + void LANAPI::sendProductInfoMessage(Int messageType, UnsignedInt senderIP) { LANMessage msg; From eb84ebfc2ac7b298eb8c48377db4f62d145176b2 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:10:50 +0100 Subject: [PATCH 06/17] Product info is now updated via the util functions. --- GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp | 3 +++ .../Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp index f7f56d1e662..44e9dfbf886 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -926,6 +926,9 @@ void LANAPI::RequestGameCreate( UnicodeString gameName, Bool isDirectConnect ) newSlot.setLogin(m_userName); newSlot.setHost(m_hostName); + // set product information for local game slot + setProductInfoFromLocalData(&newSlot); + myGame->setSlot(0,newSlot); myGame->setNext(NULL); LANPreferences pref; diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 4dd08a28923..a13d90d5ae3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -99,7 +99,7 @@ void LANAPI::handleGameProductInfoResponse(LANMessage *msg, UnsignedInt senderIP return; // a game host has acknowledged our request for product information - game->getSlot(0)->setProductVersion(msg->ProductInfo.productVersion); + setProductInfoFromMessage(msg, game->getSlot(0)); // update game list with colored names OnGameList(m_games); @@ -153,7 +153,7 @@ void LANAPI::handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderI continue; // a fellow player in the game has acknowledged our request for product information - m_currentGame->getSlot(i)->setProductVersion(msg->ProductInfo.productVersion); + setProductInfoFromMessage(msg, m_currentGame->getSlot(i)); // update player list with colored names lanUpdateSlotList(); @@ -515,6 +515,10 @@ void LANAPI::handleJoinAccept( LANMessage *msg, UnsignedInt senderIP ) slot.setLastHeard(0); slot.setLogin(m_userName); slot.setHost(m_hostName); + + // set product information for local game slot + setProductInfoFromLocalData(&slot); + m_currentGame->setSlot(pos, slot); m_currentGame->getLANSlot(0)->setHost(msg->hostName); From f4177d458140c2c6d2feee469bdaab74efc769e0 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:11:29 +0100 Subject: [PATCH 07/17] Show git tag/hash for each player. --- .../Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index e8143d092fb..88ae56d030f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -574,8 +574,13 @@ void PopulateInGameDiplomacyPopup( void ) } // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using a patched client version. - if (slot->isHuman() && (TheGameInfo->getLocalSlotNum() == slotNum || slot->getProductVersion() > 0)) - text.format(L"%s [%s]", text.str(), TheVersion->getUnicodeGitTagOrHash().str()); + if (slot->isHuman() && slot->getProductInfo().productVersion > 0) + { + UnicodeString gitTagOrHash; + gitTagOrHash.translate(slot->getProductInfo().gitTagOrHash); + + text.format(L"%s [%s]", text.str(), gitTagOrHash.str()); + } staticTextStatus[rowNum]->winSetEnabledTextColors(frontColor, backColor); GadgetStaticTextSetText(staticTextStatus[rowNum], text); From 8caeda6867885ca80bf8c2e11c84ec5c574ebaa0 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:12:11 +0100 Subject: [PATCH 08/17] Added product info struct to game slot class. --- .../GameEngine/Include/GameNetwork/GameInfo.h | 15 ++++++++++++--- .../GameEngine/Source/GameNetwork/GUIUtil.cpp | 2 +- .../GameEngine/Source/GameNetwork/GameInfo.cpp | 6 +++--- .../Source/GameNetwork/LANAPIhandlers.cpp | 2 +- .../GameEngine/Source/GameNetwork/LANGameInfo.cpp | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h index c64f94da08c..e7ccfdd210a 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h +++ b/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h @@ -57,6 +57,15 @@ enum class GameSlot { public: + struct ProductInfo + { + UnsignedInt exeCRC; + UnsignedInt iniCRC; + UnsignedInt productVersion; + AsciiString gitTagOrHash; + UnicodeString productName; + }; + GameSlot(); virtual void reset(); @@ -126,8 +135,8 @@ class GameSlot void mute( Bool isMuted ) { m_isMuted = isMuted; } Bool isMuted( void ) const { return m_isMuted; } - void setProductVersion(UnsignedInt productVersion) { m_productVersion = productVersion; } - UnsignedInt getProductVersion() const { return m_productVersion; } + void setProductInfo(const ProductInfo& productInfo) { m_productInfo = productInfo; } + const ProductInfo& getProductInfo() const { return m_productInfo; } protected: SlotState m_state; Bool m_isAccepted; @@ -146,7 +155,7 @@ class GameSlot FirewallHelperClass::FirewallBehaviorType m_NATBehavior; ///< The NAT behavior for this slot's player. UnsignedInt m_lastFrameInGame; // only valid for human players Bool m_disconnected; // only valid for human players - UnsignedInt m_productVersion; ///< Community made product version + ProductInfo m_productInfo; ///< Community made product information }; /** diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp index 05d6a589e3e..80970952479 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp @@ -423,7 +423,7 @@ void UpdateSlotList( GameInfo *myGame, GameWindow *comboPlayer[], } if(slot->isHuman()) { - if (i == myGame->getLocalSlotNum() || myGame->getSlot(i)->getProductVersion() > 0) + if (myGame->getSlot(i)->getProductInfo().productVersion > 0) { // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. GadgetComboBoxSetEnabledTextColors(comboPlayer[i], playerColorPatchVersion, GameMakeColor(0, 0, 0, 255)); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp index 737b5652906..9607a21e172 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -73,7 +73,7 @@ void GameSlot::reset() m_origPlayerTemplate = -1; m_origStartPos = -1; m_origColor = -1; - m_productVersion = 0; + m_productInfo = ProductInfo(); } void GameSlot::saveOffOriginalInfo( void ) @@ -1486,9 +1486,9 @@ Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) for(Int i = 0; igetConstSlot(i)->getState() == SLOT_PLAYER && newSlot[i].getState() == SLOT_PLAYER) - newSlot[i].setProductVersion(game->getConstSlot(i)->getProductVersion()); + newSlot[i].setProductInfo(game->getConstSlot(i)->getProductInfo()); game->setSlot(i,newSlot[i]); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index a13d90d5ae3..2cd51c6be39 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -149,7 +149,7 @@ void LANAPI::handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderI for (Int i = 0; i < MAX_SLOTS; ++i) { - if (!m_currentGame->getLANSlot(i)->isHuman() || m_currentGame->getIP(i) != senderIP) + if (!m_currentGame->getConstSlot(i)->isHuman() || m_currentGame->getIP(i) != senderIP) continue; // a fellow player in the game has acknowledged our request for product information diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp index 3fa96829ca3..584618c4b3c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp @@ -228,7 +228,7 @@ void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList ) } // TheSuperHackers @feature Caball009 06/11/2025 Set special color for games that are using a patched client version. - const Color color = (gameList->getSlot(0)->getProductVersion() > 0) + const Color color = (gameList->getSlot(0)->getProductInfo().productVersion > 0) ? ((gameList->isGameInProgress()) ? gameInProgressColorPatchVersion : gameColorPatchVersion) : ((gameList->isGameInProgress()) ? gameInProgressColor : gameColor); const Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, color, -1, -1); From abf9f4e0f721a66dc06eb7e4550bb863cd3eef31 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:37:57 +0100 Subject: [PATCH 09/17] Unify network files. --- Core/GameEngine/CMakeLists.txt | 18 +- .../GameEngine/Include/GameNetwork/GameInfo.h | 0 .../GameEngine/Include/GameNetwork/LANAPI.h | 0 .../Include/GameNetwork/LANAPICallbacks.h | 0 .../Include/GameNetwork/LANPlayer.h | 0 .../Source/GameNetwork/GameInfo.cpp | 0 .../GameEngine/Source/GameNetwork/LANAPI.cpp | 0 .../Source/GameNetwork/LANAPICallbacks.cpp | 0 .../Source/GameNetwork/LANAPIhandlers.cpp | 0 .../Source/GameNetwork/LANGameInfo.cpp | 0 Generals/Code/GameEngine/CMakeLists.txt | 18 +- .../GameEngine/Include/GameNetwork/GameInfo.h | 305 --- .../GameEngine/Include/GameNetwork/LANAPI.h | 412 ----- .../Include/GameNetwork/LANAPICallbacks.h | 83 - .../Include/GameNetwork/LANPlayer.h | 63 - .../Source/GameNetwork/GameInfo.cpp | 1636 ----------------- .../GameEngine/Source/GameNetwork/LANAPI.cpp | 1283 ------------- .../Source/GameNetwork/LANAPICallbacks.cpp | 737 -------- .../Source/GameNetwork/LANAPIhandlers.cpp | 661 ------- .../Source/GameNetwork/LANGameInfo.cpp | 320 ---- GeneralsMD/Code/GameEngine/CMakeLists.txt | 18 +- 21 files changed, 27 insertions(+), 5527 deletions(-) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameNetwork/GameInfo.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameNetwork/LANAPI.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameNetwork/LANAPICallbacks.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Include/GameNetwork/LANPlayer.h (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameNetwork/GameInfo.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameNetwork/LANAPI.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp (100%) rename {GeneralsMD/Code => Core}/GameEngine/Source/GameNetwork/LANGameInfo.cpp (100%) delete mode 100644 Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h delete mode 100644 Generals/Code/GameEngine/Include/GameNetwork/LANAPI.h delete mode 100644 Generals/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h delete mode 100644 Generals/Code/GameEngine/Include/GameNetwork/LANPlayer.h delete mode 100644 Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp delete mode 100644 Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp diff --git a/Core/GameEngine/CMakeLists.txt b/Core/GameEngine/CMakeLists.txt index 0a58dfc4315..15f08a6313f 100644 --- a/Core/GameEngine/CMakeLists.txt +++ b/Core/GameEngine/CMakeLists.txt @@ -510,7 +510,7 @@ set(GAMEENGINE_SRC # Include/GameNetwork/FrameData.h # Include/GameNetwork/FrameDataManager.h # Include/GameNetwork/FrameMetrics.h -# Include/GameNetwork/GameInfo.h + Include/GameNetwork/GameInfo.h # Include/GameNetwork/GameMessageParser.h # Include/GameNetwork/GameSpy/BuddyDefs.h # Include/GameNetwork/GameSpy/BuddyThread.h @@ -534,10 +534,10 @@ set(GAMEENGINE_SRC # Include/GameNetwork/GameSpyThread.h # Include/GameNetwork/GUIUtil.h # Include/GameNetwork/IPEnumeration.h -# Include/GameNetwork/LANAPI.h -# Include/GameNetwork/LANAPICallbacks.h + Include/GameNetwork/LANAPI.h + Include/GameNetwork/LANAPICallbacks.h # Include/GameNetwork/LANGameInfo.h -# Include/GameNetwork/LANPlayer.h + Include/GameNetwork/LANPlayer.h # Include/GameNetwork/NAT.h # Include/GameNetwork/NetCommandList.h # Include/GameNetwork/NetCommandMsg.h @@ -1100,7 +1100,7 @@ set(GAMEENGINE_SRC # Source/GameNetwork/FrameData.cpp # Source/GameNetwork/FrameDataManager.cpp # Source/GameNetwork/FrameMetrics.cpp -# Source/GameNetwork/GameInfo.cpp + Source/GameNetwork/GameInfo.cpp # Source/GameNetwork/GameMessageParser.cpp #Source/GameNetwork/GameSpyChat.cpp # unused #Source/GameNetwork/GameSpyGameInfo.cpp # unused @@ -1121,10 +1121,10 @@ set(GAMEENGINE_SRC # Source/GameNetwork/GameSpyOverlay.cpp # Source/GameNetwork/GUIUtil.cpp # Source/GameNetwork/IPEnumeration.cpp -# Source/GameNetwork/LANAPI.cpp -# Source/GameNetwork/LANAPICallbacks.cpp -# Source/GameNetwork/LANAPIhandlers.cpp -# Source/GameNetwork/LANGameInfo.cpp + Source/GameNetwork/LANAPI.cpp + Source/GameNetwork/LANAPICallbacks.cpp + Source/GameNetwork/LANAPIhandlers.cpp + Source/GameNetwork/LANGameInfo.cpp # Source/GameNetwork/NAT.cpp # Source/GameNetwork/NetCommandList.cpp # Source/GameNetwork/NetCommandMsg.cpp diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h b/Core/GameEngine/Include/GameNetwork/GameInfo.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameNetwork/GameInfo.h rename to Core/GameEngine/Include/GameNetwork/GameInfo.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPI.h rename to Core/GameEngine/Include/GameNetwork/LANAPI.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h rename to Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h diff --git a/GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h b/Core/GameEngine/Include/GameNetwork/LANPlayer.h similarity index 100% rename from GeneralsMD/Code/GameEngine/Include/GameNetwork/LANPlayer.h rename to Core/GameEngine/Include/GameNetwork/LANPlayer.h diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/Core/GameEngine/Source/GameNetwork/GameInfo.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameNetwork/GameInfo.cpp rename to Core/GameEngine/Source/GameNetwork/GameInfo.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/Core/GameEngine/Source/GameNetwork/LANAPI.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPI.cpp rename to Core/GameEngine/Source/GameNetwork/LANAPI.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp rename to Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp rename to Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp similarity index 100% rename from GeneralsMD/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp rename to Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp diff --git a/Generals/Code/GameEngine/CMakeLists.txt b/Generals/Code/GameEngine/CMakeLists.txt index 64236aa60d3..fe0117d8758 100644 --- a/Generals/Code/GameEngine/CMakeLists.txt +++ b/Generals/Code/GameEngine/CMakeLists.txt @@ -466,7 +466,7 @@ set(GAMEENGINE_SRC Include/GameNetwork/FrameData.h Include/GameNetwork/FrameDataManager.h Include/GameNetwork/FrameMetrics.h - Include/GameNetwork/GameInfo.h +# Include/GameNetwork/GameInfo.h Include/GameNetwork/GameMessageParser.h Include/GameNetwork/GameSpy.h Include/GameNetwork/GameSpy/BuddyDefs.h @@ -492,10 +492,10 @@ set(GAMEENGINE_SRC Include/GameNetwork/GameSpyThread.h Include/GameNetwork/GUIUtil.h Include/GameNetwork/IPEnumeration.h - Include/GameNetwork/LANAPI.h - Include/GameNetwork/LANAPICallbacks.h +# Include/GameNetwork/LANAPI.h +# Include/GameNetwork/LANAPICallbacks.h Include/GameNetwork/LANGameInfo.h - Include/GameNetwork/LANPlayer.h +# Include/GameNetwork/LANPlayer.h Include/GameNetwork/NAT.h Include/GameNetwork/NetCommandList.h Include/GameNetwork/NetCommandMsg.h @@ -1014,7 +1014,7 @@ set(GAMEENGINE_SRC Source/GameNetwork/FrameData.cpp Source/GameNetwork/FrameDataManager.cpp Source/GameNetwork/FrameMetrics.cpp - Source/GameNetwork/GameInfo.cpp +# Source/GameNetwork/GameInfo.cpp Source/GameNetwork/GameMessageParser.cpp #Source/GameNetwork/GameSpy.cpp #Source/GameNetwork/GameSpyChat.cpp @@ -1037,10 +1037,10 @@ set(GAMEENGINE_SRC Source/GameNetwork/GameSpyOverlay.cpp Source/GameNetwork/GUIUtil.cpp Source/GameNetwork/IPEnumeration.cpp - Source/GameNetwork/LANAPI.cpp - Source/GameNetwork/LANAPICallbacks.cpp - Source/GameNetwork/LANAPIhandlers.cpp - Source/GameNetwork/LANGameInfo.cpp +# Source/GameNetwork/LANAPI.cpp +# Source/GameNetwork/LANAPICallbacks.cpp +# Source/GameNetwork/LANAPIhandlers.cpp +# Source/GameNetwork/LANGameInfo.cpp Source/GameNetwork/NAT.cpp Source/GameNetwork/NetCommandList.cpp Source/GameNetwork/NetCommandMsg.cpp diff --git a/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h b/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h deleted file mode 100644 index 21aab5bc521..00000000000 --- a/Generals/Code/GameEngine/Include/GameNetwork/GameInfo.h +++ /dev/null @@ -1,305 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// FILE: GameInfo.h ////////////////////////////////////////////////////// -// Generals game setup information -// Author: Matthew D. Campbell, December 2001 - -#pragma once - -#include "Common/Snapshot.h" -#include "Common/Money.h" -#include "GameNetwork/NetworkDefs.h" -#include "GameNetwork/FirewallHelper.h" - -enum SlotState CPP_11(: Int) -{ - SLOT_OPEN, - SLOT_CLOSED, - SLOT_EASY_AI, - SLOT_MED_AI, - SLOT_BRUTAL_AI, - SLOT_PLAYER -}; - -enum -{ - PLAYERTEMPLATE_RANDOM = -1, - PLAYERTEMPLATE_OBSERVER = -2, - PLAYERTEMPLATE_MIN = PLAYERTEMPLATE_OBSERVER -}; - -/** - * GameSlot class - maintains information about the contents of a - * game slot. This persists throughout the game. - */ -class GameSlot -{ -public: - GameSlot(); - virtual void reset(); - - void setAccept( void ) { m_isAccepted = true; } ///< Accept the current options - void unAccept( void ); ///< Unaccept (options changed, etc) - Bool isAccepted( void ) const { return m_isAccepted; } ///< Non-human slots are always accepted - - void setMapAvailability( Bool hasMap ); ///< Set whether the slot has the map - Bool hasMap( void ) const { return m_hasMap; } ///< Non-human slots always have the map - - void setState( SlotState state, - UnicodeString name = UnicodeString::TheEmptyString, - UnsignedInt IP = 0); ///< Set the slot's state (human, AI, open, etc) - SlotState getState( void ) const { return m_state; } ///< Get the slot state - - void setColor( Int color ) { m_color = color; } - Int getColor( void ) const { return m_color; } - - void setStartPos( Int startPos ) { m_startPos = startPos; } - Int getStartPos( void ) const { return m_startPos; } - - void setPlayerTemplate( Int playerTemplate ) - { m_playerTemplate = playerTemplate; - if (playerTemplate <= PLAYERTEMPLATE_MIN) - m_startPos = -1; - } - Int getPlayerTemplate( void ) const { return m_playerTemplate; } - - void setTeamNumber( Int teamNumber ) { m_teamNumber = teamNumber; } - Int getTeamNumber( void ) const { return m_teamNumber; } - - inline void setName( UnicodeString name ) { m_name = name; } - inline UnicodeString getName( void ) const { return m_name; } - - inline void setIP( UnsignedInt IP ) { m_IP = IP; } - inline UnsignedInt getIP( void ) const { return m_IP; } - - inline void setPort( UnsignedShort port ) { m_port = port; } - inline UnsignedShort getPort( void ) const { return m_port; } - - inline void setNATBehavior( FirewallHelperClass::FirewallBehaviorType NATBehavior) { m_NATBehavior = NATBehavior; } - inline FirewallHelperClass::FirewallBehaviorType getNATBehavior() const { return m_NATBehavior; } - - void saveOffOriginalInfo( void ); - inline Int getOriginalPlayerTemplate( void ) const { return m_origPlayerTemplate; } - inline Int getOriginalColor( void ) const { return m_origColor; } - inline Int getOriginalStartPos( void ) const { return m_origStartPos; } - Int getApparentPlayerTemplate( void ) const; - Int getApparentColor( void ) const; - Int getApparentStartPos( void ) const; - UnicodeString getApparentPlayerTemplateDisplayName( void ) const; - - // Various tests - Bool isHuman( void ) const; ///< Is this slot occupied by a human player? - Bool isOccupied( void ) const; ///< Is this slot occupied (by a human or an AI)? - Bool isAI( void ) const; ///< Is this slot occupied by an AI? - Bool isPlayer( AsciiString userName ) const; ///< Does this slot contain the given user? - Bool isPlayer( UnicodeString userName ) const; ///< Does this slot contain the given user? - Bool isPlayer( UnsignedInt ip ) const; ///< Is this slot at this IP? - Bool isOpen( void ) const; - - void setLastFrameInGame( UnsignedInt frame ) { m_lastFrameInGame = frame; } - void markAsDisconnected( void ) { m_disconnected = TRUE; } - UnsignedInt lastFrameInGame( void ) const { return m_lastFrameInGame; } - Bool disconnected( void ) const { return isHuman() && m_disconnected; } - - void mute( Bool isMuted ) { m_isMuted = isMuted; } - Bool isMuted( void ) const { return m_isMuted; } -protected: - SlotState m_state; - Bool m_isAccepted; - Bool m_hasMap; - Bool m_isMuted; - Int m_color; ///< color, or -1 for random - Int m_startPos; ///< start position, or -1 for random - Int m_playerTemplate; ///< PlayerTemplate - Int m_teamNumber; ///< alliance, -1 for none - Int m_origColor; ///< color, or -1 for random - Int m_origStartPos; ///< start position, or -1 for random - Int m_origPlayerTemplate; ///< PlayerTemplate - UnicodeString m_name; ///< Only valid for human players - UnsignedInt m_IP; ///< Only valid for human players in LAN/WOL - UnsignedShort m_port; ///< Only valid for human players in LAN/WOL - FirewallHelperClass::FirewallBehaviorType m_NATBehavior; ///< The NAT behavior for this slot's player. - UnsignedInt m_lastFrameInGame; // only valid for human players - Bool m_disconnected; // only valid for human players -}; - -/** - * GameInfo class - maintains information about the game setup and - * the contents of its slot list hroughout the game. - */ -class GameInfo -{ -public: - GameInfo(); - - void init( void ); - virtual void reset( void ); - - void clearSlotList( void ); - - Int getNumPlayers( void ) const; ///< How many players (human and AI) are in the game? - Int getNumNonObserverPlayers( void ) const; ///< How many non-observer players (human and AI) are in the game? - Int getMaxPlayers( void ) const; ///< How many players (human and AI) can be in the game? - - void enterGame( void ); ///< Mark us as having entered the game - void leaveGame( void ); ///< Mark us as having left the game - virtual void startGame( Int gameID ); ///< Mark our game as started, and record the game ID - void endGame( void ); ///< Mark us as out of game - inline Int getGameID( void ) const; ///< Get the game ID of the current game or the last one if we're not in game - - inline void setInGame( void ); ///< set the m_inGame flag - inline Bool isInGame( void ) const; ///< Are we (in game or in game setup)? As opposed to chatting, matching, etc - inline Bool isGameInProgress( void ) const; ///< Is the game in progress? - inline void setGameInProgress( Bool inProgress ); ///< Set whether the game is in progress or not. - void setSlot( Int slotNum, GameSlot slotInfo ); ///< Set the slot state (human, open, AI, etc) - GameSlot* getSlot( Int slotNum ); ///< Get the slot - const GameSlot* getConstSlot( Int slotNum ) const; ///< Get the slot - virtual Bool amIHost( void ) const; ///< Convenience function - is the local player the game host? - virtual Int getLocalSlotNum( void ) const; ///< Get the local slot number, or -1 if we're not present - Int getSlotNum( AsciiString userName ) const; ///< Get the slot number corresponding to a specific user, or -1 if he's not present - - // Game options - void setMap( AsciiString mapName ); ///< Set the map to play on - void setMapCRC( UnsignedInt mapCRC ); ///< Set the map CRC - void setMapSize( UnsignedInt mapSize ); ///< Set the map size - void setMapContentsMask( Int mask ); ///< Set the map contents mask (1=map,2=preview,4=map.ini) - inline AsciiString getMap( void ) const; ///< Get the game map - inline UnsignedInt getMapCRC( void ) const; ///< Get the map CRC - inline UnsignedInt getMapSize( void ) const; ///< Get the map size - inline Int getMapContentsMask( void ) const; ///< Get the map contents mask - void setSeed( Int seed ); ///< Set the random seed for the game - inline Int getSeed( void ) const; ///< Get the game seed - inline Int getUseStats( void ) const; ///< Does this game count towards gamespy stats? - inline void setUseStats( Int useStats ); - - inline UnsignedShort getSuperweaponRestriction( void ) const; ///< Get any optional limits on superweapons - void setSuperweaponRestriction( UnsignedShort restriction ); ///< Set the optional limits on superweapons - inline const Money & getStartingCash(void) const; - void setStartingCash( const Money & startingCash ); - - void setSlotPointer( Int index, GameSlot *slot ); ///< Set the slot info pointer - - void setLocalIP( UnsignedInt ip ) { m_localIP =ip; } ///< Set the local IP - UnsignedInt getLocalIP( void ) const { return m_localIP; } ///< Get the local IP - - Bool isColorTaken(Int colorIdx, Int slotToIgnore = -1 ) const; - Bool isStartPositionTaken(Int positionIdx, Int slotToIgnore = -1 ) const; - - virtual void resetAccepted(void); ///< Reset the accepted flag on all players - virtual void resetStartSpots(void); ///< reset the start spots for the new map. - virtual void adjustSlotsForMap(void); ///< adjusts the slots to open and closed depending on the players in the game and the number of players the map can hold. - - virtual void closeOpenSlots(void); ///< close all slots that are currently unoccupied. - - // CRC checking hack - void setCRCInterval( Int val ) { m_crcInterval = (val<100)?val:100; } - inline Int getCRCInterval( void ) const { return m_crcInterval; } - - Bool haveWeSurrendered(void) { return m_surrendered; } - void markAsSurrendered(void) { m_surrendered = TRUE; } - - Bool isSkirmish(void); // TRUE if 1 human & 1+ AI are present && !isSandbox() - Bool isMultiPlayer(void); // TRUE if 2+ human are present - Bool isSandbox(void); // TRUE if everybody is on the same team - - Bool isPlayerPreorder(Int index); - void markPlayerAsPreorder(Int index); - - inline Bool oldFactionsOnly(void) const; - inline void setOldFactionsOnly( Bool oldFactionsOnly ); - -protected: - Int m_preorderMask; - Int m_crcInterval; - Bool m_inGame; - Bool m_inProgress; - Bool m_surrendered; - Int m_gameID; - GameSlot *m_slot[MAX_SLOTS]; - - UnsignedInt m_localIP; - - // Game options - AsciiString m_mapName; - UnsignedInt m_mapCRC; - UnsignedInt m_mapSize; - Int m_mapMask; - Int m_seed; - Int m_useStats; - Money m_startingCash; - UnsignedShort m_superweaponRestriction; - Bool m_oldFactionsOnly; // Only USA, China, GLA -- not USA Air Force General, GLA Toxic General, et al -}; - -extern GameInfo *TheGameInfo; - -// Inline functions -Int GameInfo::getGameID( void ) const { return m_gameID; } -AsciiString GameInfo::getMap( void ) const { return m_mapName; } -UnsignedInt GameInfo::getMapCRC( void ) const { return m_mapCRC; } -UnsignedInt GameInfo::getMapSize( void ) const { return m_mapSize; } -Int GameInfo::getMapContentsMask( void ) const { return m_mapMask; } -Int GameInfo::getSeed( void ) const { return m_seed; } -Bool GameInfo::isInGame( void ) const { return m_inGame; } -void GameInfo::setInGame( void ) { m_inGame = true; } -Bool GameInfo::isGameInProgress( void ) const { return m_inProgress; } -void GameInfo::setGameInProgress( Bool inProgress ) { m_inProgress = inProgress; } -Int GameInfo::getUseStats( void ) const { return m_useStats; } -void GameInfo::setUseStats( Int useStats ) { m_useStats = useStats; } -const Money&GameInfo::getStartingCash( void ) const { return m_startingCash; } -UnsignedShort GameInfo::getSuperweaponRestriction( void ) const { return m_superweaponRestriction; } -Bool GameInfo::oldFactionsOnly(void) const { return m_oldFactionsOnly; } -void GameInfo::setOldFactionsOnly( Bool oldFactionsOnly ) { m_oldFactionsOnly = oldFactionsOnly; } - -AsciiString GameInfoToAsciiString( const GameInfo *game ); -Bool ParseAsciiStringToGameInfo( GameInfo *game, AsciiString options ); - - -/** - * The SkirmishGameInfo class holds information about the skirmish game and - * the contents of its slot list. - */ - -class SkirmishGameInfo : public GameInfo, public Snapshot -{ -private: - GameSlot m_skirmishSlot[MAX_SLOTS]; - -protected: - // snapshot methods - virtual void crc( Xfer *xfer ); - virtual void xfer( Xfer *xfer ); - virtual void loadPostProcess( void ); - -public: - SkirmishGameInfo() - { - for (Int i = 0; i< MAX_SLOTS; ++i) - setSlotPointer(i, &m_skirmishSlot[i]); - } -}; - -extern SkirmishGameInfo *TheSkirmishGameInfo; -extern SkirmishGameInfo *TheChallengeGameInfo; diff --git a/Generals/Code/GameEngine/Include/GameNetwork/LANAPI.h b/Generals/Code/GameEngine/Include/GameNetwork/LANAPI.h deleted file mode 100644 index 48d04a2991a..00000000000 --- a/Generals/Code/GameEngine/Include/GameNetwork/LANAPI.h +++ /dev/null @@ -1,412 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// LANAPI.h /////////////////////////////////////////////////////////////// -// LANAPI singleton class - defines interface to LAN broadcast communications -// Author: Matthew D. Campbell, October 2001 - -#pragma once - -#include "GameNetwork/Transport.h" -#include "GameNetwork/NetworkInterface.h" -#include "GameNetwork/NetworkDefs.h" -#include "GameNetwork/LANPlayer.h" -#include "GameNetwork/LANGameInfo.h" - -//static const Int g_lanPlayerNameLength = 20; -static const Int g_lanPlayerNameLength = 12; // reduced length because of game option length -//static const Int g_lanLoginNameLength = 16; -//static const Int g_lanHostNameLength = 16; -static const Int g_lanLoginNameLength = 1; -static const Int g_lanHostNameLength = 1; -//static const Int g_lanGameNameLength = 32; -static const Int g_lanGameNameLength = 16; // reduced length because of game option length -static const Int g_lanGameNameReservedLength = 16; // save N wchars for ID info -static const Int g_lanMaxChatLength = 100; -static const Int m_lanMaxOptionsLength = MAX_PACKET_SIZE - ( 8 + (g_lanGameNameLength+1)*2 + 4 + (g_lanPlayerNameLength+1)*2 - + (g_lanLoginNameLength+1) + (g_lanHostNameLength+1) ); -static const Int g_maxSerialLength = 23; // including the trailing '\0' - -struct LANMessage; - -/** - * The LANAPI class is used to instantiate a singleton which - * implements the interface to all LAN broadcast communications. - */ -class LANAPIInterface : public SubsystemInterface -{ -public: - - virtual ~LANAPIInterface() { }; - - virtual void init( void ) = 0; ///< Initialize or re-initialize the instance - virtual void reset( void ) = 0; ///< reset the logic system - virtual void update( void ) = 0; ///< update the world - - virtual void setIsActive(Bool isActive ) = 0; ///< Tell TheLAN whether or not the app is active. - - // Possible types of chat messages - enum ChatType - { - LANCHAT_NORMAL = 0, - LANCHAT_EMOTE, - LANCHAT_SYSTEM, - }; - - // Request functions generate network traffic - virtual void RequestLocations( void ) = 0; ///< Request everybody to respond with where they are - virtual void RequestGameJoin( LANGameInfo *game, UnsignedInt ip = 0 ) = 0; ///< Request to join a game - virtual void RequestGameJoinDirectConnect( UnsignedInt ipaddress ) = 0; ///< Request to join a game at an IP address - virtual void RequestGameLeave( void ) = 0; ///< Tell everyone we're leaving - virtual void RequestAccept( void ) = 0; ///< Indicate we're OK with the game options - virtual void RequestHasMap( void ) = 0; ///< Send our map status - virtual void RequestChat( UnicodeString message, ChatType format ) = 0; ///< Send a chat message - virtual void RequestGameStart( void ) = 0; ///< Tell everyone the game is starting - virtual void RequestGameStartTimer( Int seconds ) = 0; - virtual void RequestGameOptions( AsciiString gameOptions, Bool isPublic, UnsignedInt ip = 0 ) = 0; ///< Change the game options - virtual void RequestGameCreate( UnicodeString gameName, Bool isDirectConnect ) = 0; ///< Try to host a game - virtual void RequestGameAnnounce( void ) = 0; ///< Sound out current game info if host -// virtual void RequestSlotList( void ) = 0; ///< Pump out the Slot info. - virtual void RequestSetName( UnicodeString newName ) = 0; ///< Pick a new name - virtual void RequestLobbyLeave( Bool forced ) = 0; ///< Announce that we're leaving the lobby - virtual void ResetGameStartTimer( void ) = 0; - - // Possible result codes passed to On functions - enum ReturnType - { - RET_OK, // Any function - RET_TIMEOUT, // OnGameJoin/Leave/Start, etc - RET_GAME_FULL, // OnGameJoin - RET_DUPLICATE_NAME, // OnGameJoin - RET_CRC_MISMATCH, // OnGameJoin - RET_SERIAL_DUPE, // OnGameJoin - RET_GAME_STARTED, // OnGameJoin - RET_GAME_EXISTS, // OnGameCreate - RET_GAME_GONE, // OnGameJoin - RET_BUSY, // OnGameCreate/Join/etc if another action is in progress - RET_UNKNOWN, // Default message for oddity - }; - UnicodeString getErrorStringFromReturnType( ReturnType ret ); - - // On functions are (generally) the result of network traffic - virtual void OnGameList( LANGameInfo *gameList ) = 0; ///< List of games - virtual void OnPlayerList( LANPlayer *playerList ) = 0; ///< List of players in the Lobby - virtual void OnGameJoin( ReturnType ret, LANGameInfo *theGame ) = 0; ///< Did we get in the game? - virtual void OnPlayerJoin( Int slot, UnicodeString playerName ) = 0; ///< Someone else joined our game (host only; joiners get a slotlist) - virtual void OnHostLeave( void ) = 0; ///< Host left the game - virtual void OnPlayerLeave( UnicodeString player ) = 0; ///< Someone left the game - virtual void OnAccept( UnsignedInt playerIP, Bool status ) = 0; ///< Someone's accept status changed - virtual void OnHasMap( UnsignedInt playerIP, Bool status ) = 0; ///< Someone's map status changed - virtual void OnChat( UnicodeString player, UnsignedInt ip, - UnicodeString message, ChatType format ) = 0; ///< Chat message from someone - virtual void OnGameStart( void ) = 0; ///< The game is starting - virtual void OnGameStartTimer( Int seconds ) = 0; - virtual void OnGameOptions( UnsignedInt playerIP, Int playerSlot, AsciiString options ) = 0; ///< Someone sent game options - virtual void OnGameCreate( ReturnType ret ) = 0; ///< Your game is created -// virtual void OnSlotList( ReturnType ret, LANGameInfo *theGame ) = 0; ///< Slotlist for a game in setup - virtual void OnNameChange( UnsignedInt IP, UnicodeString newName ) = 0; ///< Someone has morphed - - // Misc utility functions - virtual LANGameInfo * LookupGame( UnicodeString gameName ) = 0; ///< return a pointer to a game we know about - virtual LANGameInfo * LookupGameByListOffset( Int offset ) = 0; ///< return a pointer to a game we know about - virtual Bool SetLocalIP( UnsignedInt localIP ) = 0; ///< For multiple NIC machines - virtual void SetLocalIP( AsciiString localIP ) = 0; ///< For multiple NIC machines - virtual Bool AmIHost( void ) = 0; ///< Am I hosting a game? - virtual inline UnicodeString GetMyName( void ) = 0; ///< What's my name? - virtual inline LANGameInfo *GetMyGame( void ) = 0; ///< What's my Game? - virtual void fillInLANMessage( LANMessage *msg ) = 0; ///< Fill in default params - virtual void checkMOTD( void ) = 0; -}; - - -/** - * The LANAPI class is used to instantiate a singleton which - * implements the interface to all LAN broadcast communications. - */ -class LANAPI : public LANAPIInterface -{ -public: - - LANAPI(); - virtual ~LANAPI(); - - virtual void init( void ); ///< Initialize or re-initialize the instance - virtual void reset( void ); ///< reset the logic system - virtual void update( void ); ///< update the world - - virtual void setIsActive(Bool isActive); ///< tell TheLAN whether or not - - // Request functions generate network traffic - virtual void RequestLocations( void ); ///< Request everybody to respond with where they are - virtual void RequestGameJoin( LANGameInfo *game, UnsignedInt ip = 0 ); ///< Request to join a game - virtual void RequestGameJoinDirectConnect( UnsignedInt ipaddress ); ///< Request to join a game at an IP address - virtual void RequestGameLeave( void ); ///< Tell everyone we're leaving - virtual void RequestAccept( void ); ///< Indicate we're OK with the game options - virtual void RequestHasMap( void ); ///< Send our map status - virtual void RequestChat( UnicodeString message, ChatType format ); ///< Send a chat message - virtual void RequestGameStart( void ); ///< Tell everyone the game is starting - virtual void RequestGameStartTimer( Int seconds ); - virtual void RequestGameOptions( AsciiString gameOptions, Bool isPublic, UnsignedInt ip = 0 ); ///< Change the game options - virtual void RequestGameCreate( UnicodeString gameName, Bool isDirectConnect ); ///< Try to host a game - virtual void RequestGameAnnounce( void ); ///< Send out game info if host - virtual void RequestSetName( UnicodeString newName ); ///< Pick a new name -// virtual void RequestSlotList( void ); ///< Pump out the Slot info. - virtual void RequestLobbyLeave( Bool forced ); ///< Announce that we're leaving the lobby - virtual void ResetGameStartTimer( void ); - - // On functions are (generally) the result of network traffic - virtual void OnGameList( LANGameInfo *gameList ); ///< List of games - virtual void OnPlayerList( LANPlayer *playerList ); ///< List of players in the Lobby - virtual void OnGameJoin( ReturnType ret, LANGameInfo *theGame ); ///< Did we get in the game? - virtual void OnPlayerJoin( Int slot, UnicodeString playerName ); ///< Someone else joined our game (host only; joiners get a slotlist) - virtual void OnHostLeave( void ); ///< Host left the game - virtual void OnPlayerLeave( UnicodeString player ); ///< Someone left the game - virtual void OnAccept( UnsignedInt playerIP, Bool status ); ///< Someone's accept status changed - virtual void OnHasMap( UnsignedInt playerIP, Bool status ); ///< Someone's map status changed - virtual void OnChat( UnicodeString player, UnsignedInt ip, - UnicodeString message, ChatType format ); ///< Chat message from someone - virtual void OnGameStart( void ); ///< The game is starting - virtual void OnGameStartTimer( Int seconds ); - virtual void OnGameOptions( UnsignedInt playerIP, Int playerSlot, AsciiString options ); ///< Someone sent game options - virtual void OnGameCreate( ReturnType ret ); ///< Your game is created - //virtual void OnSlotList( ReturnType ret, LANGameInfo *theGame ); ///< Slotlist for a game in setup - virtual void OnNameChange( UnsignedInt IP, UnicodeString newName ); ///< Someone has morphed - virtual void OnInActive( UnsignedInt IP ); ///< Someone has alt-tabbed out. - - - // Misc utility functions - virtual LANGameInfo * LookupGame( UnicodeString gameName ); ///< return a pointer to a game we know about - virtual LANGameInfo * LookupGameByListOffset( Int offset ); ///< return a pointer to a game we know about - virtual LANPlayer * LookupPlayer( UnsignedInt playerIP ); ///< return a pointer to a player we know about - virtual Bool SetLocalIP( UnsignedInt localIP ); ///< For multiple NIC machines - virtual void SetLocalIP( AsciiString localIP ); ///< For multiple NIC machines - virtual Bool AmIHost( void ); ///< Am I hosting a game? - virtual inline UnicodeString GetMyName( void ) { return m_name; } ///< What's my name? - virtual inline LANGameInfo* GetMyGame( void ) { return m_currentGame; } ///< What's my Game? - virtual inline UnsignedInt GetLocalIP( void ) { return m_localIP; } ///< What's my IP? - virtual void fillInLANMessage( LANMessage *msg ); ///< Fill in default params - virtual void checkMOTD( void ); -protected: - - enum PendingActionType - { - ACT_NONE = 0, - ACT_JOIN, - ACT_JOINDIRECTCONNECT, - ACT_LEAVE, - }; - - static const UnsignedInt s_resendDelta; // in ms - -protected: - LANPlayer * m_lobbyPlayers; ///< List of players in the lobby - LANGameInfo * m_games; ///< List of games - UnicodeString m_name; ///< Who do we think we are? - AsciiString m_userName; ///< login name - AsciiString m_hostName; ///< machine name - UnsignedInt m_gameStartTime; - Int m_gameStartSeconds; - - PendingActionType m_pendingAction; ///< What action are we performing? - UnsignedInt m_expiration; ///< When should we give up on our action? - UnsignedInt m_actionTimeout; - UnsignedInt m_directConnectRemoteIP;///< The IP address of the game we are direct connecting to. - - // Resend timer --------------------------------------------------------------------------- - UnsignedInt m_lastResendTime; // in ms - - Bool m_isInLANMenu; ///< true while we are in a LAN menu (lobby, game options, direct connect) - Bool m_inLobby; ///< Are we in the lobby (not in a game)? - LANGameInfo * m_currentGame; ///< Pointer to game (setup screen) we are currently in (NULL for lobby) - //LANGameInfo *m_currentGameInfo; ///< Pointer to game setup info we are currently in. - - UnsignedInt m_localIP; - Transport* m_transport; - - UnsignedInt m_broadcastAddr; - - UnsignedInt m_lastUpdate; - AsciiString m_lastGameopt; /// @todo: hack for demo - remove this - - Bool m_isActive; ///< is the game currently active? - -protected: - void sendMessage(LANMessage *msg, UnsignedInt ip = 0); // Convenience function - void removePlayer(LANPlayer *player); - void removeGame(LANGameInfo *game); - void addPlayer(LANPlayer *player); - void addGame(LANGameInfo *game); - AsciiString createSlotString( void ); - - // Functions to handle incoming messages ----------------------------------- - void handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ); - void handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP ); - void handleLobbyAnnounce( LANMessage *msg, UnsignedInt senderIP ); - void handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP ); - void handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ); - void handleJoinAccept( LANMessage *msg, UnsignedInt senderIP ); - void handleJoinDeny( LANMessage *msg, UnsignedInt senderIP ); - void handleRequestGameLeave( LANMessage *msg, UnsignedInt senderIP ); - void handleRequestLobbyLeave( LANMessage *msg, UnsignedInt senderIP ); - void handleSetAccept( LANMessage *msg, UnsignedInt senderIP ); - void handleHasMap( LANMessage *msg, UnsignedInt senderIP ); - void handleChat( LANMessage *msg, UnsignedInt senderIP ); - void handleGameStart( LANMessage *msg, UnsignedInt senderIP ); - void handleGameStartTimer( LANMessage *msg, UnsignedInt senderIP ); - void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); - void handleInActive( LANMessage *msg, UnsignedInt senderIP ); - -}; - - - -/** - * LAN message class - */ -#pragma pack(push, 1) -struct LANMessage -{ - enum Type ///< What kind of message are we? - { - // Locating everybody - MSG_REQUEST_LOCATIONS, ///< Hey, where is everybody? - MSG_GAME_ANNOUNCE, ///< Here I am, and here's my game info! - MSG_LOBBY_ANNOUNCE, ///< Hey, I'm in the lobby! - - // Joining games - MSG_REQUEST_JOIN, ///< Let me in! Let me in! - MSG_JOIN_ACCEPT, ///< Okay, you can join. - MSG_JOIN_DENY, ///< Go away! We don't want any! - - // Leaving games - MSG_REQUEST_GAME_LEAVE, ///< I want to leave the game - MSG_REQUEST_LOBBY_LEAVE,///< I'm leaving the lobby - - // Game options, chat, etc - MSG_SET_ACCEPT, ///< I'm cool with everything as is. - MSG_MAP_AVAILABILITY, ///< I do (not) have the map. - MSG_CHAT, ///< Just spouting my mouth off. - MSG_GAME_START, ///< Hold on; we're starting! - MSG_GAME_START_TIMER, ///< The game will start in N seconds - MSG_GAME_OPTIONS, ///< Here's some info about the game. - MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey. - - MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address - } messageType; - - WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience - char userName[g_lanLoginNameLength+1]; ///< login name, for convenience - char hostName[g_lanHostNameLength+1]; ///< machine name, for convenience - - // No additional data is required for REQUEST_LOCATIONS, LOBBY_ANNOUNCE, - // REQUEST_LOBBY_LEAVE, GAME_START. - union - { - // StartTimer is sent with GAME_START_TIMER - struct - { - Int seconds; - } StartTimer; - - // GameJoined is sent with REQUEST_GAME_LEAVE - struct - { - WideChar gameName[g_lanGameNameLength+1]; - } GameToLeave; - - // GameInfo if sent with GAME_ANNOUNCE - struct - { - WideChar gameName[g_lanGameNameLength+1]; - Bool inProgress; - char options[m_lanMaxOptionsLength+1]; - Bool isDirectConnect; - } GameInfo; - - // PlayerInfo is sent with REQUEST_GAME_INFO for direct connect games. - struct - { - UnsignedInt ip; - WideChar playerName[g_lanPlayerNameLength+1]; - } PlayerInfo; - - // GameToJoin is sent with REQUEST_JOIN - struct - { - UnsignedInt gameIP; - UnsignedInt exeCRC; - UnsignedInt iniCRC; - char serial[g_maxSerialLength]; - } GameToJoin; - - // GameJoined is sent with JOIN_ACCEPT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt gameIP; - UnsignedInt playerIP; - Int slotPosition; - } GameJoined; - - // GameNotJoined is sent with JOIN_DENY - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt gameIP; - UnsignedInt playerIP; - LANAPIInterface::ReturnType reason; - } GameNotJoined; - - // Accept is sent with SET_ACCEPT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - Bool isAccepted; - } Accept; - - // Accept is sent with MAP_AVAILABILITY - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt mapCRC; // to make sure we're talking about the same map - Bool hasMap; - } MapStatus; - - // Chat is sent with CHAT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - LANAPIInterface::ChatType chatType; - WideChar message[g_lanMaxChatLength+1]; - } Chat; - - // GameOptions is sent with GAME_OPTIONS - struct - { - char options[m_lanMaxOptionsLength+1]; - } GameOptions; - - }; -}; -#pragma pack(pop) diff --git a/Generals/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/Generals/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h deleted file mode 100644 index 48d5ee28a89..00000000000 --- a/Generals/Code/GameEngine/Include/GameNetwork/LANAPICallbacks.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////// -// FILE: LANAPICallbacks.h -// Author: Chris Huybregts, October 2001 -// Description: LAN API Callbacks header -/////////////////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "Common/NameKeyGenerator.h" -#include "GameClient/Shell.h" -#include "GameClient/GadgetListBox.h" -#include "GameClient/GadgetTextEntry.h" -#include "GameNetwork/LANAPI.h" - - -// LAN API Singleton ---------------------------------------------------------------------- -extern LANAPI *TheLAN; - -//external declarations of the Gadgets the callbacks can use -// LanLobby -extern NameKeyType listboxChatWindowID; -extern GameWindow *listboxChatWindow; -extern GameWindow *listboxPlayers; -extern NameKeyType listboxGamesID; -extern GameWindow *listboxGames; -//LanGame Options screen -extern NameKeyType listboxChatWindowLanGameID; -extern GameWindow *listboxChatWindowLanGame; -extern WindowLayout *mapSelectLayout; -// ScoreScreen -extern NameKeyType listboxChatWindowScoreScreenID; -extern GameWindow *listboxChatWindowScoreScreen; - - -//Colors used for the chat dialogs -extern const Color playerColor; -extern const Color gameColor; -extern const Color gameInProgressColor; -extern const Color chatNormalColor; -extern const Color chatActionColor; -extern const Color chatLocalNormalColor; -extern const Color chatLocalActionColor; -extern const Color chatSystemColor; -extern const Color chatSystemColor; -extern const Color acceptTrueColor; -extern const Color acceptFalseColor; - - -void lanUpdateSlotList( void ); -void updateGameOptions( void ); -void setLANPlayerTooltip(LANPlayer* player); - -//Enum is used for the utility function so other windows do not need -//to know about controls on LanGameOptions window. -enum PostToLanGameType CPP_11(: Int){ SEND_GAME_OPTS = 0, - MAP_BACK, - POST_TO_LAN_GAME_TYPE_COUNT }; -//the utility function mentioned above -void PostToLanGameOptions(PostToLanGameType post); diff --git a/Generals/Code/GameEngine/Include/GameNetwork/LANPlayer.h b/Generals/Code/GameEngine/Include/GameNetwork/LANPlayer.h deleted file mode 100644 index e87f3880b68..00000000000 --- a/Generals/Code/GameEngine/Include/GameNetwork/LANPlayer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// LANPlayer.h /////////////////////////////////////////////////////////////// -// LAN Player Class used for both the LANAPI and LANGameInfo -// Author: Matthew D. Campbell, October 2001 (Pulled out of LANAPI.h by CLH on 12/21/01 - -#pragma once - -/** - * LAN player class. This is for players sitting in the lobby. Players are - * uniquely identified by their IP addresses. - */ -class LANPlayer -{ -public: - LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; } - - // Access functions - inline UnicodeString getName( void ) { return m_name; } - inline void setName( UnicodeString name ) { m_name = name; } - inline UnicodeString getLogin( void ) { return m_login; } - inline void setLogin( UnicodeString name ) { m_login = name; } - inline void setLogin( AsciiString name ) { m_login.translate(name); } - inline UnicodeString getHost( void ) { return m_host; } - inline void setHost( UnicodeString name ) { m_host = name; } - inline void setHost( AsciiString name ) { m_host.translate(name); } - inline UnsignedInt getLastHeard( void ) { return m_lastHeard; } - inline void setLastHeard( UnsignedInt lastHeard ) { m_lastHeard = lastHeard; } - inline LANPlayer *getNext( void ) { return m_next; } - inline void setNext( LANPlayer *next ) { m_next = next; } - inline UnsignedInt getIP( void ) { return m_IP; } - inline void setIP( UnsignedInt IP ) { m_IP = IP; } - -protected: - UnicodeString m_name; ///< Player name - UnicodeString m_login; ///< login name - UnicodeString m_host; ///< machine name - UnsignedInt m_lastHeard; ///< The last time we heard from this player (for timeout purposes) - LANPlayer *m_next; ///< Linked list pointer - UnsignedInt m_IP; ///< Player's IP -}; diff --git a/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp b/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp deleted file mode 100644 index e565a091ae7..00000000000 --- a/Generals/Code/GameEngine/Source/GameNetwork/GameInfo.cpp +++ /dev/null @@ -1,1636 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// FILE: GameInfo.cpp ////////////////////////////////////////////////////// -// game setup state info -// Author: Matthew D. Campbell, December 2001 - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/CRCDebug.h" -#include "Common/file.h" -#include "Common/FileSystem.h" -#include "Common/GameState.h" -#include "GameClient/GameText.h" -#include "GameClient/MapUtil.h" -#include "Common/MultiplayerSettings.h" -#include "Common/PlayerTemplate.h" -#include "Common/Xfer.h" -#include "GameNetwork/FileTransfer.h" -#include "GameNetwork/GameInfo.h" -#include "GameNetwork/GameSpy/ThreadUtils.h" -#include "GameNetwork/GameSpy/StagingRoomGameInfo.h" -#include "GameNetwork/LANAPI.h" // for testing packet size -#include "GameNetwork/LANAPICallbacks.h" // for testing packet size -#include "strtok_r.h" - - - -GameInfo *TheGameInfo = NULL; - -// GameSlot ---------------------------------------- - -GameSlot::GameSlot() -{ - reset(); -} - -void GameSlot::reset() -{ - m_state = SLOT_CLOSED; // decent default - m_isAccepted = false; - m_hasMap = true; - m_color = -1; - m_startPos = -1; - m_playerTemplate = -1; - m_teamNumber = -1; - m_NATBehavior = FirewallHelperClass::FIREWALL_TYPE_SIMPLE; - m_lastFrameInGame = 0; - m_disconnected = FALSE; - m_port = 0; - m_isMuted = FALSE; - m_origPlayerTemplate = -1; - m_origStartPos = -1; - m_origColor = -1; -} - -void GameSlot::saveOffOriginalInfo( void ) -{ - DEBUG_LOG(("GameSlot::saveOffOriginalInfo() - orig was color=%d, pos=%d, house=%d", - m_origColor, m_origStartPos, m_origPlayerTemplate)); - m_origPlayerTemplate = m_playerTemplate; - m_origStartPos = m_startPos; - m_origColor = m_color; - DEBUG_LOG(("GameSlot::saveOffOriginalInfo() - color=%d, pos=%d, house=%d", - m_color, m_startPos, m_playerTemplate)); -} - -static Int getSlotIndex(const GameSlot *slot) -{ - for (Int i=0; igetConstSlot(i) == slot) - return i; - } - return -1; -} - -static Bool isSlotLocalAlly(const GameSlot *slot) -{ - Int slotIndex = getSlotIndex(slot); - Int localIndex = TheGameInfo->getLocalSlotNum(); - const GameSlot *localSlot = TheGameInfo->getConstSlot(localIndex); - - // if either doesn't exist, not an ally - if (slotIndex < 0 || localIndex < 0) - return FALSE; - - // if slot is us, ally - if (slotIndex == localIndex) - return TRUE; - - // if slot is same team as us, ally - if (slot->getTeamNumber() == localSlot->getTeamNumber() && slot->getTeamNumber() >= 0) - return TRUE; - - // if we're an observer, we see all - if (localSlot->getOriginalPlayerTemplate() == PLAYERTEMPLATE_OBSERVER) - return TRUE; - - // nope - return FALSE; -} - -UnicodeString GameSlot::getApparentPlayerTemplateDisplayName( void ) const -{ - if (TheMultiplayerSettings && TheMultiplayerSettings->showRandomPlayerTemplate() && - m_origPlayerTemplate == PLAYERTEMPLATE_RANDOM && !isSlotLocalAlly(this)) - { - return TheGameText->fetch("GUI:Random"); - } - else if (m_origPlayerTemplate == PLAYERTEMPLATE_OBSERVER) - { - return TheGameText->fetch("GUI:Observer"); - } - DEBUG_LOG(("Fetching player template display name for player template %d (orig is %d)", - m_playerTemplate, m_origPlayerTemplate)); - if (m_playerTemplate < 0) - { - return TheGameText->fetch("GUI:Random"); - } - return ThePlayerTemplateStore->getNthPlayerTemplate(m_playerTemplate)->getDisplayName(); -} - -Int GameSlot::getApparentPlayerTemplate( void ) const -{ - if (TheMultiplayerSettings && TheMultiplayerSettings->showRandomPlayerTemplate() && - !isSlotLocalAlly(this)) - { - return m_origPlayerTemplate; - } - return m_playerTemplate; -} - -Int GameSlot::getApparentColor( void ) const -{ - if (TheMultiplayerSettings && m_origPlayerTemplate == PLAYERTEMPLATE_OBSERVER) - return TheMultiplayerSettings->getColor(PLAYERTEMPLATE_OBSERVER)->getColor(); - - if (TheMultiplayerSettings && TheMultiplayerSettings->showRandomColor() && - !isSlotLocalAlly(this)) - { - return m_origColor; - } - return m_color; -} - -Int GameSlot::getApparentStartPos( void ) const -{ - if (TheMultiplayerSettings && TheMultiplayerSettings->showRandomStartPos() && - !isSlotLocalAlly(this)) - { - return m_origStartPos; - } - return m_startPos; -} - - -void GameSlot::unAccept( void ) -{ - if (isHuman()) - { - m_isAccepted = false; - } -} - -void GameSlot::setMapAvailability( Bool hasMap ) -{ - if (isHuman()) - { - m_hasMap = hasMap; - } -} - -void GameSlot::setState( SlotState state, UnicodeString name, UnsignedInt IP ) -{ - if (!(isAI() && (state == SLOT_EASY_AI || state == SLOT_MED_AI || state == SLOT_BRUTAL_AI))) - { - m_color = -1; - m_startPos = -1; - m_playerTemplate = -1; - m_teamNumber = -1; - - if (state == SLOT_OPEN && TheGameSpyGame && TheGameSpyGame->getConstSlot(0) == this) - { - DEBUG_CRASH(("Game Is Hosed!")); - } - } - if (state == SLOT_PLAYER) - { - reset(); - m_state = state; - m_name = name; - } - else - { - m_state = state; - m_isAccepted = true; - m_hasMap = true; - switch(state) - { - case SLOT_OPEN: - m_name = TheGameText->fetch("GUI:Open"); - break; - case SLOT_EASY_AI: - m_name = TheGameText->fetch("GUI:EasyAI"); - break; - case SLOT_MED_AI: - m_name = TheGameText->fetch("GUI:MediumAI"); - break; - case SLOT_BRUTAL_AI: - m_name = TheGameText->fetch("GUI:HardAI"); - break; - case SLOT_CLOSED: - default: - m_name = TheGameText->fetch("GUI:Closed"); - break; - } - } - - m_IP = IP; -} - -// Various tests -Bool GameSlot::isHuman( void ) const -{ - return m_state == SLOT_PLAYER; -} - -Bool GameSlot::isOccupied( void ) const -{ - return m_state == SLOT_PLAYER || m_state == SLOT_EASY_AI || m_state == SLOT_MED_AI || m_state == SLOT_BRUTAL_AI; -} - -Bool GameSlot::isAI( void ) const -{ - return m_state == SLOT_EASY_AI || m_state == SLOT_MED_AI || m_state == SLOT_BRUTAL_AI; -} - -Bool GameSlot::isPlayer( AsciiString userName ) const -{ - UnicodeString uName; - uName.translate(userName); - return (m_state == SLOT_PLAYER && !m_name.compareNoCase(uName)); -} - -Bool GameSlot::isPlayer( UnicodeString userName ) const -{ - return (m_state == SLOT_PLAYER && !m_name.compareNoCase(userName)); -} - -Bool GameSlot::isPlayer( UnsignedInt ip ) const -{ - return (m_state == SLOT_PLAYER && m_IP == ip); -} - -Bool GameSlot::isOpen( void ) const -{ - return m_state == SLOT_OPEN; -} - -// GameInfo ---------------------------------------- - -GameInfo::GameInfo() -{ - for (int i=0; im_defaultStartingCash; - - // - - for (Int i=0; ireset(); - } - - m_preorderMask = 0; -} - -Bool GameInfo::isPlayerPreorder(Int index) -{ - if (index >= 0 && index < MAX_SLOTS) - return ((m_preorderMask & (1 << index)) != 0); - return FALSE; -} - -void GameInfo::markPlayerAsPreorder(Int index) -{ - if (index >= 0 && index < MAX_SLOTS) - m_preorderMask |= 1 << index; -} - - -void GameInfo::clearSlotList( void ) -{ - for (int i=0; isetState(SLOT_CLOSED); - } -} - -Int GameInfo::getNumPlayers( void ) const -{ - Int numPlayers = 0; - for (int i=0; iisOccupied()) - numPlayers++; - } - return numPlayers; -} - -Int GameInfo::getNumNonObserverPlayers( void ) const -{ - Int numPlayers = 0; - for (int i=0; iisOccupied() && m_slot[i]->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER) - numPlayers++; - } - return numPlayers; -} - -Int GameInfo::getMaxPlayers( void ) const -{ - if (!TheMapCache) - return -1; - - AsciiString lowerMap = m_mapName; - lowerMap.toLower(); - MapCache::iterator it = TheMapCache->find(lowerMap); - if (it == TheMapCache->end()) - return -1; - MapMetaData data = it->second; - return data.m_numPlayers; -} - -void GameInfo::enterGame( void ) -{ - DEBUG_ASSERTCRASH(!m_inGame && !m_inProgress, ("Entering game at a bad time!")); - reset(); - m_inGame = true; - m_inProgress = false; -} - -void GameInfo::leaveGame( void ) -{ - DEBUG_ASSERTCRASH(m_inGame && !m_inProgress, ("Leaving game at a bad time!")); - reset(); -} - -void GameInfo::startGame( Int gameID ) -{ - DEBUG_ASSERTCRASH(m_inGame && !m_inProgress, ("Starting game at a bad time!")); - m_gameID = gameID; - closeOpenSlots(); - m_inProgress = true; -} - -void GameInfo::endGame( void ) -{ - DEBUG_ASSERTCRASH(m_inGame && m_inProgress, ("Ending game without playing one!")); - m_inGame = false; - m_inProgress = false; -} - -void GameInfo::setSlot( Int slotNum, GameSlot slotInfo ) -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("GameInfo::setSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return; - - DEBUG_ASSERTCRASH( m_slot[slotNum], ("NULL slot pointer")); - if (!m_slot[slotNum]) - return; - -// Bool isHuman = slotInfo.isHuman(); -// Bool wasHuman = m_slot[slotNum]->isHuman(); - - if (slotNum == 0) - { - slotInfo.setAccept(); - slotInfo.setMapAvailability(true); - } - *m_slot[slotNum] = slotInfo; - -#ifdef DEBUG_LOGGING - UnsignedInt ip = slotInfo.getIP(); -#endif - - DEBUG_LOG(("GameInfo::setSlot - setting slot %d to be player %ls with IP %d.%d.%d.%d", slotNum, slotInfo.getName().str(), - PRINTF_IP_AS_4_INTS(ip))); -} - -GameSlot* GameInfo::getSlot( Int slotNum ) -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("GameInfo::getSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return NULL; - - DEBUG_ASSERTCRASH( m_slot[slotNum], ("NULL slot pointer") ); - return m_slot[slotNum]; -} - -const GameSlot* GameInfo::getConstSlot( Int slotNum ) const -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("GameInfo::getSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return NULL; - - DEBUG_ASSERTCRASH( m_slot[slotNum], ("NULL slot pointer") ); - return m_slot[slotNum]; -} - -Int GameInfo::getLocalSlotNum( void ) const -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for local game slot while not in game")); - if (!m_inGame) - return -1; - - for (Int i=0; iisPlayer(m_localIP)) - return i; - } - return -1; -} - -Int GameInfo::getSlotNum( AsciiString userName ) const -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game")); - if (!m_inGame) - return -1; - - UnicodeString uName; - uName.translate(userName); - for (Int i=0; iisPlayer( uName )) - return i; - } - return -1; -} - -Bool GameInfo::amIHost( void ) const -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game")); - if (!m_inGame) - return false; - - return getConstSlot(0)->isPlayer(m_localIP); -} - -void GameInfo::setMap( AsciiString mapName ) -{ - m_mapName = mapName; - if (m_inGame && amIHost()) - { - const MapMetaData *mapData = TheMapCache->findMap( mapName ); - if (mapData) - { - m_mapMask = 1; - AsciiString path = mapName; - path.truncateBy(3); - path.concat("tga"); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", path.str())); - File *fp = TheFileSystem->openFile(path.str()); - if (fp) - { - m_mapMask |= 2; - fp->close(); - fp = NULL; - } - - AsciiString newMapName; - if (mapName.getLength() > 0) - { - AsciiString token; - mapName.nextToken(&token, "\\/"); - // add all the tokens except the last one. - // that way we don't add the filename, just the - // directory name, we can do this since the filename - // is just the directory name with the file extention - // added onto it. - while (mapName.find('\\') != NULL) - { - if (newMapName.getLength() > 0) - { - newMapName.concat('/'); - } - newMapName.concat(token); - mapName.nextToken(&token, "\\/"); - } - } - newMapName.concat("/map.ini"); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", newMapName.str())); - fp = TheFileSystem->openFile(newMapName.str()); - if (fp) - { - m_mapMask |= 4; - fp->close(); - fp = NULL; - } - - path = GetStrFileFromMap(m_mapName); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", path.str())); - fp = TheFileSystem->openFile(path.str()); - if (fp) - { - m_mapMask |= 8; - fp->close(); - fp = NULL; - } - - path = GetSoloINIFromMap(m_mapName); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", path.str())); - fp = TheFileSystem->openFile(path.str()); - if (fp) - { - m_mapMask |= 16; - fp->close(); - fp = NULL; - } - - path = GetAssetUsageFromMap(m_mapName); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", path.str())); - fp = TheFileSystem->openFile(path.str()); - if (fp) - { - m_mapMask |= 32; - fp->close(); - fp = NULL; - } - - path = GetReadmeFromMap(m_mapName); - DEBUG_LOG(("GameInfo::setMap() - Looking for '%s'", path.str())); - fp = TheFileSystem->openFile(path.str()); - if (fp) - { - m_mapMask |= 64; - fp->close(); - fp = NULL; - } - } - else - { - m_mapMask = 0; - } - } -} - -void GameInfo::setMapContentsMask( Int mask ) -{ - m_mapMask = mask; -} - -void GameInfo::setMapCRC( UnsignedInt mapCRC ) -{ - m_mapCRC = mapCRC; - if (!TheMapCache) - return; - - // check the map cache - if (m_inGame && getLocalSlotNum() >= 0) - { - //TheMapCache->updateCache(); - AsciiString lowerMap = m_mapName; - lowerMap.toLower(); - //DEBUG_LOG(("GameInfo::setMapCRC - looking for map file \"%s\" in the map cache", lowerMap.str())); - std::map::iterator it = TheMapCache->find(lowerMap); - if (it == TheMapCache->end()) - { - /* - DEBUG_LOG(("GameInfo::setMapCRC - could not find map file.")); - it = TheMapCache->begin(); - while (it != TheMapCache->end()) - { - DEBUG_LOG(("\t\"%s\"", it->first.str())); - ++it; - } - */ - getSlot(getLocalSlotNum())->setMapAvailability(false); - } - else if (m_mapCRC != it->second.m_CRC) - { - DEBUG_LOG(("GameInfo::setMapCRC - map CRC's do not match (%X/%X).", m_mapCRC, it->second.m_CRC)); - getSlot(getLocalSlotNum())->setMapAvailability(false); - } - else - { - //DEBUG_LOG(("GameInfo::setMapCRC - map CRC's match.")); - getSlot(getLocalSlotNum())->setMapAvailability(true); - } - } -} - -void GameInfo::setMapSize( UnsignedInt mapSize ) -{ - m_mapSize = mapSize; - if (!TheMapCache) - return; - - // check the map cache - if (m_inGame && getLocalSlotNum() >= 0) - { - //TheMapCache->updateCache(); - AsciiString lowerMap = m_mapName; - lowerMap.toLower(); - std::map::iterator it = TheMapCache->find(lowerMap); - if (it == TheMapCache->end()) - { - DEBUG_LOG(("GameInfo::setMapSize - could not find map file.")); - getSlot(getLocalSlotNum())->setMapAvailability(false); - } - else if (m_mapCRC != it->second.m_CRC) - { - DEBUG_LOG(("GameInfo::setMapSize - map CRC's do not match.")); - getSlot(getLocalSlotNum())->setMapAvailability(false); - } - else - { - //DEBUG_LOG(("GameInfo::setMapSize - map CRC's match.")); - getSlot(getLocalSlotNum())->setMapAvailability(true); - } - } -} - -void GameInfo::setSeed( Int seed ) -{ - m_seed = seed; -} - -void GameInfo::setSlotPointer( Int index, GameSlot *slot ) -{ - if (index < 0 || index >= MAX_SLOTS) - return; - - m_slot[index] = slot; -} - -void GameInfo::setSuperweaponRestriction( UnsignedShort restriction ) -{ - m_superweaponRestriction = restriction; -} - -void GameInfo::setStartingCash( const Money & startingCash ) -{ - m_startingCash = startingCash; -} - -Bool GameInfo::isColorTaken(Int colorIdx, Int slotToIgnore ) const -{ - for (Int i=0; igetColor() == colorIdx && i != slotToIgnore) - return true; - } - return false; -} - -Bool GameInfo::isStartPositionTaken(Int positionIdx, Int slotToIgnore ) const -{ - for (Int i=0; igetStartPos() == positionIdx && i != slotToIgnore) - return true; - } - return false; -} - -void GameInfo::resetAccepted( void ) -{ - GameSlot *slot = getSlot(0); - if (slot) - slot->setAccept(); - for(int i = 1; i< MAX_SLOTS; i++) - { - slot = getSlot(i); - if (slot) - slot->unAccept(); - } -} - -void GameInfo::resetStartSpots() -{ - GameSlot *slot = NULL; - for (Int i = 0; i < MAX_SLOTS; ++i) - { - slot = getSlot(i); - if (slot != NULL) - { - slot->setStartPos(-1); - } - } -} - -// adjust the slots in the game to open or closed -// depending on the players in there now and the number of -// players the map can hold. -void GameInfo::adjustSlotsForMap() -{ - const MapMetaData *md = TheMapCache->findMap(m_mapName); - if (md != NULL) - { - // get the number of players allowed from the map. - Int numPlayers = md->m_numPlayers; - Int numPlayerSlots = 0; - - // first get the number of occupied slots. - Int i = 0; - for (; i < MAX_SLOTS; ++i) - { - GameSlot *tempSlot = getSlot(i); - if (tempSlot->isOccupied()) - { - ++numPlayerSlots; - } - } - - // now go through and close the appropriate number of slots. - // note that no players are kicked in this process, we leave - // that up to the user. - for (i = 0; i < MAX_SLOTS; ++i) - { - // we have room for more players, if this slot is unoccupied, set it to open. - GameSlot *slot = getSlot(i); - if (numPlayers > numPlayerSlots) - { - if (!(slot->isOccupied())) - { - GameSlot newSlot; - newSlot.setState(SLOT_OPEN); - setSlot(i, newSlot); - ++numPlayerSlots; - } - } - else - { - if (!(slot->isOccupied())) - { - // we don't have any more room, set this slot to closed. - GameSlot newSlot; - newSlot.setState(SLOT_CLOSED); - setSlot(i, newSlot); - } - } - } - } -} - -void GameInfo::closeOpenSlots() -{ - for (Int i = 0; i < MAX_SLOTS; ++i) - { - GameSlot *slot = getSlot(i); - if (!(slot->isOccupied())) - { - GameSlot newSlot; - newSlot.setState(SLOT_CLOSED); - setSlot(i, newSlot); - } - } -} - -static Bool isSlotLocalAlly(GameInfo *game, const GameSlot *slot) -{ - const GameSlot *localSlot = game->getConstSlot(game->getLocalSlotNum()); - if (!localSlot) - return TRUE; - - if (slot == localSlot) - return TRUE; - - if (slot->getTeamNumber() < 0) - return FALSE; - - return slot->getTeamNumber() == localSlot->getTeamNumber(); -} - -Bool GameInfo::isSkirmish(void) -{ - Bool sawAI = FALSE; - - for (Int i=0; iisHuman()) - return FALSE; - - if (getConstSlot(i)->isAI()) - { - if (isSlotLocalAlly(getConstSlot(i))) - return FALSE; - sawAI = TRUE; - } - } - return sawAI; -} - -Bool GameInfo::isMultiPlayer(void) -{ - for (Int i=0; iisHuman()) - return TRUE; - } - - return FALSE; -} - -Bool GameInfo::isSandbox(void) -{ - Int localSlotNum = getLocalSlotNum(); - Int localTeam = getConstSlot(localSlotNum)->getTeamNumber(); - for (Int i=0; iisOccupied() && (slot->getTeamNumber() < 0 || slot->getTeamNumber() != localTeam)) - return FALSE; - } - return TRUE; -} - - -// Convenience Functions ---------------------------------------- - -static const char slotListID = 'S'; - -AsciiString GameInfoToAsciiString( const GameInfo *game ) -{ - if (!game) - return AsciiString::TheEmptyString; - - AsciiString mapName = game->getMap(); - mapName = TheGameState->realMapPathToPortableMapPath(mapName); - AsciiString newMapName; - if (mapName.getLength() > 0) - { - AsciiString token; - mapName.nextToken(&token, "\\/"); - // add all the tokens except the last one. - // that way we don't add the filename, just the - // directory name, we can do this since the filename - // is just the directory name with the file extention - // added onto it. - while (mapName.find('\\') != NULL) - { - if (newMapName.getLength() > 0) - { - newMapName.concat('/'); - } - newMapName.concat(token); - mapName.nextToken(&token, "\\/"); - } - DEBUG_LOG(("Map name is %s", mapName.str())); - } - - AsciiString optionsString; -#if RTS_GENERALS - optionsString.format("M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;", game->getMapContentsMask(), newMapName.str(), - game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval()); -#else - optionsString.format("US=%d;M=%2.2x%s;MC=%X;MS=%d;SD=%d;C=%d;SR=%u;SC=%u;O=%c;", game->getUseStats(), game->getMapContentsMask(), newMapName.str(), - game->getMapCRC(), game->getMapSize(), game->getSeed(), game->getCRCInterval(), game->getSuperweaponRestriction(), - game->getStartingCash().countMoney(), game->oldFactionsOnly() ? 'Y' : 'N' ); -#endif - - //add player info for each slot - optionsString.concat(slotListID); - optionsString.concat('='); - for (Int i=0; igetConstSlot(i); - - AsciiString str; - if (slot && slot->isHuman()) - { - AsciiString tmp; //all this data goes after name - tmp.format( ",%X,%d,%c%c,%d,%d,%d,%d,%d:", - slot->getIP(), slot->getPort(), - (slot->isAccepted()?'T':'F'), - (slot->hasMap()?'T':'F'), - slot->getColor(), slot->getPlayerTemplate(), - slot->getStartPos(), slot->getTeamNumber(), - slot->getNATBehavior() ); - //make sure name doesn't cause overflow of m_lanMaxOptionsLength - int lenCur = tmp.getLength() + optionsString.getLength() + 2; //+2 for H and trailing ; - int lenRem = m_lanMaxOptionsLength - lenCur; //length remaining before overflowing - int lenMax = lenRem / (MAX_SLOTS-i); //share lenRem with all remaining slots - AsciiString name = WideCharStringToMultiByte(slot->getName().str()).c_str(); - while( name.getLength() > lenMax ) - name.removeLastChar(); //what a horrible way to truncate. I hate AsciiString. - - str.format( "H%s%s", name.str(), tmp.str() ); - } - else if (slot && slot->isAI()) - { - Char c; - if (slot->getState() == SLOT_EASY_AI) - c = 'E'; - else if (slot->getState() == SLOT_MED_AI) - c = 'M'; - else - c = 'H'; - str.format("C%c,%d,%d,%d,%d:", c, - slot->getColor(), slot->getPlayerTemplate(), - slot->getStartPos(), slot->getTeamNumber()); - } - else if (slot && slot->getState() == SLOT_OPEN) - { - str = "O:"; - } - else if (slot && slot->getState() == SLOT_CLOSED) - { - str = "X:"; - } - else - { - DEBUG_ASSERTCRASH(false, ("Bad slot type")); - str = "X:"; - } - optionsString.concat(str); - } - optionsString.concat(';'); - - DEBUG_ASSERTCRASH(!TheLAN || (optionsString.getLength() < m_lanMaxOptionsLength), - ("WARNING: options string is longer than expected! Length is %d, but max is %d!", - optionsString.getLength(), m_lanMaxOptionsLength)); - - return optionsString; -} - -static Int grabHexInt(const char *s) -{ - char tmp[5] = "0xff"; - tmp[2] = s[0]; - tmp[3] = s[1]; - Int b = strtol(tmp, NULL, 16); - return b; -} -Bool ParseAsciiStringToGameInfo(GameInfo *game, AsciiString options) -{ - // Parse game options - char *buf = strdup(options.str()); - char *bufPtr = buf; - char *strPos, *keyValPair; - GameSlot newSlot[MAX_SLOTS]; - Bool optionsOk = true; - AsciiString mapName; - Int mapContentsMask; - UnsignedInt mapCRC, mapSize; - Int seed = 0; - Int crc = 100; - Bool sawCRC = FALSE; - Bool oldFactionsOnly = FALSE; - Int useStats = TRUE; - Money startingCash = TheGlobalData->m_defaultStartingCash; - UnsignedShort restriction = 0; // Always the default - - Bool sawMap, sawMapCRC, sawMapSize, sawSeed, sawSlotlist, sawUseStats, sawSuperweaponRestriction, sawStartingCash, sawOldFactions; - sawMap = sawMapCRC = sawMapSize = sawSeed = sawSlotlist = sawUseStats = sawSuperweaponRestriction = sawStartingCash = sawOldFactions = FALSE; - - //DEBUG_LOG(("Saw options of %s", options.str())); - DEBUG_LOG(("ParseAsciiStringToGameInfo - parsing [%s]", options.str())); - - - while ( (keyValPair = strtok_r(bufPtr, ";", &strPos)) != NULL ) - { - bufPtr = NULL; // strtok within the same string - - AsciiString key, val; - char *pos = NULL; - char *keyPtr, *valPtr; - keyPtr = (strtok_r(keyValPair, "=", &pos)); - valPtr = (strtok_r(NULL, "\n", &pos)); - if (keyPtr) - key = keyPtr; - if (valPtr) - val = valPtr; - - if (val.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - saw empty value, quitting")); - break; - } - - if (key.compare("US") == 0) - { - useStats = atoi(val.str()); - sawUseStats = true; - } - else - if (key.compare("M") == 0) - { - if (val.getLength() < 3) - { - optionsOk = FALSE; - DEBUG_LOG(("ParseAsciiStringToGameInfo - saw bogus map; quitting")); - break; - } - mapContentsMask = grabHexInt(val.str()); - AsciiString tempstr; - AsciiString token; - tempstr = val.str()+2; - tempstr.nextToken(&token, "\\/"); - while (tempstr.getLength() > 0) - { - mapName.concat(token); - mapName.concat('\\'); - tempstr.nextToken(&token, "\\/"); - } - mapName.concat(token); - mapName.concat('\\'); - mapName.concat(token); - mapName.concat('.'); - mapName.concat(TheMapCache->getMapExtension()); - AsciiString realMapName = TheGameState->portableMapPathToRealMapPath(mapName); - if (realMapName.isEmpty()) - { - // TheSuperHackers @security slurmlord 18/06/2025 As the map file name/path from the AsciiString failed to normalize, - // in other words is bogus and points outside of the approved target directory for maps, avoid an arbitrary file overwrite vulnerability - // if the save or network game embeds a custom map to store at the location, by flagging the options as not OK and rejecting the game. - optionsOk = FALSE; - DEBUG_LOG(("ParseAsciiStringToGameInfo - saw bogus map name ('%s'); quitting", mapName.str())); - break; - } - mapName = realMapName; - sawMap = true; - DEBUG_LOG(("ParseAsciiStringToGameInfo - map name is %s", mapName.str())); - } - else if (key.compare("MC") == 0) - { - mapCRC = 0; - sscanf(val.str(), "%X", &mapCRC); - sawMapCRC = true; - } - else if (key.compare("MS") == 0) - { - mapSize = atoi(val.str()); - sawMapSize = true; - } - else if (key.compare("SD") == 0) - { - seed = atoi(val.str()); - sawSeed = true; -// DEBUG_LOG(("ParseAsciiStringToGameInfo - random seed is %d", seed)); - } - else if (key.compare("C") == 0) - { - crc = atoi(val.str()); - sawCRC = TRUE; - } - else if (key.compare("SR") == 0 ) - { - restriction = (UnsignedShort)atoi(val.str()); - sawSuperweaponRestriction = TRUE; - } - else if (key.compare("SC") == 0 ) - { - UnsignedInt startingCashAmount = strtoul( val.str(), NULL, 10 ); - startingCash.init(); - startingCash.deposit( startingCashAmount, FALSE, FALSE ); - sawStartingCash = TRUE; - } - else if (key.compare("O") == 0 ) - { - oldFactionsOnly = ( val.compareNoCase( "Y" ) == 0 ); - sawOldFactions = TRUE; - } - else if (key.getLength() == 1 && *key.str() == slotListID) - { - sawSlotlist = true; - /// @TODO: Need to read in all the slot info... big mess right now. - char *rawSlotBuf = strdup(val.str()); - char *freeMe = NULL; - AsciiString rawSlot; -// Bool slotsOk = true; //flag that lets us know whether or not the slot list is good. - -// DEBUG_LOG(("ParseAsciiStringToGameInfo - Parsing slot list")); - for (int i=0; i= TheMultiplayerSettings->getNumColors()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - player color was invalid, quitting")); - break; - } - newSlot[i].setColor(color); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - player color set to %d", color)); - - //Read playerTemplate index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue player template is empty, quitting")); - break; - } - Int playerTemplate = atoi(slotValue.str()); - if (playerTemplate < PLAYERTEMPLATE_MIN || playerTemplate >= ThePlayerTemplateStore->getPlayerTemplateCount()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - player template value is invalid, quitting")); - break; - } - newSlot[i].setPlayerTemplate(playerTemplate); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - player template is %d", playerTemplate)); - - //Read start position index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue start position is empty, quitting")); - break; - } - Int startPos = atoi(slotValue.str()); - if (startPos < -1 || startPos >= MAX_SLOTS) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - player start position is invalid, quitting")); - break; - } - newSlot[i].setStartPos(startPos); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - player start position is %d", startPos)); - - //Read team index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue team number is empty, quitting")); - break; - } - Int team = atoi(slotValue.str()); - if (team < -1 || team >= MAX_SLOTS/2) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - team number is invalid, quitting")); - break; - } - newSlot[i].setTeamNumber(team); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - team number is %d", team)); - - // Read the NAT behavior - slotValue = strtok_r(NULL, ",",&slotPos); - if (slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - NAT behavior is empty, quitting")); - break; - } - FirewallHelperClass::FirewallBehaviorType NATType = (FirewallHelperClass::FirewallBehaviorType)atoi(slotValue.str()); - if ((NATType < FirewallHelperClass::FIREWALL_MIN) || - (NATType > FirewallHelperClass::FIREWALL_MAX)) { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - NAT behavior is invalid, quitting")); - break; - } - newSlot[i].setNATBehavior(NATType); - DEBUG_LOG(("ParseAsciiStringToGameInfo - NAT behavior is %X", NATType)); - } - break; - case 'C': - { - DEBUG_LOG(("ParseAsciiStringToGameInfo - AI player")); - char *slotPos = NULL; - //Parse out the Name - AsciiString slotValue(strtok_r((char *)rawSlot.str(),",",&slotPos)); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue AI Type is empty, quitting")); - break; - } - - switch(*(slotValue.str() + 1)) - { - case 'E': - { - newSlot[i].setState(SLOT_EASY_AI); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - Easy AI")); - } - break; - case 'M': - { - newSlot[i].setState(SLOT_MED_AI); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - Medium AI")); - } - break; - case 'H': - { - newSlot[i].setState(SLOT_BRUTAL_AI); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - Brutal AI")); - } - break; - default: - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - Unknown AI, quitting")); - } - break; - } - - //Read color index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue color is empty, quitting")); - break; - } - Int color = atoi(slotValue.str()); - if (color < -1 || color >= TheMultiplayerSettings->getNumColors()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - player color was invalid, quitting")); - break; - } - newSlot[i].setColor(color); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - player color set to %d", color)); - - //Read playerTemplate index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue player template is empty, quitting")); - break; - } - Int playerTemplate = atoi(slotValue.str()); - if (playerTemplate < PLAYERTEMPLATE_MIN || playerTemplate >= ThePlayerTemplateStore->getPlayerTemplateCount()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - player template value is invalid, quitting")); - break; - } - newSlot[i].setPlayerTemplate(playerTemplate); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - player template is %d", playerTemplate)); - - //Read start pos - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue start pos is empty, quitting")); - break; - } - Int startPos = atoi(slotValue.str()); - Bool isStartPosBad = FALSE; - if (startPos < -1 || startPos >= MAX_SLOTS) - { - isStartPosBad = TRUE; - } - for (Int j=0; j= 0 && startPos == newSlot[i].getStartPos()) - { - isStartPosBad = TRUE; // can't have multiple people using the same start pos - } - } - if (isStartPosBad) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - start pos is invalid, quitting")); - break; - } - newSlot[i].setStartPos(startPos); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - start spot is %d", startPos)); - - //Read team index - slotValue = strtok_r(NULL,",",&slotPos); - if(slotValue.isEmpty()) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - slotValue team number is empty, quitting")); - break; - } - Int team = atoi(slotValue.str()); - if (team < -1 || team >= MAX_SLOTS/2) - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - team number is invalid, quitting")); - break; - } - newSlot[i].setTeamNumber(team); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - team number is %d", team)); - - } - break; - case 'O': - { - newSlot[i].setState( SLOT_OPEN ); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - Slot is open")); - } - break; - case 'X': - { - newSlot[i].setState( SLOT_CLOSED ); - //DEBUG_LOG(("ParseAsciiStringToGameInfo - Slot is closed")); - } - break; - default: - { - optionsOk = false; - DEBUG_LOG(("ParseAsciiStringToGameInfo - unrecognized slot entry, quitting")); - } - break; - } - } - - free(freeMe); - } - else - { - optionsOk = false; - break; - } - } - - free(buf); - - //DEBUG_LOG(("Options were ok == %d", optionsOk)); - if (optionsOk && sawMap && sawMapCRC && sawMapSize && sawSeed && sawSlotlist && sawCRC && sawUseStats && sawSuperweaponRestriction && sawStartingCash && sawOldFactions ) - { - // We were setting the Global Data directly here, but Instead, I'm now - // first setting the data in game. We'll set the global data when - // we start a game. - if (!game) - return true; - - //DEBUG_LOG(("ParseAsciiStringToGameInfo - game options all good, setting info")); - - for(Int i = 0; isetSlot(i,newSlot[i]); - - game->setMap(mapName); - game->setMapCRC(mapCRC); - game->setMapSize(mapSize); - game->setMapContentsMask(mapContentsMask); - game->setSeed(seed); - game->setCRCInterval(crc); - game->setUseStats(useStats); - game->setSuperweaponRestriction(restriction); - game->setStartingCash( startingCash ); - game->setOldFactionsOnly( oldFactionsOnly ); - - return true; - } - - DEBUG_LOG(("ParseAsciiStringToGameInfo - game options messed up")); - return false; -} - - -//---------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------- - -//------------------------- SkirmishGameInfo --------------------------------------------------------------- - -// ------------------------------------------------------------------------------------------------ -/** CRC */ -// ------------------------------------------------------------------------------------------------ -void SkirmishGameInfo::crc( Xfer *xfer ) -{ -} - -// ------------------------------------------------------------------------------------------------ -/** Xfer Method */ -// ------------------------------------------------------------------------------------------------ -void SkirmishGameInfo::xfer( Xfer *xfer ) -{ -#if RTS_GENERALS - const XferVersion currentVersion = 2; -#else - const XferVersion currentVersion = 4; -#endif - XferVersion version = currentVersion; - xfer->xferVersion( &version, currentVersion ); - - - xfer->xferInt(&m_preorderMask); - xfer->xferInt(&m_crcInterval); - xfer->xferBool(&m_inGame); - xfer->xferBool(&m_inProgress); - xfer->xferBool(&m_surrendered); - xfer->xferInt(&m_gameID); - - Int slot = MAX_SLOTS; - xfer->xferInt(&slot); - DEBUG_ASSERTCRASH(slot==MAX_SLOTS, ("MAX_SLOTS changed, need to change version. jba.")); - - for (slot = 0; slot < MAX_SLOTS; slot++) - { - Int state = m_slot[slot]->getState(); - xfer->xferInt(&state); - - UnicodeString name=m_slot[slot]->getName(); - if (version >= 2) - { - xfer->xferUnicodeString(&name); - } - - Bool isAccepted=m_slot[slot]->isAccepted(); - xfer->xferBool(&isAccepted); - - Bool isMuted=m_slot[slot]->isMuted(); - xfer->xferBool(&isMuted); - m_slot[slot]->mute(isMuted); - - Int color=m_slot[slot]->getColor(); - xfer->xferInt(&color); - - Int startPos=m_slot[slot]->getStartPos(); - xfer->xferInt(&startPos); - - Int playerTemplate=m_slot[slot]->getPlayerTemplate(); - xfer->xferInt(&playerTemplate); - - Int teamNumber=m_slot[slot]->getTeamNumber(); - xfer->xferInt(&teamNumber); - - Int origColor=m_slot[slot]->getOriginalColor(); - xfer->xferInt(&origColor); - - Int origStartPos=m_slot[slot]->getOriginalStartPos(); - xfer->xferInt(&origStartPos); - - Int origPlayerTemplate=m_slot[slot]->getOriginalPlayerTemplate(); - xfer->xferInt(&origPlayerTemplate); - - if( xfer->getXferMode() == XFER_LOAD ) { - m_slot[slot]->setState((SlotState)state, name); - if (isAccepted) m_slot[slot]->setAccept(); - - m_slot[slot]->setPlayerTemplate(origPlayerTemplate); - m_slot[slot]->setStartPos(origStartPos); - m_slot[slot]->setColor(origColor); - m_slot[slot]->saveOffOriginalInfo(); - - m_slot[slot]->setTeamNumber(teamNumber); - m_slot[slot]->setColor(color); - m_slot[slot]->setStartPos(startPos); - m_slot[slot]->setPlayerTemplate(playerTemplate); - } - } - - xfer->xferUnsignedInt(&m_localIP); - - xfer->xferMapName(&m_mapName); - xfer->xferUnsignedInt(&m_mapCRC); - xfer->xferUnsignedInt(&m_mapSize); - xfer->xferInt(&m_mapMask); - xfer->xferInt(&m_seed); - - if ( version >= 3 ) - { - xfer->xferUnsignedShort( &m_superweaponRestriction ); - - if ( version == 3 ) - { - // Version 3 had a bool which is now gone - Bool obsoleteBool; - xfer->xferBool( &obsoleteBool ); - } - - xfer->xferSnapshot( &m_startingCash ); - } - else if ( xfer->getXferMode() == XFER_LOAD ) - { - m_superweaponRestriction = 0; - m_startingCash = TheGlobalData->m_defaultStartingCash; - } - -} - -// ------------------------------------------------------------------------------------------------ -/** Load post process */ -// ------------------------------------------------------------------------------------------------ -void SkirmishGameInfo::loadPostProcess( void ) -{ -} - - diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp deleted file mode 100644 index 5398c52ad05..00000000000 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPI.cpp +++ /dev/null @@ -1,1283 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#define WIN32_LEAN_AND_MEAN // only bare bones windows stuff wanted - -#include "Common/crc.h" -#include "Common/GameState.h" -#include "Common/Registry.h" -#include "GameNetwork/LANAPI.h" -#include "GameNetwork/networkutil.h" -#include "Common/GlobalData.h" -#include "Common/RandomValue.h" -#include "GameClient/GameText.h" -#include "GameClient/MapUtil.h" -#include "Common/UserPreferences.h" -#include "GameLogic/GameLogic.h" - - -static const UnsignedShort lobbyPort = 8086; ///< This is the UDP port used by all LANAPI communication - -AsciiString GetMessageTypeString(UnsignedInt type); - -const UnsignedInt LANAPI::s_resendDelta = 10 * 1000; ///< This is how often we announce ourselves to the world -/* -LANGame::LANGame( void ) -{ - m_gameName = L""; - - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - m_playerName[player] = L""; - m_playerIP[player]= 0; - m_playerAccepted[player] = false; - } - m_lastHeard = 0; - m_inProgress = false; - m_next = NULL; -} -*/ - - - - -LANAPI::LANAPI( void ) : m_transport(NULL) -{ - DEBUG_LOG(("LANAPI::LANAPI() - max game option size is %d, sizeof(LANMessage)=%d, MAX_PACKET_SIZE=%d", - m_lanMaxOptionsLength, sizeof(LANMessage), MAX_PACKET_SIZE)); - - m_lastResendTime = 0; - // - m_lobbyPlayers = NULL; - m_games = NULL; - m_name = L""; // safe default? - m_pendingAction = ACT_NONE; - m_expiration = 0; - m_localIP = 0; - m_inLobby = true; - m_isInLANMenu = TRUE; - m_currentGame = NULL; - m_broadcastAddr = INADDR_BROADCAST; - m_directConnectRemoteIP = 0; - m_actionTimeout = 5000; // ms - m_lastUpdate = 0; - m_transport = new Transport; - m_isActive = TRUE; -} - -LANAPI::~LANAPI( void ) -{ - reset(); - delete m_transport; -} - -void LANAPI::init( void ) -{ - m_gameStartTime = 0; - m_gameStartSeconds = 0; - m_transport->reset(); - m_transport->init(m_localIP, lobbyPort); - m_transport->allowBroadcasts(true); - - m_pendingAction = ACT_NONE; - m_expiration = 0; - m_inLobby = true; - m_isInLANMenu = TRUE; - m_currentGame = NULL; - m_directConnectRemoteIP = 0; - - m_lastGameopt = ""; - -#if TELL_COMPUTER_IDENTITY_IN_LAN_LOBBY - char userName[UNLEN + 1]; - DWORD bufSize = ARRAY_SIZE(userName); - if (GetUserNameA(userName, &bufSize)) - { - m_userName.set(userName, bufSize - 1); - } - else - { - m_userName = "unknown"; - } - - char computerName[MAX_COMPUTERNAME_LENGTH + 1]; - bufSize = ARRAY_SIZE(computerName); - if (GetComputerNameA(computerName, &bufSize)) - { - m_hostName.set(computerName, bufSize - 1); - } - else - { - m_hostName = "unknown"; - } -#endif -} - -void LANAPI::reset( void ) -{ - if (m_inLobby) - { - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_REQUEST_LOBBY_LEAVE; - sendMessage(&msg); - } - m_transport->update(); - - LANGameInfo *theGame = m_games; - LANGameInfo *deletableGame = NULL; - - while (theGame) - { - deletableGame = theGame; - theGame = theGame->getNext(); - delete deletableGame; - } - - LANPlayer *thePlayer = m_lobbyPlayers; - LANPlayer *deletablePlayer = NULL; - - while (thePlayer) - { - deletablePlayer = thePlayer; - thePlayer = thePlayer->getNext(); - delete deletablePlayer; - } - - m_games = NULL; - m_lobbyPlayers = NULL; - m_directConnectRemoteIP = 0; - m_pendingAction = ACT_NONE; - m_expiration = 0; - m_inLobby = true; - m_isInLANMenu = TRUE; - m_currentGame = NULL; - -} - -void LANAPI::sendMessage(LANMessage *msg, UnsignedInt ip /* = 0 */) -{ - if (ip != 0) - { - m_transport->queueSend(ip, lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */); - } - else if ((m_currentGame != NULL) && (m_currentGame->getIsDirectConnect())) - { - Int localSlot = m_currentGame->getLocalSlotNum(); - for (Int i = 0; i < MAX_SLOTS; ++i) - { - if (i != localSlot) { - GameSlot *slot = m_currentGame->getSlot(i); - if ((slot != NULL) && (slot->isHuman())) { - m_transport->queueSend(slot->getIP(), lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */); - } - } - } - } - else - { - m_transport->queueSend(m_broadcastAddr, lobbyPort, (unsigned char *)msg, sizeof(LANMessage) /*, 0, 0 */); - } -} - - -AsciiString GetMessageTypeString(UnsignedInt type) -{ - AsciiString returnString; - - switch (type) - { - case LANMessage::MSG_REQUEST_LOCATIONS: - returnString.format( "Request Locations (%d)",type); - break; - case LANMessage::MSG_GAME_ANNOUNCE: - returnString.format("Game Announce (%d)",type); - break; - case LANMessage::MSG_LOBBY_ANNOUNCE: - returnString.format("Lobby Announce (%d)",type); - break; - case LANMessage::MSG_REQUEST_JOIN: - returnString.format("Request Join (%d)",type); - break; - case LANMessage::MSG_JOIN_ACCEPT: - returnString.format("Join Accept (%d)",type); - break; - case LANMessage::MSG_JOIN_DENY: - returnString.format("Join Deny (%d)",type); - break; - case LANMessage::MSG_REQUEST_GAME_LEAVE: - returnString.format("Request Game Leave (%d)",type); - break; - case LANMessage::MSG_REQUEST_LOBBY_LEAVE: - returnString.format("Request Lobby Leave (%d)",type); - break; - case LANMessage::MSG_SET_ACCEPT: - returnString.format("Set Accept(%d)",type); - break; - case LANMessage::MSG_CHAT: - returnString.format("Chat (%d)",type); - break; - case LANMessage::MSG_GAME_START: - returnString.format("Game Start (%d)",type); - break; - case LANMessage::MSG_GAME_START_TIMER: - returnString.format("Game Start Timer (%d)",type); - break; - case LANMessage::MSG_GAME_OPTIONS: - returnString.format("Game Options (%d)",type); - break; - case LANMessage::MSG_REQUEST_GAME_INFO: - returnString.format("Request GameInfo (%d)", type); - break; - case LANMessage::MSG_INACTIVE: - returnString.format("Inactive (%d)", type); - break; - default: - returnString.format("Unknown Message (%d)",type); - } - return returnString; -} - - -void LANAPI::checkMOTD( void ) -{ -#if defined(RTS_DEBUG) - if (TheGlobalData->m_useLocalMOTD) - { - // for a playtest, let's log some play statistics, eh? - if (TheGlobalData->m_playStats <= 0) - TheWritableGlobalData->m_playStats = 30; - - static UnsignedInt oldMOTDCRC = 0; - UnsignedInt newMOTDCRC = 0; - AsciiString asciiMOTD; - char buf[4096]; - FILE *fp = fopen(TheGlobalData->m_MOTDPath.str(), "r"); - Int len; - if (fp) - { - while( (len = fread(buf, 1, 4096, fp)) > 0 ) - { - buf[len] = 0; - asciiMOTD.concat(buf); - } - fclose(fp); - CRC crcObj; - crcObj.computeCRC(asciiMOTD.str(), asciiMOTD.getLength()); - newMOTDCRC = crcObj.get(); - } - - if (oldMOTDCRC != newMOTDCRC) - { - // different MOTD... display it - oldMOTDCRC = newMOTDCRC; - AsciiString line; - while (asciiMOTD.nextToken(&line, "\n")) - { - if (line.getCharAt(line.getLength()-1) == '\r') - line.removeLastChar(); // there is a trailing '\r' - - if (line.isEmpty()) - { - line = " "; - } - - UnicodeString uniLine; - uniLine.translate(line); - OnChat( UnicodeString(L"MOTD"), 0, uniLine, LANCHAT_SYSTEM ); - } - } - } -#endif -} - -extern Bool LANbuttonPushed; -extern Bool LANSocketErrorDetected; -void LANAPI::update( void ) -{ - if(LANbuttonPushed) - return; - static const UnsignedInt LANAPIUpdateDelay = 200; - UnsignedInt now = timeGetTime(); - - if( now > m_lastUpdate + LANAPIUpdateDelay) - { - m_lastUpdate = now; - } - else - { - return; - } - - // Let the UDP socket breathe - if ((m_transport->update() == FALSE) && (LANSocketErrorDetected == FALSE)) { - if (m_isInLANMenu == TRUE) { - LANSocketErrorDetected = TRUE; - } - } - - // Handle any new messages - int i; - for (i=0; im_inBuffer[i].length > 0) - { - // Process the new message - UnsignedInt senderIP = m_transport->m_inBuffer[i].addr; - if (senderIP == m_localIP) - { - m_transport->m_inBuffer[i].length = 0; - continue; - } - - LANMessage *msg = (LANMessage *)(m_transport->m_inBuffer[i].data); - //DEBUG_LOG(("LAN message type %s from %ls (%s@%s)", GetMessageTypeString(msg->messageType).str(), - // msg->name, msg->userName, msg->hostName)); - switch (msg->messageType) - { - // Location specification - case LANMessage::MSG_REQUEST_LOCATIONS: // Hey, where is everybody? - DEBUG_LOG(("LANAPI::update - got a MSG_REQUEST_LOCATIONS from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleRequestLocations( msg, senderIP ); - break; - case LANMessage::MSG_GAME_ANNOUNCE: // Here someone is, and here's his game info! - DEBUG_LOG(("LANAPI::update - got a MSG_GAME_ANNOUNCE from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleGameAnnounce( msg, senderIP ); - break; - case LANMessage::MSG_LOBBY_ANNOUNCE: // Hey, I'm in the lobby! - DEBUG_LOG(("LANAPI::update - got a MSG_LOBBY_ANNOUNCE from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleLobbyAnnounce( msg, senderIP ); - break; - case LANMessage::MSG_REQUEST_GAME_INFO: - DEBUG_LOG(("LANAPI::update - got a MSG_REQUEST_GAME_INFO from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleRequestGameInfo( msg, senderIP ); - break; - - // Joining games - case LANMessage::MSG_REQUEST_JOIN: // Let me in! Let me in! - DEBUG_LOG(("LANAPI::update - got a MSG_REQUEST_JOIN from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleRequestJoin( msg, senderIP ); - break; - case LANMessage::MSG_JOIN_ACCEPT: // Okay, you can join. - DEBUG_LOG(("LANAPI::update - got a MSG_JOIN_ACCEPT from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleJoinAccept( msg, senderIP ); - break; - case LANMessage::MSG_JOIN_DENY: // Go away! We don't want any! - DEBUG_LOG(("LANAPI::update - got a MSG_JOIN_DENY from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleJoinDeny( msg, senderIP ); - break; - - // Leaving games, lobby - case LANMessage::MSG_REQUEST_GAME_LEAVE: // I'm outa here! - DEBUG_LOG(("LANAPI::update - got a MSG_REQUEST_GAME_LEAVE from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleRequestGameLeave( msg, senderIP ); - break; - case LANMessage::MSG_REQUEST_LOBBY_LEAVE: // I'm outa here! - DEBUG_LOG(("LANAPI::update - got a MSG_REQUEST_LOBBY_LEAVE from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleRequestLobbyLeave( msg, senderIP ); - break; - - // Game options, chat, etc - case LANMessage::MSG_SET_ACCEPT: // I'm cool with everything as is. - handleSetAccept( msg, senderIP ); - break; - case LANMessage::MSG_MAP_AVAILABILITY: // Map status - handleHasMap( msg, senderIP ); - break; - case LANMessage::MSG_CHAT: // Just spouting my mouth off. - handleChat( msg, senderIP ); - break; - case LANMessage::MSG_GAME_START: // Hold on; we're starting! - handleGameStart( msg, senderIP ); - break; - case LANMessage::MSG_GAME_START_TIMER: - handleGameStartTimer( msg, senderIP ); - break; - case LANMessage::MSG_GAME_OPTIONS: // Here's some info about the game. - DEBUG_LOG(("LANAPI::update - got a MSG_GAME_OPTIONS from %d.%d.%d.%d", PRINTF_IP_AS_4_INTS(senderIP))); - handleGameOptions( msg, senderIP ); - break; - case LANMessage::MSG_INACTIVE: // someone is telling us that we're inactive. - handleInActive( msg, senderIP ); - break; - - default: - DEBUG_LOG(("Unknown LAN message type %d", msg->messageType)); - } - - // Mark it as read - m_transport->m_inBuffer[i].length = 0; - } - } - if(LANbuttonPushed) - return; - // Send out periodic I'm Here messages - if (now > s_resendDelta + m_lastResendTime) - { - m_lastResendTime = now; - - if (m_inLobby) - { - RequestSetName(m_name); - } - else if (m_currentGame && !m_currentGame->isGameInProgress()) - { - if (AmIHost()) - { - RequestGameOptions( GenerateGameOptionsString(), true ); - RequestGameAnnounce( ); - } - else - { -#if TELL_COMPUTER_IDENTITY_IN_LAN_LOBBY - AsciiString text; - text.format("User=%s", m_userName.str()); - RequestGameOptions( text, true ); - text.format("Host=%s", m_hostName.str()); - RequestGameOptions( text, true ); -#endif - RequestGameOptions( "HELLO", false ); - } - } - else if (m_currentGame) - { - // game is in progress - RequestGameAnnounce will check if we should send it - RequestGameAnnounce(); - } - } - - Bool playerListChanged = false; - Bool gameListChanged = false; - - // Weed out people we haven't heard from in a while - LANPlayer *player = m_lobbyPlayers; - while (player) - { - if (player->getLastHeard() + s_resendDelta*2 < now) - { - // He's gone! - removePlayer(player); - LANPlayer *nextPlayer = player->getNext(); - delete player; - player = nextPlayer; - playerListChanged = true; - } - else - { - player = player->getNext(); - } - } - - // Weed out people we haven't heard from in a while - LANGameInfo *game = m_games; - while (game) - { - if (game != m_currentGame && game->getLastHeard() + s_resendDelta*2 < now) - { - // He's gone! - removeGame(game); - LANGameInfo *nextGame = game->getNext(); - delete game; - game = nextGame; - gameListChanged = true; - } - else - { - game = game->getNext(); - } - } - if ( m_currentGame && !m_currentGame->isGameInProgress() ) - { - if ( !AmIHost() && (m_currentGame->getLastHeard() + s_resendDelta*16 < now) ) - { - // We haven't heard from the host in a while. Bail. - // Actually, fake a host leaving message. :) - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_REQUEST_GAME_LEAVE; - wcslcpy(msg.name, m_currentGame->getPlayerName(0).str(), ARRAY_SIZE(msg.name)); - handleRequestGameLeave(&msg, m_currentGame->getIP(0)); - UnicodeString text; - text = TheGameText->fetch("LAN:HostNotResponding"); - OnChat(UnicodeString::TheEmptyString, m_localIP, text, LANCHAT_SYSTEM); - } - else if ( AmIHost() ) - { - // Check each player for timeouts - for (int p=1; pgetIP(p) && m_currentGame->getPlayerLastHeard(p) + s_resendDelta*8 < now) - { - LANMessage msg; - fillInLANMessage( &msg ); - UnicodeString theStr; - theStr.format(TheGameText->fetch("LAN:PlayerDropped"), m_currentGame->getPlayerName(p).str()); - msg.messageType = LANMessage::MSG_REQUEST_GAME_LEAVE; - wcslcpy(msg.name, m_currentGame->getPlayerName(p).str(), ARRAY_SIZE(msg.name)); - handleRequestGameLeave(&msg, m_currentGame->getIP(p)); - OnChat(UnicodeString::TheEmptyString, m_localIP, theStr, LANCHAT_SYSTEM); - } - } - } - } - - if (playerListChanged) - { - OnPlayerList(m_lobbyPlayers); - } - - if (gameListChanged) - { - OnGameList(m_games); - } - - // Time out old actions - if (m_pendingAction != ACT_NONE && now > m_expiration) - { - switch (m_pendingAction) - { - case ACT_JOIN: - OnGameJoin(RET_TIMEOUT, NULL); - m_pendingAction = ACT_NONE; - m_currentGame = NULL; - m_inLobby = true; - break; - case ACT_LEAVE: - OnPlayerLeave(m_name); - m_pendingAction = ACT_NONE; - m_currentGame = NULL; - m_inLobby = true; - break; - case ACT_JOINDIRECTCONNECT: - OnGameJoin(RET_TIMEOUT, NULL); - m_pendingAction = ACT_NONE; - m_currentGame = NULL; - m_inLobby = true; - break; - default: - m_pendingAction = ACT_NONE; - } - } - - // send out "game starting" messages - if ( m_gameStartTime && m_gameStartSeconds && m_gameStartTime <= now ) - { - // m_gameStartTime is when the next message goes out - // m_gameStartSeconds is how many seconds remain in the message - - RequestGameStartTimer( m_gameStartSeconds ); - } - else if (m_gameStartTime && m_gameStartTime <= now) - { -// DEBUG_LOG(("m_gameStartTime=%d, now=%d, m_gameStartSeconds=%d", m_gameStartTime, now, m_gameStartSeconds)); - ResetGameStartTimer(); - RequestGameStart(); - } - - // Check for an MOTD every few seconds - static UnsignedInt lastMOTDCheck = 0; - static const UnsignedInt motdInterval = 30000; - if (now > lastMOTDCheck + motdInterval) - { - checkMOTD(); - lastMOTDCheck = now; - } -} - -// Request functions generate network traffic -void LANAPI::RequestLocations( void ) -{ - LANMessage msg; - msg.messageType = LANMessage::MSG_REQUEST_LOCATIONS; - fillInLANMessage( &msg ); - sendMessage(&msg); -} - -void LANAPI::RequestGameJoin( LANGameInfo *game, UnsignedInt ip /* = 0 */ ) -{ - if ((m_pendingAction != ACT_NONE) && (m_pendingAction != ACT_JOINDIRECTCONNECT)) - { - OnGameJoin( RET_BUSY, NULL ); - return; - } - - if (!game) - { - OnGameJoin( RET_GAME_GONE, NULL ); - return; - } - - LANMessage msg; - msg.messageType = LANMessage::MSG_REQUEST_JOIN; - fillInLANMessage( &msg ); - msg.GameToJoin.gameIP = game->getSlot(0)->getIP(); - msg.GameToJoin.exeCRC = TheGlobalData->m_exeCRC; - msg.GameToJoin.iniCRC = TheGlobalData->m_iniCRC; - - AsciiString s = ""; - GetStringFromRegistry("\\ergc", "", s); - strlcpy(msg.GameToJoin.serial, s.str(), ARRAY_SIZE(msg.GameToJoin.serial)); - - sendMessage(&msg, ip); - - m_pendingAction = ACT_JOIN; - m_expiration = timeGetTime() + m_actionTimeout; -} - -void LANAPI::RequestGameJoinDirectConnect(UnsignedInt ipaddress) -{ - if (m_pendingAction != ACT_NONE) - { - OnGameJoin( RET_BUSY, NULL ); - return; - } - - if (ipaddress == 0) - { - OnGameJoin( RET_GAME_GONE, NULL ); - return; - } - - m_directConnectRemoteIP = ipaddress; - - LANMessage msg; - msg.messageType = LANMessage::MSG_REQUEST_GAME_INFO; - fillInLANMessage(&msg); - msg.PlayerInfo.ip = GetLocalIP(); - wcslcpy(msg.PlayerInfo.playerName, m_name.str(), ARRAY_SIZE(msg.PlayerInfo.playerName)); - - sendMessage(&msg, ipaddress); - - m_pendingAction = ACT_JOINDIRECTCONNECT; - m_expiration = timeGetTime() + m_actionTimeout; -} - -void LANAPI::RequestGameLeave( void ) -{ - LANMessage msg; - msg.messageType = LANMessage::MSG_REQUEST_GAME_LEAVE; - fillInLANMessage( &msg ); - wcslcpy(msg.PlayerInfo.playerName, m_name.str(), ARRAY_SIZE(msg.PlayerInfo.playerName)); - sendMessage(&msg); - m_transport->update(); // Send immediately, before OnPlayerLeave below resets everything. - - if (m_currentGame && m_currentGame->getIP(0) == m_localIP) - { - // Exit out immediately if we're hosting - OnPlayerLeave(m_name); - removeGame(m_currentGame); - m_currentGame = NULL; - m_inLobby = true; - } - else - { - m_pendingAction = ACT_LEAVE; - m_expiration = timeGetTime() + m_actionTimeout; - } -} - -void LANAPI::RequestGameAnnounce( void ) -{ - // In game - are we a game host? - if (m_currentGame && !(m_currentGame->getIsDirectConnect())) - { - if (m_currentGame->getIP(0) == m_localIP || (m_currentGame->isGameInProgress() && TheNetwork && TheNetwork->isPacketRouter())) // if we're in game we should reply if we're the packet router - { - LANMessage reply; - fillInLANMessage( &reply ); - reply.messageType = LANMessage::MSG_GAME_ANNOUNCE; - - AsciiString gameOpts = GameInfoToAsciiString(m_currentGame); - strlcpy(reply.GameInfo.options,gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); - wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); - reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); - reply.GameInfo.isDirectConnect = m_currentGame->getIsDirectConnect(); - - sendMessage(&reply); - } - } -} - -void LANAPI::RequestAccept( void ) -{ - if (m_inLobby || !m_currentGame) - return; - - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_SET_ACCEPT; - msg.Accept.isAccepted = true; - wcslcpy(msg.Accept.gameName, m_currentGame->getName().str(), ARRAY_SIZE(msg.Accept.gameName)); - sendMessage(&msg); -} - -void LANAPI::RequestHasMap( void ) -{ - if (m_inLobby || !m_currentGame) - return; - - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_MAP_AVAILABILITY; - msg.MapStatus.hasMap = m_currentGame->getSlot(m_currentGame->getLocalSlotNum())->hasMap(); - wcslcpy(msg.MapStatus.gameName, m_currentGame->getName().str(), ARRAY_SIZE(msg.MapStatus.gameName)); - CRC mapNameCRC; -//mapNameCRC.computeCRC(m_currentGame->getMap().str(), m_currentGame->getMap().getLength()); - AsciiString portableMapName = TheGameState->realMapPathToPortableMapPath(m_currentGame->getMap()); - mapNameCRC.computeCRC(portableMapName.str(), portableMapName.getLength()); - msg.MapStatus.mapCRC = mapNameCRC.get(); - sendMessage(&msg); - - if (!msg.MapStatus.hasMap) - { - UnicodeString text; - UnicodeString mapDisplayName; - const MapMetaData *mapData = TheMapCache->findMap( m_currentGame->getMap() ); - Bool willTransfer = TRUE; - if (mapData) - { - mapDisplayName.format(L"%ls", mapData->m_displayName.str()); - if (mapData->m_isOfficial) - willTransfer = FALSE; - } - else - { - mapDisplayName.format(L"%hs", TheGameState->getMapLeafName(m_currentGame->getMap()).str()); - willTransfer = WouldMapTransfer(m_currentGame->getMap()); - } - if (willTransfer) - text.format(TheGameText->fetch("GUI:LocalPlayerNoMapWillTransfer"), mapDisplayName.str()); - else - text.format(TheGameText->fetch("GUI:LocalPlayerNoMap"), mapDisplayName.str()); - OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM); - } -} - -void LANAPI::RequestChat( UnicodeString message, ChatType format ) -{ - LANMessage msg; - fillInLANMessage( &msg ); - wcslcpy(msg.Chat.gameName, (m_currentGame) ? m_currentGame->getName().str() : L"", ARRAY_SIZE(msg.Chat.gameName)); - msg.messageType = LANMessage::MSG_CHAT; - msg.Chat.chatType = format; - wcslcpy(msg.Chat.message, message.str(), ARRAY_SIZE(msg.Chat.message)); - sendMessage(&msg); - - OnChat(m_name, m_localIP, message, format); -} - -void LANAPI::RequestGameStart( void ) -{ - if (m_inLobby || !m_currentGame || m_currentGame->getIP(0) != m_localIP) - return; - - LANMessage msg; - msg.messageType = LANMessage::MSG_GAME_START; - fillInLANMessage( &msg ); - sendMessage(&msg); - m_transport->update(); // force a send - - OnGameStart(); -} - -void LANAPI::ResetGameStartTimer( void ) -{ - m_gameStartTime = 0; - m_gameStartSeconds = 0; -} - -void LANAPI::RequestGameStartTimer( Int seconds ) -{ - if (m_inLobby || !m_currentGame || m_currentGame->getIP(0) != m_localIP) - return; - - UnsignedInt now = timeGetTime(); - m_gameStartTime = now + 1000; - m_gameStartSeconds = (seconds) ? seconds - 1 : 0; - - LANMessage msg; - msg.messageType = LANMessage::MSG_GAME_START_TIMER; - msg.StartTimer.seconds = seconds; - fillInLANMessage( &msg ); - sendMessage(&msg); - m_transport->update(); // force a send - - OnGameStartTimer(seconds); -} - -void LANAPI::RequestGameOptions( AsciiString gameOptions, Bool isPublic, UnsignedInt ip /* = 0 */ ) -{ - DEBUG_ASSERTCRASH(gameOptions.getLength() < m_lanMaxOptionsLength, ("Game options string is too long!")); - - if (!m_currentGame) - return; - - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_GAME_OPTIONS; - strlcpy(msg.GameOptions.options, gameOptions.str(), ARRAY_SIZE(msg.GameOptions.options)); - sendMessage(&msg, ip); - - m_lastGameopt = gameOptions; - - int player; - for (player = 0; playergetIP(player) == m_localIP) - { - OnGameOptions(m_localIP, player, AsciiString(msg.GameOptions.options)); - break; - } - } - - // We can request game options (side, color, etc) while we don't have a slot yet. Of course, we don't need to - // call OnGameOptions for those, so it's okay to silently fail. - //DEBUG_ASSERTCRASH(player != MAX_SLOTS, ("Requested game options, but we're not in slot list!"); -} - -void LANAPI::RequestGameCreate( UnicodeString gameName, Bool isDirectConnect ) -{ - // No games of the same name should exist... Ignore that for now. - /// @todo: make sure LAN games with identical names don't crash things like in RA2. - - if ((!m_inLobby || m_currentGame) && !isDirectConnect) - { - DEBUG_ASSERTCRASH(m_inLobby && m_currentGame, ("Can't create a game while in one!")); - OnGameCreate(LANAPIInterface::RET_BUSY); - return; - } - - if (m_pendingAction != ACT_NONE) - { - OnGameCreate(LANAPIInterface::RET_BUSY); - return; - } - - // Create the local game object - m_inLobby = false; - LANGameInfo *myGame = NEW LANGameInfo; - - myGame->setSeed(GetTickCount()); - -// myGame->setInProgress(false); - myGame->enterGame(); - UnicodeString s; - s.format(L"%8.8X%8.8X", m_localIP, myGame->getSeed()); - if (gameName.isEmpty()) - s.concat(m_name); - else - s.concat(gameName); - - s.truncateTo(g_lanGameNameLength); - - DEBUG_LOG(("Setting local game name to '%ls'", s.str())); - - myGame->setName(s); - - LANGameSlot newSlot; - newSlot.setState(SLOT_PLAYER, m_name); - newSlot.setIP(m_localIP); - newSlot.setPort(NETWORK_BASE_PORT_NUMBER); // LAN game, everyone has a unique IP, so it's ok to use the same port. - newSlot.setLastHeard(0); - newSlot.setLogin(m_userName); - newSlot.setHost(m_hostName); - - myGame->setSlot(0,newSlot); - myGame->setNext(NULL); - LANPreferences pref; - - AsciiString mapName = pref.getPreferredMap(); - - myGame->setMap(mapName); - myGame->setIsDirectConnect(isDirectConnect); - - myGame->setLastHeard(timeGetTime()); - m_currentGame = myGame; - -/// @todo: Need to initialize the players elsewere. -/* for (int player = 1; player < MAX_SLOTS; ++player) - { - myGame->setPlayerName(player, UnicodeString(L"")); - myGame->setIP(player, 0); - myGame->setAccepted(player, false); - }*/ - - // Add the game to the local game list - addGame(myGame); - - // Send an announcement - //RequestSlotList(); -/* - LANMessage msg; - wcslcpy(msg.name, m_name.str(), ARRAY_SIZE(msg.name)); - wcscpy(msg.GameInfo.gameName, myGame->getName().str()); - for (player=0; playergetPlayerName(player).str()); - msg.GameInfo.ip[player] = myGame->getIP(player); - msg.GameInfo.playerAccepted[player] = myGame->getAccepted(player); - } - msg.messageType = LANMessage::MSG_GAME_ANNOUNCE; -*/ - OnGameCreate(LANAPIInterface::RET_OK); -} - - -/*static const char slotListID = 'S'; -static const char gameOptionsID = 'G'; -static const char acceptID = 'A'; -static const char wannaStartID = 'W'; - -AsciiString LANAPI::createSlotString( void ) -{ - AsciiString slotList; - slotList.concat(slotListID); - for (int i=0; igetLANSlot(i); - AsciiString str; - if (slot->isHuman()) - { - str = "H"; - LANPlayer *user = slot->getUser(); - DEBUG_ASSERTCRASH(user, ("Human player has no User*!")); - AsciiString name; - name.translate(user->getName()); - str.concat(name); - str.concat(','); - } - else if (slot->isAI()) - { - if (slot->getState() == SLOT_EASY_AI) - str = "CE,"; - if (slot->getState() == SLOT_MED_AI) - str = "CM,"; - else - str = "CB,"; - } - else if (slot->getState() == SLOT_OPEN) - { - str = "O,"; - } - else if (slot->getState() == SLOT_CLOSED) - { - str = "X,"; - } - else - { - DEBUG_ASSERTCRASH(false, ("Bad slot type")); - str = "X,"; - } - - slotList.concat(str); - } - return slotList; -} -*/ -/* -void LANAPI::RequestSlotList( void ) -{ - - LANMessage reply; - reply.messageType = LANMessage::MSG_GAME_ANNOUNCE; - wcslcpy(reply.name, m_name.str(), ARRAY_SIZE(reply.name)); - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - wcslcpy(reply.GameInfo.name[player], m_currentGame->getPlayerName(player).str(), ARRAY_SIZE(reply.GameInfo.name[player])); - reply.GameInfo.ip[player] = m_currentGame->getIP(player); - reply.GameInfo.playerAccepted[player] = m_currentGame->getSlot(player)->isAccepted(); - } - wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); - reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); - - sendMessage(&reply); - - OnSlotList(LANAPIInterface::RET_OK, m_currentGame); -} -*/ -void LANAPI::RequestSetName( UnicodeString newName ) -{ - newName.trim(); - if (m_pendingAction != ACT_NONE) - { - // Can't change name while joining games - OnNameChange(m_localIP, newName); - return; - } - - // Set up timer - m_lastResendTime = timeGetTime(); - - if (m_inLobby && m_pendingAction == ACT_NONE) - { - m_name = newName; - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_LOBBY_ANNOUNCE; - sendMessage(&msg); - - // Update the interface - LANPlayer *player = LookupPlayer(m_localIP); - if (!player) - { - player = NEW LANPlayer; - player->setIP(m_localIP); - } - else - { - removePlayer(player); - } - player->setName(m_name); - player->setHost(m_hostName); - player->setLogin(m_userName); - player->setLastHeard(timeGetTime()); - - addPlayer(player); - - OnNameChange(player->getIP(), player->getName()); - } -} - -void LANAPI::fillInLANMessage( LANMessage *msg ) -{ - if (!msg) - return; - - wcslcpy(msg->name, m_name.str(), ARRAY_SIZE(msg->name)); - strlcpy(msg->userName, m_userName.str(), ARRAY_SIZE(msg->userName)); - strlcpy(msg->hostName, m_hostName.str(), ARRAY_SIZE(msg->hostName)); -} - -void LANAPI::RequestLobbyLeave( Bool forced ) -{ - LANMessage msg; - msg.messageType = LANMessage::MSG_REQUEST_LOBBY_LEAVE; - fillInLANMessage( &msg ); - sendMessage(&msg); - - if (forced) - m_transport->update(); -} - -// Misc utility functions -LANGameInfo * LANAPI::LookupGame( UnicodeString gameName ) -{ - LANGameInfo *theGame = m_games; - - while (theGame && theGame->getName() != gameName) - { - theGame = theGame->getNext(); - } - - return theGame; // NULL means we didn't find anything. -} - -LANGameInfo * LANAPI::LookupGameByListOffset( Int offset ) -{ - LANGameInfo *theGame = m_games; - - if (offset < 0) - return NULL; - - while (offset-- && theGame) - { - theGame = theGame->getNext(); - } - - return theGame; // NULL means we didn't find anything. -} - -void LANAPI::removeGame( LANGameInfo *game ) -{ - LANGameInfo *g = m_games; - if (!game) - { - return; - } - else if (m_games == game) - { - m_games = m_games->getNext(); - } - else - { - while (g->getNext() && g->getNext() != game) - { - g = g->getNext(); - } - if (g->getNext() == game) - { - g->setNext(game->getNext()); - } - else - { - // Odd. We went the whole way without finding it in the list. - DEBUG_ASSERTCRASH(false, ("LANGameInfo wasn't in the list")); - } - } -} - -LANPlayer * LANAPI::LookupPlayer( UnsignedInt playerIP ) -{ - LANPlayer *thePlayer = m_lobbyPlayers; - - while (thePlayer && thePlayer->getIP() != playerIP) - { - thePlayer = thePlayer->getNext(); - } - - return thePlayer; // NULL means we didn't find anything. -} - -void LANAPI::removePlayer( LANPlayer *player ) -{ - LANPlayer *p = m_lobbyPlayers; - if (!player) - { - return; - } - else if (m_lobbyPlayers == player) - { - m_lobbyPlayers = m_lobbyPlayers->getNext(); - } - else - { - while (p->getNext() && p->getNext() != player) - { - p = p->getNext(); - } - if (p->getNext() == player) - { - p->setNext(player->getNext()); - } - else - { - // Odd. We went the whole way without finding it in the list. - DEBUG_ASSERTCRASH(false, ("LANPlayer wasn't in the list")); - } - } -} - -void LANAPI::addGame( LANGameInfo *game ) -{ - if (!m_games) - { - m_games = game; - game->setNext(NULL); - return; - } - else - { - if (game->getName().compareNoCase(m_games->getName()) < 0) - { - game->setNext(m_games); - m_games = game; - return; - } - else - { - LANGameInfo *g = m_games; - while (g->getNext() && g->getNext()->getName().compareNoCase(game->getName()) > 0) - { - g = g->getNext(); - } - game->setNext(g->getNext()); - g->setNext(game); - return; - } - } -} - -void LANAPI::addPlayer( LANPlayer *player ) -{ - if (!m_lobbyPlayers) - { - m_lobbyPlayers = player; - player->setNext(NULL); - return; - } - else - { - if (player->getName().compareNoCase(m_lobbyPlayers->getName()) < 0) - { - player->setNext(m_lobbyPlayers); - m_lobbyPlayers = player; - return; - } - else - { - LANPlayer *p = m_lobbyPlayers; - while (p->getNext() && p->getNext()->getName().compareNoCase(player->getName()) > 0) - { - p = p->getNext(); - } - player->setNext(p->getNext()); - p->setNext(player); - return; - } - } -} - -Bool LANAPI::SetLocalIP( UnsignedInt localIP ) -{ - Bool retval = TRUE; - m_localIP = localIP; - - m_transport->reset(); - retval = m_transport->init(m_localIP, lobbyPort); - m_transport->allowBroadcasts(true); - - return retval; -} - -void LANAPI::SetLocalIP( AsciiString localIP ) -{ - UnsignedInt resolvedIP = ResolveIP(localIP); - SetLocalIP(resolvedIP); -} - -Bool LANAPI::AmIHost( void ) -{ - return m_currentGame && m_currentGame->getIP(0) == m_localIP; -} - -void LANAPI::setIsActive(Bool isActive) { - DEBUG_LOG(("LANAPI::setIsActive - entering")); - if (isActive != m_isActive) { - DEBUG_LOG(("LANAPI::setIsActive - m_isActive changed to %s", isActive ? "TRUE" : "FALSE")); - if (isActive == FALSE) { - if ((m_inLobby == FALSE) && (m_currentGame != NULL)) { - LANMessage msg; - fillInLANMessage( &msg ); - msg.messageType = LANMessage::MSG_INACTIVE; - sendMessage(&msg); - DEBUG_LOG(("LANAPI::setIsActive - sent an IsActive message")); - } - } - } - m_isActive = isActive; -} diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp deleted file mode 100644 index 1c61c6c1443..00000000000 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ /dev/null @@ -1,737 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////// -// FILE: LANAPICallbacks.cpp -// Author: Chris Huybregts, October 2001 -// Description: LAN API Callbacks -/////////////////////////////////////////////////////////////////////////////////////// -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "strtok_r.h" -#include "Common/GameEngine.h" -#include "Common/GlobalData.h" -#include "Common/MessageStream.h" -#include "Common/MultiplayerSettings.h" -#include "Common/PlayerTemplate.h" -#include "Common/QuotedPrintable.h" -#include "Common/RandomValue.h" -#include "Common/UserPreferences.h" -#include "GameClient/GameText.h" -#include "GameClient/LanguageFilter.h" -#include "GameClient/MapUtil.h" -#include "GameClient/MessageBox.h" -#include "GameLogic/GameLogic.h" -#include "GameNetwork/FileTransfer.h" -#include "GameNetwork/LANAPICallbacks.h" -#include "GameNetwork/networkutil.h" - -LANAPI *TheLAN = NULL; -extern Bool LANbuttonPushed; - - -//Colors used for the chat dialogs -const Color playerColor = GameMakeColor(255,255,255,255); -const Color gameColor = GameMakeColor(255,255,255,255); -const Color gameInProgressColor = GameMakeColor(128,128,128,255); -const Color chatNormalColor = GameMakeColor(50,215,230,255); -const Color chatActionColor = GameMakeColor(255,0,255,255); -const Color chatLocalNormalColor = GameMakeColor(255,128,0,255); -const Color chatLocalActionColor = GameMakeColor(128,255,255,255); -const Color chatSystemColor = GameMakeColor(255,255,255,255); -const Color acceptTrueColor = GameMakeColor(0,255,0,255); -const Color acceptFalseColor = GameMakeColor(255,0,0,255); - - -UnicodeString LANAPIInterface::getErrorStringFromReturnType( ReturnType ret ) -{ - switch (ret) - { - case RET_OK: - return TheGameText->fetch("LAN:OK"); - case RET_TIMEOUT: - return TheGameText->fetch("LAN:ErrorTimeout"); - case RET_GAME_FULL: - return TheGameText->fetch("LAN:ErrorGameFull"); - case RET_DUPLICATE_NAME: - return TheGameText->fetch("LAN:ErrorDuplicateName"); - case RET_CRC_MISMATCH: - return TheGameText->fetch("LAN:ErrorCRCMismatch"); - case RET_GAME_STARTED: - return TheGameText->fetch("LAN:ErrorGameStarted"); - case RET_GAME_EXISTS: - return TheGameText->fetch("LAN:ErrorGameExists"); - case RET_GAME_GONE: - return TheGameText->fetch("LAN:ErrorGameGone"); - case RET_BUSY: - return TheGameText->fetch("LAN:ErrorBusy"); - case RET_SERIAL_DUPE: - return TheGameText->fetch("WOL:ChatErrorSerialDup"); - default: - return TheGameText->fetch("LAN:ErrorUnknown"); - } -} - -// On functions are (generally) the result of network traffic - -void LANAPI::OnAccept( UnsignedInt playerIP, Bool status ) -{ - if( AmIHost() ) - { - Int i = 0; - for (; i < MAX_SLOTS; i++) - { - if (m_currentGame->getIP(i) == playerIP) - { - if(status) - m_currentGame->getLANSlot(i)->setAccept(); - else - m_currentGame->getLANSlot(i)->unAccept(); - break; - } - } - if (i != MAX_SLOTS ) - { - RequestGameOptions( GenerateGameOptionsString(), false ); - lanUpdateSlotList(); - } - } - else - { - //i'm not the host but if the accept came from the host... - if( m_currentGame->getIP(0) == playerIP ) - { - UnicodeString text; - text = TheGameText->fetch("GUI:HostWantsToStart"); - OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM); - } - } -} - -void LANAPI::OnHasMap( UnsignedInt playerIP, Bool status ) -{ - if( AmIHost() ) - { - Int i = 0; - for (; i < MAX_SLOTS; i++) - { - if (m_currentGame->getIP(i) == playerIP) - { - m_currentGame->getLANSlot(i)->setMapAvailability( status ); - break; - } - } - if (i != MAX_SLOTS ) - { - UnicodeString mapDisplayName; - const MapMetaData *mapData = TheMapCache->findMap( m_currentGame->getMap() ); - Bool willTransfer = TRUE; - if (mapData) - { - mapDisplayName.format(L"%ls", mapData->m_displayName.str()); - if (mapData->m_isOfficial) - willTransfer = FALSE; - } - else - { - mapDisplayName.format(L"%hs", m_currentGame->getMap().str()); - willTransfer = WouldMapTransfer(m_currentGame->getMap()); - } - if (!status) - { - UnicodeString text; - if (willTransfer) - text.format(TheGameText->fetch("GUI:PlayerNoMapWillTransfer"), m_currentGame->getLANSlot(i)->getName().str(), mapDisplayName.str()); - else - text.format(TheGameText->fetch("GUI:PlayerNoMap"), m_currentGame->getLANSlot(i)->getName().str(), mapDisplayName.str()); - OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM); - } - lanUpdateSlotList(); - } - } -} - -void LANAPI::OnGameStartTimer( Int seconds ) -{ - UnicodeString text; - if (seconds == 1) - text.format(TheGameText->fetch("LAN:GameStartTimerSingular"), seconds); - else - text.format(TheGameText->fetch("LAN:GameStartTimerPlural"), seconds); - OnChat(UnicodeString(L"SYSTEM"), m_localIP, text, LANCHAT_SYSTEM); -} - -void LANAPI::OnGameStart( void ) -{ - //DEBUG_LOG(("Map is '%s', preview is '%s'", m_currentGame->getMap().str(), GetPreviewFromMap(m_currentGame->getMap()).str())); - //DEBUG_LOG(("Map is '%s', INI is '%s'", m_currentGame->getMap().str(), GetINIFromMap(m_currentGame->getMap()).str())); - - if (m_currentGame) - { - LANPreferences pref; - AsciiString option; - option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getPlayerTemplate()); - pref["PlayerTemplate"] = option; - option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getColor()); - pref["Color"] = option; - if (m_currentGame->amIHost()) - { - pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap()); - pref.setSuperweaponRestricted( m_currentGame->getSuperweaponRestriction() > 0 ); - pref.setStartingCash( m_currentGame->getStartingCash() ); - } - pref.write(); - - m_isInLANMenu = FALSE; - - //m_currentGame->startGame(0); - - // Set up the game network - DEBUG_ASSERTCRASH(TheNetwork == NULL, ("For some reason TheNetwork isn't NULL at the start of this game. Better look into that.")); - - delete TheNetwork; - TheNetwork = NULL; - - // Time to initialize TheNetwork for this game. - TheNetwork = NetworkInterface::createNetwork(); - TheNetwork->init(); - TheNetwork->setLocalAddress(m_localIP, 8088); - TheNetwork->initTransport(); - - TheNetwork->parseUserList(m_currentGame); - - if (TheGameLogic->isInGame()) - TheGameLogic->clearGameData(); - - Bool filesOk = DoAnyMapTransfers(m_currentGame); - - // see if we really have the map. if not, back out. - TheMapCache->updateCache(); - if (!filesOk || TheMapCache->findMap(m_currentGame->getMap()) == NULL) - { - DEBUG_LOG(("After transfer, we didn't really have the map. Bailing...")); - OnPlayerLeave(m_name); - removeGame(m_currentGame); - m_currentGame = NULL; - m_inLobby = TRUE; - - delete TheNetwork; - TheNetwork = NULL; - - OnChat(UnicodeString::TheEmptyString, 0, TheGameText->fetch("GUI:CouldNotTransferMap"), LANCHAT_SYSTEM); - return; - } - - m_currentGame->startGame(0); - - // shutdown the top, but do not pop it off the stack - //TheShell->hideShell(); - // setup the Global Data with the Map and Seed - TheWritableGlobalData->m_pendingFile = m_currentGame->getMap(); - - // send a message to the logic for a new game - GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME ); - msg->appendIntegerArgument(GAME_LAN); - - TheWritableGlobalData->m_useFpsLimit = false; - - // Set the random seed - InitGameLogicRandom( m_currentGame->getSeed() ); - DEBUG_LOG(("InitGameLogicRandom( %d )", m_currentGame->getSeed())); - } -} - -void LANAPI::OnGameOptions( UnsignedInt playerIP, Int playerSlot, AsciiString options ) -{ - if (!m_currentGame) - return; - - if (m_currentGame->getIP(playerSlot) != playerIP) - return; // He's not in our game?!? - - - if (m_currentGame->isGameInProgress()) - return; // we don't want to process any game options while in game. - - if (playerSlot == 0 && !m_currentGame->amIHost()) - { - m_currentGame->setLastHeard(timeGetTime()); - AsciiString oldOptions = GameInfoToAsciiString(m_currentGame); // save these off for if we get booted - if(ParseGameOptionsString(m_currentGame,options)) - { - lanUpdateSlotList(); - updateGameOptions(); - } - Bool booted = true; - for(Int player = 1; player< MAX_SLOTS; player++) - { - if(m_currentGame->getIP(player) == m_localIP) - { - booted = false; - break; - } - } - if(booted) - { - // restore the options with us in so we can save prefs - ParseGameOptionsString(m_currentGame, oldOptions); - OnPlayerLeave(m_name); - } - - } - else - { - // Check for user/host updates - { - AsciiString key; - AsciiString munkee = options; - munkee.nextToken(&key, "="); - //DEBUG_LOG(("GameOpt request: key=%s, val=%s from player %d", key.str(), munkee.str(), playerSlot)); - - LANGameSlot *slot = m_currentGame->getLANSlot(playerSlot); - if (!slot) - return; - - if (key == "User") - { - slot->setLogin(munkee.str()+1); - return; - } - else if (key == "Host") - { - slot->setHost(munkee.str()+1); - return; - } - } - - // Parse player requests (side, color, etc) - if( AmIHost() && m_localIP != playerIP) - { - if (options.compare("HELLO") == 0) - { - m_currentGame->setPlayerLastHeard(playerSlot, timeGetTime()); - } - else - { - m_currentGame->setPlayerLastHeard(playerSlot, timeGetTime()); - Bool change = false; - Bool shouldUnaccept = false; - AsciiString key; - options.nextToken(&key, "="); - Int val = atoi(options.str()+1); - DEBUG_LOG(("GameOpt request: key=%s, val=%s from player %d", key.str(), options.str(), playerSlot)); - - LANGameSlot *slot = m_currentGame->getLANSlot(playerSlot); - if (!slot) - return; - - if (key == "Color") - { - if (val >= -1 && val < TheMultiplayerSettings->getNumColors() && val != slot->getColor() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER) - { - Bool colorAvailable = TRUE; - if(val != -1 ) - { - for(Int i=0; i getLANSlot(i); - if(val == checkSlot->getColor() && slot != checkSlot) - { - colorAvailable = FALSE; - break; - } - } - } - if(colorAvailable) - slot->setColor(val); - change = true; - } - else - { - DEBUG_LOG(("Rejecting invalid color %d", val)); - } - } - else if (key == "PlayerTemplate") - { - if (val >= PLAYERTEMPLATE_MIN && val < ThePlayerTemplateStore->getPlayerTemplateCount() && val != slot->getPlayerTemplate()) - { - slot->setPlayerTemplate(val); - if (val == PLAYERTEMPLATE_OBSERVER) - { - slot->setColor(-1); - slot->setStartPos(-1); - slot->setTeamNumber(-1); - } - change = true; - shouldUnaccept = true; - } - else - { - DEBUG_LOG(("Rejecting invalid PlayerTemplate %d", val)); - } - } - else if (key == "StartPos" && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER) - { - - if (val >= -1 && val < MAX_SLOTS && val != slot->getStartPos()) - { - Bool startPosAvailable = TRUE; - if(val != -1) - for(Int i=0; i getLANSlot(i); - if(val == checkSlot->getStartPos() && slot != checkSlot) - { - startPosAvailable = FALSE; - break; - } - } - if(startPosAvailable) - slot->setStartPos(val); - change = true; - shouldUnaccept = true; - } - else - { - DEBUG_LOG(("Rejecting invalid startPos %d", val)); - } - } - else if (key == "Team") - { - if (val >= -1 && val < MAX_SLOTS/2 && val != slot->getTeamNumber() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER) - { - slot->setTeamNumber(val); - change = true; - shouldUnaccept = true; - } - else - { - DEBUG_LOG(("Rejecting invalid team %d", val)); - } - } - else if (key == "NAT") - { - if ((val >= FirewallHelperClass::FIREWALL_TYPE_SIMPLE) && - (val <= FirewallHelperClass::FIREWALL_TYPE_DESTINATION_PORT_DELTA)) - { - slot->setNATBehavior((FirewallHelperClass::FirewallBehaviorType)val); - DEBUG_LOG(("NAT behavior set to %d for player %d", val, playerSlot)); - change = true; - } - else - { - DEBUG_LOG(("Rejecting invalid NAT behavior %d", (Int)val)); - } - } - - if (change) - { - if (shouldUnaccept) - m_currentGame->resetAccepted(); - RequestGameOptions(GenerateGameOptionsString(), true); - lanUpdateSlotList(); - DEBUG_LOG(("Slot value is color=%d, PlayerTemplate=%d, startPos=%d, team=%d", - slot->getColor(), slot->getPlayerTemplate(), slot->getStartPos(), slot->getTeamNumber())); - DEBUG_LOG(("Slot list updated to %s", GenerateGameOptionsString().str())); - } - } - } - } -} - - -/* -void LANAPI::OnSlotList( ReturnType ret, LANGameInfo *theGame ) -{ - if (!theGame || theGame != m_currentGame) - return; - - Bool foundMe = false; - for (int player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame->getIP(player) == m_localIP) - { - foundMe = true; - break; - } - } - if (!foundMe) - { - // I've been kicked - back to the lobby for me! - // We're counting on the fact that OnPlayerLeave winds up calling reset on TheLAN. - OnPlayerLeave(m_name); - return; - } - - lanUpdateSlotList(); -} -*/ -void LANAPI::OnPlayerJoin( Int slot, UnicodeString playerName ) -{ - if (m_currentGame && m_currentGame->getIP(0) == m_localIP) - { - // Someone New Joined.. lets reset the accepts - m_currentGame->resetAccepted(); - - // Send out the game options - RequestGameOptions(GenerateGameOptionsString(), true); - } - - lanUpdateSlotList(); -} - -void LANAPI::OnGameJoin( ReturnType ret, LANGameInfo *theGame ) -{ - if (ret == RET_OK) - { - LANbuttonPushed = true; - TheShell->push( AsciiString("Menus/LanGameOptionsMenu.wnd") ); - //lanUpdateSlotList(); - - LANPreferences pref; - AsciiString options; - options.format("PlayerTemplate=%d", pref.getPreferredFaction()); - RequestGameOptions(options, true); - options.format("Color=%d", pref.getPreferredColor()); - RequestGameOptions(options, true); - options.format("User=%s", m_userName.str()); - RequestGameOptions( options, true ); - options.format("Host=%s", m_hostName.str()); - RequestGameOptions( options, true ); - options.format("NAT=%d", FirewallHelperClass::FIREWALL_TYPE_SIMPLE); // BGC: This is a LAN game, so there is no firewall. - RequestGameOptions( options, true ); - } - else if (ret != RET_BUSY) - { - /// @todo: re-enable lobby controls? Error msgs? - UnicodeString title, body; - title = TheGameText->fetch("LAN:JoinFailed"); - body = getErrorStringFromReturnType(ret); - MessageBoxOk(title, body, NULL); - } -} - -void LANAPI::OnHostLeave( void ) -{ - DEBUG_ASSERTCRASH(!m_inLobby && m_currentGame, ("Game info is gone!")); - if (m_inLobby || !m_currentGame) - return; - LANbuttonPushed = true; - DEBUG_LOG(("Host left - popping to lobby")); - TheShell->pop(); -} - -void LANAPI::OnPlayerLeave( UnicodeString player ) -{ - DEBUG_ASSERTCRASH(!m_inLobby && m_currentGame, ("Game info is gone!")); - if (m_inLobby || !m_currentGame || m_currentGame->isGameInProgress()) - return; - - if (m_name.compare(player) == 0) - { - // We're leaving. Save options and Pop the shell up a screen. - //DEBUG_ASSERTCRASH(false, ("Slot is %d", m_currentGame->getLocalSlotNum())); - if (m_currentGame && m_currentGame->isInGame() && m_currentGame->getLocalSlotNum() >= 0) - { - LANPreferences pref; - AsciiString option; - option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getPlayerTemplate()); - pref["PlayerTemplate"] = option; - option.format("%d", m_currentGame->getLANSlot( m_currentGame->getLocalSlotNum() )->getColor()); - pref["Color"] = option; - if (m_currentGame->amIHost()) - pref["Map"] = AsciiStringToQuotedPrintable(m_currentGame->getMap()); - pref.write(); - } - LANbuttonPushed = true; - DEBUG_LOG(("OnPlayerLeave says we're leaving! pop away!")); - TheShell->pop(); - } - else - { - if (m_currentGame && m_currentGame->getIP(0) == m_localIP) - { - // Force a new slotlist send - m_lastResendTime = 0; - - lanUpdateSlotList(); - RequestGameOptions( GenerateGameOptionsString(), true ); - - } - } -} - -void LANAPI::OnGameList( LANGameInfo *gameList ) -{ - - if (m_inLobby) - { - LANDisplayGameList(listboxGames, gameList); - } -} - -void LANAPI::OnGameCreate( ReturnType ret ) -{ - if (ret == RET_OK) - { - - LANbuttonPushed = true; - TheShell->push( AsciiString("Menus/LanGameOptionsMenu.wnd") ); - - RequestLobbyLeave( false ); - //RequestGameAnnounce( ); // can't do this here, since we don't have a map set - } - else - { - if(m_inLobby) - { - switch( ret ) - { - case RET_GAME_EXISTS: - GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorGameExists"), chatSystemColor, -1, -1); - break; - case RET_BUSY: - GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorBusy"), chatSystemColor, -1, -1); - break; - default: - GadgetListBoxAddEntryText(listboxChatWindow, TheGameText->fetch("LAN:ErrorUnknown"), chatSystemColor, -1, -1); - } - } - } - -} - -void LANAPI::OnPlayerList( LANPlayer *playerList ) -{ - if (m_inLobby) - { - - UnsignedInt selectedIP = 0; - Int selectedIndex = -1; - Int indexToSelect = -1; - GadgetListBoxGetSelected(listboxPlayers, &selectedIndex); - - if (selectedIndex != -1 ) - selectedIP = (UnsignedInt) GadgetListBoxGetItemData(listboxPlayers, selectedIndex, 0); - - GadgetListBoxReset(listboxPlayers); - - LANPlayer *player = m_lobbyPlayers; - while (player) - { - Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), playerColor, -1, -1); - GadgetListBoxSetItemData(listboxPlayers, (void *)player->getIP(),addedIndex, 0 ); - - if (selectedIP == player->getIP()) - indexToSelect = addedIndex; - - player = player->getNext(); - } - - if (indexToSelect >= 0) - GadgetListBoxSetSelected(listboxPlayers, indexToSelect); - } -} - -void LANAPI::OnNameChange( UnsignedInt IP, UnicodeString newName ) -{ - OnPlayerList(m_lobbyPlayers); -} - -void LANAPI::OnInActive(UnsignedInt IP) { - -} - -void LANAPI::OnChat( UnicodeString player, UnsignedInt ip, UnicodeString message, ChatType format ) -{ - GameWindow *chatWindow = NULL; - - if (m_inLobby) - { - chatWindow = listboxChatWindow; - } - else if( m_currentGame && m_currentGame->isGameInProgress() && TheShell->isShellActive()) - { - chatWindow = listboxChatWindowScoreScreen; - } - else if( m_currentGame && !m_currentGame->isGameInProgress()) - { - chatWindow = listboxChatWindowLanGame; - } - if (chatWindow == NULL) - return; - Int index = -1; - UnicodeString unicodeChat; - switch (format) - { - case LANAPIInterface::LANCHAT_SYSTEM: - unicodeChat = L""; - unicodeChat.concat(message); - unicodeChat.concat(L""); - index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatSystemColor, -1, -1); - break; - case LANAPIInterface::LANCHAT_EMOTE: - unicodeChat = player; - unicodeChat.concat(L' '); - unicodeChat.concat(message); - if (ip == m_localIP) - index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatLocalActionColor, -1, -1); - else - index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatActionColor, -1, -1); - break; - case LANAPIInterface::LANCHAT_NORMAL: - default: - { - // Do the language filtering. - TheLanguageFilter->filterLine(message); - - Color chatColor = GameMakeColor(255, 255, 255, 255); - if (m_currentGame) - { - Int slotNum = m_currentGame->getSlotNum(player); - // it'll be -1 if its invalid. - if (slotNum >= 0) { - GameSlot *gs = m_currentGame->getSlot(slotNum); - if (gs) { - Int colorIndex = gs->getColor(); - MultiplayerColorDefinition *def = TheMultiplayerSettings->getColor(colorIndex); - if (def) - chatColor = def->getColor(); - } - } - } - - unicodeChat = L"["; - unicodeChat.concat(player); - unicodeChat.concat(L"] "); - unicodeChat.concat(message); - if (ip == m_localIP) - index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatColor, -1, -1); - else - index =GadgetListBoxAddEntryText(chatWindow, unicodeChat, chatColor, -1, -1); - break; - } - } - GadgetListBoxSetItemData(chatWindow, (void *)-1, index); -} diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp deleted file mode 100644 index fd505c41297..00000000000 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ /dev/null @@ -1,661 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -/////////////////////////////////////////////////////////////////////////////////////// -// FILE: LANAPIHandlers.cpp -// Author: Matthew D. Campbell, October 2001 -// Description: LAN callback handlers -/////////////////////////////////////////////////////////////////////////////////////// - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "Common/crc.h" -#include "Common/GameState.h" -#include "Common/Registry.h" -#include "Common/GlobalData.h" -#include "Common/QuotedPrintable.h" -#include "Common/UserPreferences.h" -#include "GameNetwork/LANAPI.h" -#include "GameNetwork/LANAPICallbacks.h" -#include "GameClient/MapUtil.h" - -void LANAPI::handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ) -{ - if (m_inLobby) - { - LANMessage reply; - fillInLANMessage( &reply ); - reply.messageType = LANMessage::MSG_LOBBY_ANNOUNCE; - - sendMessage(&reply); - m_lastResendTime = timeGetTime(); - } - else - { - // In game - are we a game host? - if (m_currentGame) - { - if (m_currentGame->getIP(0) == m_localIP) - { - LANMessage reply; - fillInLANMessage( &reply ); - reply.messageType = LANMessage::MSG_GAME_ANNOUNCE; - AsciiString gameOpts = GenerateGameOptionsString(); - strlcpy(reply.GameInfo.options, gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); - wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); - reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); - - sendMessage(&reply); - } - else - { - // We're a joiner - } - } - } - // Add the player to the lobby player list - LANPlayer *player = LookupPlayer(senderIP); - if (!player) - { - player = NEW LANPlayer; - player->setIP(senderIP); - } - else - { - removePlayer(player); - } - player->setName(UnicodeString(msg->name)); - player->setHost(msg->hostName); - player->setLogin(msg->userName); - player->setLastHeard(timeGetTime()); - - addPlayer(player); - - OnNameChange(player->getIP(), player->getName()); -} - -void LANAPI::handleGameAnnounce( LANMessage *msg, UnsignedInt senderIP ) -{ - if (senderIP == m_localIP) - { - return; // Don't try to update own info - } - else if (m_currentGame && m_currentGame->isGameInProgress()) - { - return; // Don't care about games if we're playing - } - else if (senderIP == m_directConnectRemoteIP) - { - - if (m_currentGame == NULL) - { - LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName)); - if (!game) - { - game = NEW LANGameInfo; - game->setName(UnicodeString(msg->GameInfo.gameName)); - addGame(game); - } - Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options)); - game->setGameInProgress(msg->GameInfo.inProgress); - game->setIsDirectConnect(msg->GameInfo.isDirectConnect); - game->setLastHeard(timeGetTime()); - if (!success) - { - // remove from list - removeGame(game); - delete game; - return; - } - RequestGameJoin(game, m_directConnectRemoteIP); - } - } - else - { - LANGameInfo *game = LookupGame(UnicodeString(msg->GameInfo.gameName)); - if (!game) - { - game = NEW LANGameInfo; - game->setName(UnicodeString(msg->GameInfo.gameName)); - addGame(game); - } - Bool success = ParseGameOptionsString(game,AsciiString(msg->GameInfo.options)); - game->setGameInProgress(msg->GameInfo.inProgress); - game->setIsDirectConnect(msg->GameInfo.isDirectConnect); - game->setLastHeard(timeGetTime()); - if (!success) - { - // remove from list - removeGame(game); - delete game; - game = NULL; - } - - OnGameList( m_games ); - // if (game == m_currentGame && !m_inLobby) - // OnSlotList(RET_OK, game); - } -} - -void LANAPI::handleLobbyAnnounce( LANMessage *msg, UnsignedInt senderIP ) -{ - LANPlayer *player = LookupPlayer(senderIP); - if (!player) - { - player = NEW LANPlayer; - player->setIP(senderIP); - } - else - { - removePlayer(player); - } - player->setName(UnicodeString(msg->name)); - player->setHost(msg->hostName); - player->setLogin(msg->userName); - player->setLastHeard(timeGetTime()); - - addPlayer(player); - - OnNameChange(player->getIP(), player->getName()); -} - -void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP ) -{ - // In game - are we a game host? - if (m_currentGame) - { - if (m_currentGame->getIP(0) == m_localIP || (m_currentGame->isGameInProgress() && TheNetwork && TheNetwork->isPacketRouter())) // if we're in game we should reply if we're the packet router - { - LANMessage reply; - fillInLANMessage( &reply ); - reply.messageType = LANMessage::MSG_GAME_ANNOUNCE; - - AsciiString gameOpts = GameInfoToAsciiString(m_currentGame); - strlcpy(reply.GameInfo.options,gameOpts.str(), ARRAY_SIZE(reply.GameInfo.options)); - wcslcpy(reply.GameInfo.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameInfo.gameName)); - reply.GameInfo.inProgress = m_currentGame->isGameInProgress(); - reply.GameInfo.isDirectConnect = m_currentGame->getIsDirectConnect(); - - sendMessage(&reply, senderIP); - } - } -} - -void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) -{ - UnsignedInt responseIP = senderIP; // need this cause the player may or may not be - // in the player list at the sendMessage. - - if (msg->GameToJoin.gameIP != m_localIP) - { - return; // Not us. Ignore it. - } - LANMessage reply; - fillInLANMessage( &reply ); - if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == m_localIP) - { - if (m_currentGame->isGameInProgress()) - { - reply.messageType = LANMessage::MSG_JOIN_DENY; - reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_STARTED; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because game already started.")); - } - else - { - int player; - Bool canJoin = true; - - // see if the CRCs match -#if defined(RTS_DEBUG) - if (TheGlobalData->m_netMinPlayers > 0) { -#endif -// TheSuperHackers @todo Enable CRC checks! -#if !RTS_ZEROHOUR - if (msg->GameToJoin.iniCRC != TheGlobalData->m_iniCRC || - msg->GameToJoin.exeCRC != TheGlobalData->m_exeCRC) - { - DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of CRC mismatch. CRCs are them/us INI:%X/%X exe:%X/%X", - msg->GameToJoin.iniCRC, TheGlobalData->m_iniCRC, - msg->GameToJoin.exeCRC, TheGlobalData->m_exeCRC)); - reply.messageType = LANMessage::MSG_JOIN_DENY; - reply.GameNotJoined.reason = LANAPIInterface::RET_CRC_MISMATCH; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - canJoin = false; - } -#endif -#if defined(RTS_DEBUG) - } -#endif - -// TheSuperHackers @tweak Disables the duplicate serial check -#if 0 - // check for a duplicate serial - AsciiString s; - for (player = 0; canJoin && playergetLANSlot(player); - s.clear(); - if (player == 0) - { - GetStringFromRegistry("\\ergc", "", s); - } - else if (slot->isHuman()) - { - s = slot->getSerial(); - if (s.isEmpty()) - s = ""; - } - - if (s.isNotEmpty()) - { - DEBUG_LOG(("Checking serial '%s' in slot %d", s.str(), player)); - - if (!strncmp(s.str(), msg->GameToJoin.serial, g_maxSerialLength)) - { - // serials match! kick the punk! - reply.messageType = LANMessage::MSG_JOIN_DENY; - reply.GameNotJoined.reason = LANAPIInterface::RET_SERIAL_DUPE; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - canJoin = false; - - DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of duplicate serial # (%s).", s.str())); - break; - } - } - } -#endif - - // We're the host, so see if he has a duplicate name - for (player = 0; canJoin && playergetLANSlot(player); - if (slot->isHuman() && slot->getName().compare(msg->name) == 0) - { - // just deny duplicates - reply.messageType = LANMessage::MSG_JOIN_DENY; - reply.GameNotJoined.reason = LANAPIInterface::RET_DUPLICATE_NAME; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - canJoin = false; - - DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of duplicate names.")); - break; - } - } - - // TheSuperHackers @bugfix Stubbjax 26/09/2025 Players can now join open slots regardless of starting spots on the map. - for (player = 0; canJoin && playergetLANSlot(player)->isOpen()) - { - // OK, add him in. - reply.messageType = LANMessage::MSG_JOIN_ACCEPT; - wcslcpy(reply.GameJoined.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameJoined.gameName)); - reply.GameJoined.slotPosition = player; - reply.GameJoined.gameIP = m_localIP; - reply.GameJoined.playerIP = senderIP; - - LANGameSlot newSlot; - newSlot.setState(SLOT_PLAYER, UnicodeString(msg->name)); - newSlot.setIP(senderIP); - newSlot.setPort(NETWORK_BASE_PORT_NUMBER); - newSlot.setLastHeard(timeGetTime()); - newSlot.setSerial(msg->GameToJoin.serial); - m_currentGame->setSlot(player,newSlot); - DEBUG_LOG(("LANAPI::handleRequestJoin - added player %ls at ip 0x%08x to the game", msg->name, senderIP)); - - OnPlayerJoin(player, UnicodeString(msg->name)); - responseIP = 0; - - break; - } - } - - if (canJoin && player == MAX_SLOTS) - { - reply.messageType = LANMessage::MSG_JOIN_DENY; - wcslcpy(reply.GameNotJoined.gameName, m_currentGame->getName().str(), ARRAY_SIZE(reply.GameNotJoined.gameName)); - reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_FULL; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because game is full.")); - } - } - } - else - { - reply.messageType = LANMessage::MSG_JOIN_DENY; - reply.GameNotJoined.reason = LANAPIInterface::RET_GAME_GONE; - reply.GameNotJoined.gameIP = m_localIP; - reply.GameNotJoined.playerIP = senderIP; - } - sendMessage(&reply, responseIP); - RequestGameOptions(GenerateGameOptionsString(), true); -} - -void LANAPI::handleJoinAccept( LANMessage *msg, UnsignedInt senderIP ) -{ - if (msg->GameJoined.playerIP == m_localIP) // Is it for us? - { - if (m_pendingAction == ACT_JOIN) // Are we trying to join? - { - m_currentGame = LookupGame(UnicodeString(msg->GameJoined.gameName)); - - if (!m_currentGame) - { - DEBUG_ASSERTCRASH(false, ("Could not find game to join!")); - OnGameJoin(RET_UNKNOWN, NULL); - } - else - { - m_inLobby = false; - AsciiString options = GameInfoToAsciiString(m_currentGame); - m_currentGame->enterGame(); - ParseAsciiStringToGameInfo(m_currentGame, options); - - Int pos = msg->GameJoined.slotPosition; - - LANGameSlot slot; - slot.setState(SLOT_PLAYER, m_name); - slot.setIP(m_localIP); - slot.setPort(NETWORK_BASE_PORT_NUMBER); - slot.setLastHeard(0); - slot.setLogin(m_userName); - slot.setHost(m_hostName); - m_currentGame->setSlot(pos, slot); - - m_currentGame->getLANSlot(0)->setHost(msg->hostName); - m_currentGame->getLANSlot(0)->setLogin(msg->userName); - - LANPreferences prefs; - AsciiString entry; - entry.format("%d.%d.%d.%d:%s", PRINTF_IP_AS_4_INTS(senderIP), UnicodeStringToQuotedPrintable(m_currentGame->getSlot(0)->getName()).str()); - prefs["RemoteIP0"] = entry; - prefs.write(); - - OnGameJoin(RET_OK, m_currentGame); - //DEBUG_ASSERTCRASH(false, ("setting host to %ls@%ls", m_currentGame->getLANSlot(0)->getUser()->getLogin().str(), - // m_currentGame->getLANSlot(0)->getUser()->getHost().str())); - } - m_pendingAction = ACT_NONE; - m_expiration = 0; - } - } -} - -void LANAPI::handleJoinDeny( LANMessage *msg, UnsignedInt senderIP ) -{ - if (msg->GameJoined.playerIP == m_localIP) // Is it for us? - { - if (m_pendingAction == ACT_JOIN) // Are we trying to join? - { - OnGameJoin(msg->GameNotJoined.reason, LookupGame(UnicodeString(msg->GameNotJoined.gameName))); - m_pendingAction = ACT_NONE; - m_expiration = 0; - } - } -} - -void LANAPI::handleRequestGameLeave( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress()) - { - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame->getIP(player) == senderIP) - { - if (player == 0) - { - OnHostLeave(); - removeGame(m_currentGame); - delete m_currentGame; - m_currentGame = NULL; - - /// @todo re-add myself to lobby? Or just keep me there all the time? If we send a LOBBY_ANNOUNCE things'll work out... - LANPlayer *lanPlayer = LookupPlayer(m_localIP); - if (!lanPlayer) - { - lanPlayer = NEW LANPlayer; - lanPlayer->setIP(m_localIP); - } - else - { - removePlayer(lanPlayer); - } - lanPlayer->setName(UnicodeString(m_name)); - lanPlayer->setHost(m_hostName); - lanPlayer->setLogin(m_userName); - lanPlayer->setLastHeard(timeGetTime()); - addPlayer(lanPlayer); - - } - else - { - if (AmIHost()) - { - // remove the deadbeat - LANGameSlot slot; - slot.setState(SLOT_OPEN); - m_currentGame->setSlot( player, slot ); - } - OnPlayerLeave(UnicodeString(msg->name)); - m_currentGame->getLANSlot(player)->setState(SLOT_OPEN); - m_currentGame->resetAccepted(); - RequestGameOptions(GenerateGameOptionsString(), false, senderIP); - //m_currentGame->endGame(); - } - break; - } - DEBUG_ASSERTCRASH(player < MAX_SLOTS, ("Didn't find player!")); - } - } - else if (m_inLobby) - { - // Look for dissappearing games - LANGameInfo *game = m_games; - while (game) - { - if (game->getName().compare(msg->GameToLeave.gameName) == 0) - { - removeGame(game); - delete game; - OnGameList(m_games); - break; - } - game = game->getNext(); - } - } -} - -void LANAPI::handleRequestLobbyLeave( LANMessage *msg, UnsignedInt senderIP ) -{ - if (m_inLobby) - { - LANPlayer *player = m_lobbyPlayers; - while (player) - { - if (player->getIP() == senderIP) - { - removePlayer(player); - OnPlayerList(m_lobbyPlayers); - break; - } - player = player->getNext(); - } - } -} - -void LANAPI::handleSetAccept( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress()) - { - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame->getIP(player) == senderIP) - { - OnAccept(senderIP, msg->Accept.isAccepted); - break; - } - } - } -} - -void LANAPI::handleHasMap( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame) - { - CRC mapNameCRC; -// mapNameCRC.computeCRC(m_currentGame->getMap().str(), m_currentGame->getMap().getLength()); - AsciiString portableMapName = TheGameState->realMapPathToPortableMapPath(m_currentGame->getMap()); - mapNameCRC.computeCRC(portableMapName.str(), portableMapName.getLength()); - if (mapNameCRC.get() != msg->MapStatus.mapCRC) - { - return; - } - - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame->getIP(player) == senderIP) - { - OnHasMap(senderIP, msg->MapStatus.hasMap); - break; - } - } - } -} - -void LANAPI::handleChat( LANMessage *msg, UnsignedInt senderIP ) -{ - if (m_inLobby) - { - LANPlayer *player; - if((player=LookupPlayer(senderIP)) != 0) - { - OnChat(UnicodeString(player->getName()), player->getIP(), UnicodeString(msg->Chat.message), msg->Chat.chatType); - player->setLastHeard(timeGetTime()); - } - } - else - { - if (LookupGame(UnicodeString(msg->Chat.gameName)) != m_currentGame) - { - DEBUG_LOG(("Game '%ls' is not my game", msg->Chat.gameName)); - if (m_currentGame) - { - DEBUG_LOG(("Current game is '%ls'", m_currentGame->getName().str())); - } - return; - } - - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame && m_currentGame->getIP(player) == senderIP) - { - OnChat(UnicodeString(msg->name), m_currentGame->getIP(player), UnicodeString(msg->Chat.message), msg->Chat.chatType); - break; - } - } - } -} - -void LANAPI::handleGameStart( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == senderIP && !m_currentGame->isGameInProgress()) - { - OnGameStart(); - } -} - -void LANAPI::handleGameStartTimer( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame && m_currentGame->getIP(0) == senderIP && !m_currentGame->isGameInProgress()) - { - OnGameStartTimer(msg->StartTimer.seconds); - } -} - -void LANAPI::handleGameOptions( LANMessage *msg, UnsignedInt senderIP ) -{ - if (!m_inLobby && m_currentGame && !m_currentGame->isGameInProgress()) - { - int player; - for (player = 0; player < MAX_SLOTS; ++player) - { - if (m_currentGame->getIP(player) == senderIP) - { - OnGameOptions(senderIP, player, AsciiString(msg->GameOptions.options)); - break; - } - } - } -} - -void LANAPI::handleInActive(LANMessage *msg, UnsignedInt senderIP) { - if (m_inLobby || (m_currentGame == NULL) || (m_currentGame->isGameInProgress())) { - return; - } - - // check to see if we are the host of this game. - if (m_currentGame->amIHost() == FALSE) { - return; - } - - UnicodeString playerName; - playerName = msg->name; - - Int slotNum = m_currentGame->getSlotNum(playerName); - if (slotNum < 0) - return; - GameSlot *slot = m_currentGame->getSlot(slotNum); - if (slot == NULL) { - return; - } - - if (senderIP != slot->getIP()) { - return; - } - - // don't want to unaccept the host, that's silly. They can't hit start alt-tabbed anyways. - if (senderIP == TheLAN->GetLocalIP()) { - return; - } - - // only unaccept if the timer hasn't started yet. - if (m_gameStartTime != 0) { - return; - } - - slot->unAccept(); - AsciiString options = GenerateGameOptionsString(); - RequestGameOptions(options, FALSE); - lanUpdateSlotList(); -} diff --git a/Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp deleted file mode 100644 index 19ac4d52b07..00000000000 --- a/Generals/Code/GameEngine/Source/GameNetwork/LANGameInfo.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* -** 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 . -*/ - -//////////////////////////////////////////////////////////////////////////////// -// // -// (c) 2001-2003 Electronic Arts Inc. // -// // -//////////////////////////////////////////////////////////////////////////////// - -// FILE: LANGameInfo.cpp ////////////////////////////////////////////////////// -// LAN game setup state info -// Author: Matthew D. Campbell, December 2001 - -#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine - -#include "GameClient/GameInfoWindow.h" -#include "GameClient/GameText.h" -#include "GameClient/GadgetListBox.h" -#include "GameNetwork/LANGameInfo.h" -#include "GameNetwork/LANAPICallbacks.h" -#include "Common/MultiplayerSettings.h" -#include "strtok_r.h" -/* -#include "GameNetwork/LAN.h" -#include "GameNetwork/LANGame.h" -#include "GameNetwork/LANPing.h" -#include "GameNetwork/LANusers.h" -#include "GameNetwork/LANmenus.h" -*/ - -// Singleton ------------------------------------------ - -LANGameInfo *TheLANGameInfo = NULL; - -// LANGameSlot ---------------------------------------- - -LANGameSlot::LANGameSlot() -{ - m_lastHeard = 0; -} - - -LANPlayer * LANGameSlot::getUser( void ) -{ - if (isHuman()) - { - m_user.setIP(getIP()); - m_user.setLastHeard(getLastHeard()); - m_user.setName(getName()); - m_user.setNext(NULL); - return &m_user; - } - return NULL; -} - -// Various tests -Bool LANGameSlot::isUser( LANPlayer *user ) -{ - return (user && m_state == SLOT_PLAYER && user->getIP() == getIP()); -} - -Bool LANGameSlot::isUser( UnicodeString userName ) -{ - return (m_state == SLOT_PLAYER && !userName.compareNoCase(getName())); -} - -Bool LANGameSlot::isLocalPlayer( void ) const -{ - return isHuman() && TheLAN && TheLAN->GetLocalIP() == getIP(); -} - -// LANGameInfo ---------------------------------------- - -LANGameInfo::LANGameInfo() -{ - //Added By Sadullah Nader - //Initializtions missing and needed - m_lastHeard = 0; - m_next = NULL; - // - for (Int i = 0; i< MAX_SLOTS; ++i) - setSlotPointer(i, &m_LANSlot[i]); - - setLocalIP(TheLAN->GetLocalIP()); -} - -void LANGameInfo::setSlot( Int slotNum, LANGameSlot slotInfo ) -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::setSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return; - - m_LANSlot[slotNum] = slotInfo; - - if (slotNum == 0) - { - m_LANSlot[slotNum].setAccept(); - m_LANSlot[slotNum].setMapAvailability(true); - } -} - -LANGameSlot* LANGameInfo::getLANSlot( Int slotNum ) -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::getLANSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return NULL; - - return &m_LANSlot[slotNum]; -} - -const LANGameSlot* LANGameInfo::getConstLANSlot( Int slotNum ) const -{ - DEBUG_ASSERTCRASH( slotNum >= 0 && slotNum < MAX_SLOTS, ("LANGameInfo::getConstLANSlot - Invalid slot number")); - if (slotNum < 0 || slotNum >= MAX_SLOTS) - return NULL; - - return &m_LANSlot[slotNum]; -} - -Int LANGameInfo::getLocalSlotNum( void ) const -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for local game slot while not in game")); - if (!m_inGame) - return -1; - - for (Int i=0; iisLocalPlayer()) - return i; - } - return -1; -} - -Int LANGameInfo::getSlotNum( UnicodeString userName ) -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game")); - if (!m_inGame) - return -1; - - for (Int i=0; iisUser( userName )) - return i; - } - return -1; -} - -Bool LANGameInfo::amIHost( void ) -{ - DEBUG_ASSERTCRASH(m_inGame, ("Looking for game slot while not in game")); - if (!m_inGame) - return false; - - return getLANSlot(0)->isLocalPlayer(); -} - -void LANGameInfo::setMap( AsciiString mapName ) -{ - GameInfo::setMap(mapName); -} - -void LANGameInfo::setSeed( Int seed ) -{ - GameInfo::setSeed(seed); -} - -void LANGameInfo::resetAccepted( void ) -{ - if (TheLAN) - { - TheLAN->ResetGameStartTimer(); - if (TheLAN->GetMyGame() == this && TheLAN->AmIHost()) - LANEnableStartButton(true); - } - for(int i = 0; i< MAX_SLOTS; i++) - { - m_LANSlot[i].unAccept(); - } -} -// Misc game-related functionality -------------------- - - -void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList ) -{ - LANGameInfo *selectedPtr = NULL; - Int selectedIndex = -1; - Int indexToSelect = -1; - if (gameListbox) - { - GadgetListBoxGetSelected(gameListbox, &selectedIndex); - - if (selectedIndex != -1 ) - { - selectedPtr = (LANGameInfo *)GadgetListBoxGetItemData(gameListbox, selectedIndex, 0); - } - - GadgetListBoxReset(gameListbox); - - while (gameList) - { - UnicodeString txtGName; - txtGName = L""; - if( gameList->isGameInProgress() ) - { - txtGName.concat(L"["); - } - txtGName.concat(gameList->getPlayerName(0)); - if( gameList->isGameInProgress() ) - { - txtGName.concat(L"]"); - } - Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, (gameList->isGameInProgress())?gameInProgressColor:gameColor, -1, -1); - GadgetListBoxSetItemData(gameListbox, (void *)gameList, addedIndex, 0 ); - - if (selectedPtr == gameList) - indexToSelect = addedIndex; - - gameList = gameList->getNext(); - } - - if (indexToSelect >= 0) - GadgetListBoxSetSelected(gameListbox, indexToSelect); - else - HideGameInfoWindow(TRUE); - } -} - -AsciiString GenerateGameOptionsString( void ) -{ - if(!TheLAN->GetMyGame() || !TheLAN->GetMyGame()->amIHost()) - return AsciiString::TheEmptyString; - - return GameInfoToAsciiString(TheLAN->GetMyGame()); -} - -Bool ParseGameOptionsString(LANGameInfo *game, AsciiString options) -{ - if (!TheLAN || !game) - return false; - - Int oldLocalSlotNum = (game->isInGame()) ? game->getLocalSlotNum() : -1; - Bool wasInGame = oldLocalSlotNum >= 0; -// Int hadMap = wasInGame && game->getSlot(oldLocalSlotNum)->hasMap(); - AsciiString oldMap = game->getMap(); - UnsignedInt oldMapCRC, newMapCRC; - oldMapCRC = game->getMapCRC(); - - std::map oldLogins, oldMachines; - std::map::iterator mapIt; - Int i; - for (i=0; igetLANSlot(i); - if (slot && slot->isHuman()) - { - //DEBUG_LOG(("Saving off %ls@%ls for %ls", slot->getUser()->getLogin().str(), slot->getUser()->getHost().str(), slot->getName().str())); - oldLogins[slot->getName()] = slot->getUser()->getLogin(); - oldMachines[slot->getName()] = slot->getUser()->getHost(); - } - } - - if (ParseAsciiStringToGameInfo(game, options)) - { - Int newLocalSlotNum = (game->isInGame()) ? game->getLocalSlotNum() : -1; - Bool isInGame = newLocalSlotNum >= 0; - if (!TheLAN->AmIHost() && isInGame) - { -// Int hasMap = game->getSlot(newLocalSlotNum)->hasMap(); - newMapCRC = game->getMapCRC(); - //DEBUG_LOG(("wasInGame:%d isInGame:%d hadMap:%d hasMap:%d oldMap:%s newMap:%s", wasInGame, isInGame, hadMap, hasMap, oldMap.str(), game->getMap().str())); - if ( (oldMapCRC ^ newMapCRC)/*(hasMap ^ hadMap)*/ || (!wasInGame && isInGame) ) - { - // it changed. send it - TheLAN->RequestHasMap(); - lanUpdateSlotList(); - updateGameOptions(); - } - } - // clean up LAN users, etc. - UnsignedInt now = timeGetTime(); - for (i=0; igetLANSlot(i); - - if (slot->isHuman()) - { - slot->setLastHeard(now); - mapIt = oldLogins.find(slot->getName()); - if (mapIt != oldLogins.end()) - slot->setLogin(mapIt->second); - mapIt = oldMachines.find(slot->getName()); - if (mapIt != oldMachines.end()) - slot->setHost(mapIt->second); - //DEBUG_LOG(("Restored %ls@%ls for %ls", slot->getUser()->getLogin().str(), slot->getUser()->getHost().str(), slot->getName().str())); - } - } - - return true; - } - - return false; -} - diff --git a/GeneralsMD/Code/GameEngine/CMakeLists.txt b/GeneralsMD/Code/GameEngine/CMakeLists.txt index 5546c7bc915..180f63877fd 100644 --- a/GeneralsMD/Code/GameEngine/CMakeLists.txt +++ b/GeneralsMD/Code/GameEngine/CMakeLists.txt @@ -507,7 +507,7 @@ set(GAMEENGINE_SRC Include/GameNetwork/FrameData.h Include/GameNetwork/FrameDataManager.h Include/GameNetwork/FrameMetrics.h - Include/GameNetwork/GameInfo.h +# Include/GameNetwork/GameInfo.h Include/GameNetwork/GameMessageParser.h Include/GameNetwork/GameSpy/BuddyDefs.h Include/GameNetwork/GameSpy/BuddyThread.h @@ -531,10 +531,10 @@ set(GAMEENGINE_SRC Include/GameNetwork/GameSpyThread.h Include/GameNetwork/GUIUtil.h Include/GameNetwork/IPEnumeration.h - Include/GameNetwork/LANAPI.h - Include/GameNetwork/LANAPICallbacks.h +# Include/GameNetwork/LANAPI.h +# Include/GameNetwork/LANAPICallbacks.h Include/GameNetwork/LANGameInfo.h - Include/GameNetwork/LANPlayer.h +# Include/GameNetwork/LANPlayer.h Include/GameNetwork/NAT.h Include/GameNetwork/NetCommandList.h Include/GameNetwork/NetCommandMsg.h @@ -1094,7 +1094,7 @@ set(GAMEENGINE_SRC Source/GameNetwork/FrameData.cpp Source/GameNetwork/FrameDataManager.cpp Source/GameNetwork/FrameMetrics.cpp - Source/GameNetwork/GameInfo.cpp +# Source/GameNetwork/GameInfo.cpp Source/GameNetwork/GameMessageParser.cpp #Source/GameNetwork/GameSpyChat.cpp #Source/GameNetwork/GameSpyGameInfo.cpp @@ -1115,10 +1115,10 @@ set(GAMEENGINE_SRC Source/GameNetwork/GameSpyOverlay.cpp Source/GameNetwork/GUIUtil.cpp Source/GameNetwork/IPEnumeration.cpp - Source/GameNetwork/LANAPI.cpp - Source/GameNetwork/LANAPICallbacks.cpp - Source/GameNetwork/LANAPIhandlers.cpp - Source/GameNetwork/LANGameInfo.cpp +# Source/GameNetwork/LANAPI.cpp +# Source/GameNetwork/LANAPICallbacks.cpp +# Source/GameNetwork/LANAPIhandlers.cpp +# Source/GameNetwork/LANGameInfo.cpp Source/GameNetwork/NAT.cpp Source/GameNetwork/NetCommandList.cpp Source/GameNetwork/NetCommandMsg.cpp From 982021de8ba6b01b3c29b70637c67e77b1f369ca Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 30 Nov 2025 18:55:55 +0100 Subject: [PATCH 10/17] feat(version): Add short Git hash string to Version class. --- .../Code/GameEngine/Include/Common/version.h | 8 ++++++ .../Code/GameEngine/Source/Common/version.cpp | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/version.h b/GeneralsMD/Code/GameEngine/Include/Common/version.h index 5c139fb6145..29bcfb9780e 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/version.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/version.h @@ -66,6 +66,9 @@ class Version AsciiString getAsciiGitTagOrHash() const; ///< Returns the git head commit tag or hash. Is prefixed with ~ if there were uncommitted changes. UnicodeString getUnicodeGitTagOrHash() const; ///< Returns the git head commit tag or hash. Is prefixed with ~ if there were uncommitted changes. + AsciiString getAsciiGitShortHash() const; ///< Returns the git head commit short hash. Is prefixed with ~ if there were uncommitted changes. + UnicodeString getUnicodeGitShortHash() const; ///< Returns the git head commit short hash. Is prefixed with ~ if there were uncommitted changes. + AsciiString getAsciiGitCommitTime() const; ///< Returns the git head commit time in YYYY-mm-dd HH:MM:SS format UnicodeString getUnicodeGitCommitTime() const; ///< Returns the git head commit time in YYYY-mm-dd HH:MM:SS format @@ -95,6 +98,9 @@ class Version static AsciiString buildAsciiGitTagOrHash(); static UnicodeString buildUnicodeGitTagOrHash(); + static AsciiString buildAsciiGitShortHash(); + static UnicodeString buildUnicodeGitShortHash(); + static AsciiString buildAsciiGitCommitTime(); static UnicodeString buildUnicodeGitCommitTime(); @@ -109,9 +115,11 @@ class Version AsciiString m_buildDate; AsciiString m_asciiGitCommitCount; AsciiString m_asciiGitTagOrHash; + AsciiString m_asciiGitShortHash; AsciiString m_asciiGitCommitTime; UnicodeString m_unicodeGitCommitCount; UnicodeString m_unicodeGitTagOrHash; + UnicodeString m_unicodeGitShortHash; UnicodeString m_unicodeGitCommitTime; Bool m_showFullVersion; }; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/version.cpp b/GeneralsMD/Code/GameEngine/Source/Common/version.cpp index abbf0991ed6..6dbb34d6e57 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/version.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/version.cpp @@ -45,9 +45,11 @@ Version::Version() m_buildLocation = AsciiString::TheEmptyString; m_asciiGitCommitCount = buildAsciiGitCommitCount(); m_asciiGitTagOrHash = buildAsciiGitTagOrHash(); + m_asciiGitShortHash = buildAsciiGitShortHash(); m_asciiGitCommitTime = buildAsciiGitCommitTime(); m_unicodeGitCommitCount = buildUnicodeGitCommitCount(); m_unicodeGitTagOrHash = buildUnicodeGitTagOrHash(); + m_unicodeGitShortHash = buildUnicodeGitShortHash(); m_unicodeGitCommitTime = buildUnicodeGitCommitTime(); #if defined(RTS_DEBUG) m_showFullVersion = TRUE; @@ -235,6 +237,16 @@ UnicodeString Version::getUnicodeGitTagOrHash() const return m_unicodeGitTagOrHash; } +AsciiString Version::getAsciiGitShortHash() const +{ + return m_asciiGitShortHash; +} + +UnicodeString Version::getUnicodeGitShortHash() const +{ + return m_unicodeGitShortHash; +} + AsciiString Version::getAsciiGitCommitTime() const { return m_asciiGitCommitTime; @@ -397,6 +409,22 @@ AsciiString Version::buildAsciiGitTagOrHash() return str; } +AsciiString Version::buildAsciiGitShortHash() +{ + AsciiString str; + str.format("%s%s", + GitUncommittedChanges ? "~" : "", + GitShortSHA1); + return str; +} + +UnicodeString Version::buildUnicodeGitShortHash() +{ + UnicodeString str; + str.translate(buildAsciiGitShortHash()); + return str; +} + UnicodeString Version::buildUnicodeGitTagOrHash() { UnicodeString str; From 3d3bd29526167653aa4f10fdfd1f0e9da008f4e3 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 30 Nov 2025 18:56:56 +0100 Subject: [PATCH 11/17] Changed to Git short hash because of limited space in the 'Status' tab. --- Core/GameEngine/Include/GameNetwork/GameInfo.h | 2 +- Core/GameEngine/Include/GameNetwork/LANAPI.h | 2 +- Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | 6 +++--- .../Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/GameInfo.h b/Core/GameEngine/Include/GameNetwork/GameInfo.h index e7ccfdd210a..9d6ca0f5d19 100644 --- a/Core/GameEngine/Include/GameNetwork/GameInfo.h +++ b/Core/GameEngine/Include/GameNetwork/GameInfo.h @@ -62,7 +62,7 @@ class GameSlot UnsignedInt exeCRC; UnsignedInt iniCRC; UnsignedInt productVersion; - AsciiString gitTagOrHash; + AsciiString gitShortHash; UnicodeString productName; }; diff --git a/Core/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h index f2d3a296530..3a8a0c408f3 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPI.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPI.h @@ -431,7 +431,7 @@ struct LANMessage UnsignedInt exeCRC; UnsignedInt iniCRC; UnsignedInt productVersion; - Char gitTagOrHash[33]; + Char gitShortHash[42]; WideChar productName[129]; } ProductInfo; }; diff --git a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index f043b7c6d66..6252f951af6 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -47,7 +47,7 @@ void LANAPI::setProductInfoFromLocalData(GameSlot *slot) productInfo.exeCRC = TheGlobalData->m_exeCRC; productInfo.iniCRC = TheGlobalData->m_iniCRC; productInfo.productVersion = TheVersion->getVersionNumber(); - productInfo.gitTagOrHash = TheVersion->getAsciiGitTagOrHash(); + productInfo.gitShortHash = TheVersion->getAsciiGitShortHash(); productInfo.productName = TheVersion->getUnicodeProductString(); slot->setProductInfo(productInfo); @@ -59,7 +59,7 @@ void LANAPI::setProductInfoFromMessage(LANMessage *msg, GameSlot *slot) productInfo.exeCRC = msg->ProductInfo.exeCRC; productInfo.iniCRC = msg->ProductInfo.iniCRC; productInfo.productVersion = msg->ProductInfo.productVersion; - productInfo.gitTagOrHash = msg->ProductInfo.gitTagOrHash; + productInfo.gitShortHash = msg->ProductInfo.gitShortHash; productInfo.productName = msg->ProductInfo.productName; slot->setProductInfo(productInfo); @@ -74,7 +74,7 @@ void LANAPI::sendProductInfoMessage(Int messageType, UnsignedInt senderIP) msg.ProductInfo.exeCRC = TheGlobalData->m_exeCRC; msg.ProductInfo.iniCRC = TheGlobalData->m_iniCRC; msg.ProductInfo.productVersion = TheVersion->getVersionNumber(); - strlcpy(msg.ProductInfo.gitTagOrHash, TheVersion->getAsciiGitTagOrHash().str(), ARRAY_SIZE(msg.ProductInfo.gitTagOrHash)); + strlcpy(msg.ProductInfo.gitShortHash, TheVersion->getAsciiGitShortHash().str(), ARRAY_SIZE(msg.ProductInfo.gitShortHash)); wcslcpy(msg.ProductInfo.productName, TheVersion->getUnicodeProductString().str(), ARRAY_SIZE(msg.ProductInfo.productName)); sendMessage(&msg, senderIP); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index 88ae56d030f..973334d38f4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -576,10 +576,10 @@ void PopulateInGameDiplomacyPopup( void ) // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using a patched client version. if (slot->isHuman() && slot->getProductInfo().productVersion > 0) { - UnicodeString gitTagOrHash; - gitTagOrHash.translate(slot->getProductInfo().gitTagOrHash); + UnicodeString gitShortHash; + gitShortHash.translate(slot->getProductInfo().gitShortHash); - text.format(L"%s [%s]", text.str(), gitTagOrHash.str()); + text.format(L"%s [%s]", text.str(), gitShortHash.str()); } staticTextStatus[rowNum]->winSetEnabledTextColors(frontColor, backColor); From 5555883234ee835ff3fd71ea73a09132903d0118 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 30 Nov 2025 19:13:21 +0100 Subject: [PATCH 12/17] feat(version): Add short Git hash string to Version class (Generals). Fixed Zero Hour version. --- .../Code/GameEngine/Include/Common/version.h | 8 ++++++ .../Code/GameEngine/Source/Common/version.cpp | 28 +++++++++++++++++++ .../Code/GameEngine/Source/Common/version.cpp | 14 +++++----- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Generals/Code/GameEngine/Include/Common/version.h b/Generals/Code/GameEngine/Include/Common/version.h index 088a0f7217c..5a8921e241b 100644 --- a/Generals/Code/GameEngine/Include/Common/version.h +++ b/Generals/Code/GameEngine/Include/Common/version.h @@ -66,6 +66,9 @@ class Version AsciiString getAsciiGitTagOrHash() const; ///< Returns the git head commit tag or hash. Is prefixed with ~ if there were uncommitted changes. UnicodeString getUnicodeGitTagOrHash() const; ///< Returns the git head commit tag or hash. Is prefixed with ~ if there were uncommitted changes. + AsciiString getAsciiGitShortHash() const; ///< Returns the git head commit short hash. Is prefixed with ~ if there were uncommitted changes. + UnicodeString getUnicodeGitShortHash() const; ///< Returns the git head commit short hash. Is prefixed with ~ if there were uncommitted changes. + AsciiString getAsciiGitCommitTime() const; ///< Returns the git head commit time in YYYY-mm-dd HH:MM:SS format UnicodeString getUnicodeGitCommitTime() const; ///< Returns the git head commit time in YYYY-mm-dd HH:MM:SS format @@ -95,6 +98,9 @@ class Version static AsciiString buildAsciiGitTagOrHash(); static UnicodeString buildUnicodeGitTagOrHash(); + static AsciiString buildAsciiGitShortHash(); + static UnicodeString buildUnicodeGitShortHash(); + static AsciiString buildAsciiGitCommitTime(); static UnicodeString buildUnicodeGitCommitTime(); @@ -109,9 +115,11 @@ class Version AsciiString m_buildDate; AsciiString m_asciiGitCommitCount; AsciiString m_asciiGitTagOrHash; + AsciiString m_asciiGitShortHash; AsciiString m_asciiGitCommitTime; UnicodeString m_unicodeGitCommitCount; UnicodeString m_unicodeGitTagOrHash; + UnicodeString m_unicodeGitShortHash; UnicodeString m_unicodeGitCommitTime; Bool m_showFullVersion; }; diff --git a/Generals/Code/GameEngine/Source/Common/version.cpp b/Generals/Code/GameEngine/Source/Common/version.cpp index a5eca5b66fd..fca4e3d652d 100644 --- a/Generals/Code/GameEngine/Source/Common/version.cpp +++ b/Generals/Code/GameEngine/Source/Common/version.cpp @@ -45,9 +45,11 @@ Version::Version() m_buildLocation = AsciiString::TheEmptyString; m_asciiGitCommitCount = buildAsciiGitCommitCount(); m_asciiGitTagOrHash = buildAsciiGitTagOrHash(); + m_asciiGitShortHash = buildAsciiGitShortHash(); m_asciiGitCommitTime = buildAsciiGitCommitTime(); m_unicodeGitCommitCount = buildUnicodeGitCommitCount(); m_unicodeGitTagOrHash = buildUnicodeGitTagOrHash(); + m_unicodeGitShortHash = buildUnicodeGitShortHash(); m_unicodeGitCommitTime = buildUnicodeGitCommitTime(); #if defined(RTS_DEBUG) m_showFullVersion = TRUE; @@ -235,6 +237,16 @@ UnicodeString Version::getUnicodeGitTagOrHash() const return m_unicodeGitTagOrHash; } +AsciiString Version::getAsciiGitShortHash() const +{ + return m_asciiGitShortHash; +} + +UnicodeString Version::getUnicodeGitShortHash() const +{ + return m_unicodeGitShortHash; +} + AsciiString Version::getAsciiGitCommitTime() const { return m_asciiGitCommitTime; @@ -404,6 +416,22 @@ UnicodeString Version::buildUnicodeGitTagOrHash() return str; } +AsciiString Version::buildAsciiGitShortHash() +{ + AsciiString str; + str.format("%s%s", + GitUncommittedChanges ? "~" : "", + GitShortSHA1); + return str; +} + +UnicodeString Version::buildUnicodeGitShortHash() +{ + UnicodeString str; + str.translate(buildAsciiGitShortHash()); + return str; +} + AsciiString Version::buildAsciiGitCommitTime() { const Int len = 19; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/version.cpp b/GeneralsMD/Code/GameEngine/Source/Common/version.cpp index 6dbb34d6e57..83dc2614157 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/version.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/version.cpp @@ -409,6 +409,13 @@ AsciiString Version::buildAsciiGitTagOrHash() return str; } +UnicodeString Version::buildUnicodeGitTagOrHash() +{ + UnicodeString str; + str.translate(buildAsciiGitTagOrHash()); + return str; +} + AsciiString Version::buildAsciiGitShortHash() { AsciiString str; @@ -425,13 +432,6 @@ UnicodeString Version::buildUnicodeGitShortHash() return str; } -UnicodeString Version::buildUnicodeGitTagOrHash() -{ - UnicodeString str; - str.translate(buildAsciiGitTagOrHash()); - return str; -} - AsciiString Version::buildAsciiGitCommitTime() { const Int len = 19; From 10a4c7d15d5af8a1f0e4343d1cfa0ef65f234bc4 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 2 Dec 2025 02:26:07 +0100 Subject: [PATCH 13/17] Added static assertion for LANMessage and comment for ProductInfo. --- Core/GameEngine/Include/GameNetwork/LANAPI.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h index 3a8a0c408f3..75bd5f9aa81 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPI.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPI.h @@ -426,6 +426,7 @@ struct LANMessage char options[m_lanMaxOptionsLength+1]; } GameOptions; + // ProductInfo is sent with REQUEST_PRODUCT_INFO and RESPONSE_PRODUCT_INFO struct { UnsignedInt exeCRC; @@ -437,3 +438,5 @@ struct LANMessage }; }; #pragma pack(pop) + +static_assert(sizeof(LANMessage) <= MAX_PACKET_SIZE); From 81609c02e20b1c17bfd0163ed81982e0117f1797 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:00:48 +0100 Subject: [PATCH 14/17] Restructured the product info struct. --- .../GameEngine/Include/GameNetwork/GameInfo.h | 13 +- Core/GameEngine/Include/GameNetwork/LANAPI.h | 308 +++++++++--------- .../Include/GameNetwork/LANAPICallbacks.h | 8 +- .../Include/GameNetwork/LANPlayer.h | 8 +- .../Source/GameNetwork/LANAPICallbacks.cpp | 33 +- .../Source/GameNetwork/LANAPIhandlers.cpp | 80 ++++- .../Source/GameNetwork/LANGameInfo.cpp | 6 +- .../GameClient/GUI/GUICallbacks/Diplomacy.cpp | 9 +- .../GameEngine/Source/GameNetwork/GUIUtil.cpp | 8 +- 9 files changed, 267 insertions(+), 206 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/GameInfo.h b/Core/GameEngine/Include/GameNetwork/GameInfo.h index 9d6ca0f5d19..93f9b38ba51 100644 --- a/Core/GameEngine/Include/GameNetwork/GameInfo.h +++ b/Core/GameEngine/Include/GameNetwork/GameInfo.h @@ -59,11 +59,18 @@ class GameSlot public: struct ProductInfo { + enum CPP_11(: UnsignedInt) + { + COMMUNITY_PATCH = 1 << 0 + }; + + UnsignedInt flags; UnsignedInt exeCRC; UnsignedInt iniCRC; - UnsignedInt productVersion; - AsciiString gitShortHash; - UnicodeString productName; + UnicodeString productTitle; + UnicodeString productVersion; + UnicodeString productAuthor; + UnicodeString gitShortHash; }; GameSlot(); diff --git a/Core/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h index 75bd5f9aa81..34ddd8183b7 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPI.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPI.h @@ -140,6 +140,155 @@ class LANAPIInterface : public SubsystemInterface }; +/** + * LAN message class + */ +#pragma pack(push, 1) +struct LANMessage +{ + enum Type ///< What kind of message are we? + { + // Locating everybody + MSG_REQUEST_LOCATIONS, ///< Hey, where is everybody? + MSG_GAME_ANNOUNCE, ///< Here I am, and here's my game info! + MSG_LOBBY_ANNOUNCE, ///< Hey, I'm in the lobby! + + // Joining games + MSG_REQUEST_JOIN, ///< Let me in! Let me in! + MSG_JOIN_ACCEPT, ///< Okay, you can join. + MSG_JOIN_DENY, ///< Go away! We don't want any! + + // Leaving games + MSG_REQUEST_GAME_LEAVE, ///< I want to leave the game + MSG_REQUEST_LOBBY_LEAVE,///< I'm leaving the lobby + + // Game options, chat, etc + MSG_SET_ACCEPT, ///< I'm cool with everything as is. + MSG_MAP_AVAILABILITY, ///< I do (not) have the map. + MSG_CHAT, ///< Just spouting my mouth off. + MSG_GAME_START, ///< Hold on; we're starting! + MSG_GAME_START_TIMER, ///< The game will start in N seconds + MSG_GAME_OPTIONS, ///< Here's some info about the game. + MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey. + + MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address + + // Community Product + MSG_GAME_REQUEST_PRODUCT_INFO = 1000, + MSG_GAME_RESPONSE_PRODUCT_INFO, + MSG_LOBBY_REQUEST_PRODUCT_INFO, + MSG_LOBBY_RESPONSE_PRODUCT_INFO, + MSG_MATCH_REQUEST_PRODUCT_INFO, + MSG_MATCH_RESPONSE_PRODUCT_INFO, + } messageType; + + WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience + char userName[g_lanLoginNameLength+1]; ///< login name, for convenience + char hostName[g_lanHostNameLength+1]; ///< machine name, for convenience + + // No additional data is required for REQUEST_LOCATIONS, LOBBY_ANNOUNCE, + // REQUEST_LOBBY_LEAVE, GAME_START. + union + { + // StartTimer is sent with GAME_START_TIMER + struct + { + Int seconds; + } StartTimer; + + // GameJoined is sent with REQUEST_GAME_LEAVE + struct + { + WideChar gameName[g_lanGameNameLength+1]; + } GameToLeave; + + // GameInfo if sent with GAME_ANNOUNCE + struct + { + WideChar gameName[g_lanGameNameLength+1]; + Bool inProgress; + char options[m_lanMaxOptionsLength+1]; + Bool isDirectConnect; + } GameInfo; + + // PlayerInfo is sent with REQUEST_GAME_INFO for direct connect games. + struct + { + UnsignedInt ip; + WideChar playerName[g_lanPlayerNameLength+1]; + } PlayerInfo; + + // GameToJoin is sent with REQUEST_JOIN + struct + { + UnsignedInt gameIP; + UnsignedInt exeCRC; + UnsignedInt iniCRC; + char serial[g_maxSerialLength]; + } GameToJoin; + + // GameJoined is sent with JOIN_ACCEPT + struct + { + WideChar gameName[g_lanGameNameLength+1]; + UnsignedInt gameIP; + UnsignedInt playerIP; + Int slotPosition; + } GameJoined; + + // GameNotJoined is sent with JOIN_DENY + struct + { + WideChar gameName[g_lanGameNameLength+1]; + UnsignedInt gameIP; + UnsignedInt playerIP; + LANAPIInterface::ReturnType reason; + } GameNotJoined; + + // Accept is sent with SET_ACCEPT + struct + { + WideChar gameName[g_lanGameNameLength+1]; + Bool isAccepted; + } Accept; + + // Accept is sent with MAP_AVAILABILITY + struct + { + WideChar gameName[g_lanGameNameLength+1]; + UnsignedInt mapCRC; // to make sure we're talking about the same map + Bool hasMap; + } MapStatus; + + // Chat is sent with CHAT + struct + { + WideChar gameName[g_lanGameNameLength+1]; + LANAPIInterface::ChatType chatType; + WideChar message[g_lanMaxChatLength+1]; + } Chat; + + // GameOptions is sent with GAME_OPTIONS + struct + { + char options[m_lanMaxOptionsLength+1]; + } GameOptions; + + // ProductInfo is sent with REQUEST_PRODUCT_INFO and RESPONSE_PRODUCT_INFO + struct + { + UnsignedInt flags; + UnsignedInt exeCRC; + UnsignedInt iniCRC; + WideChar data[201]; + } ProductInfo; + }; +}; +#pragma pack(pop) + +static_assert(sizeof(LANMessage) <= MAX_PACKET_SIZE); + + /** * The LANAPI class is used to instantiate a singleton which * implements the interface to all LAN broadcast communications. @@ -260,8 +409,10 @@ class LANAPI : public LANAPIInterface void addGame(LANGameInfo *game); AsciiString createSlotString( void ); - void setProductInfoFromLocalData(GameSlot *slot); - void setProductInfoFromMessage(LANMessage *msg, GameSlot *slot); + static void setProductInfoFromLocalData(GameSlot *slot); + static void setProductInfoFromMessage(LANMessage *msg, GameSlot *slot); + static Bool setProductInfoStrings(const UnicodeString(&input)[4], WideChar(&output)[201]); + static Bool getProductInfoStrings(const WideChar(&input)[201], UnicodeString*(&output)[4]); // Functions to handle incoming messages ----------------------------------- void handleRequestLocations( LANMessage *msg, UnsignedInt senderIP ); @@ -281,7 +432,7 @@ class LANAPI : public LANAPIInterface void handleGameOptions( LANMessage *msg, UnsignedInt senderIP ); void handleInActive( LANMessage *msg, UnsignedInt senderIP ); - void sendProductInfoMessage(Int messageType, UnsignedInt senderIP); + void sendProductInfoMessage(LANMessage::Type messageType, UnsignedInt senderIP); void handleGameProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); void handleGameProductInfoResponse(LANMessage *msg, UnsignedInt senderIP); void handleLobbyProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); @@ -289,154 +440,3 @@ class LANAPI : public LANAPIInterface void handleMatchProductInfoRequest(LANMessage *msg, UnsignedInt senderIP); void handleMatchProductInfoResponse(LANMessage *msg, UnsignedInt senderIP); }; - - - -/** - * LAN message class - */ -#pragma pack(push, 1) -struct LANMessage -{ - enum Type ///< What kind of message are we? - { - // Locating everybody - MSG_REQUEST_LOCATIONS, ///< Hey, where is everybody? - MSG_GAME_ANNOUNCE, ///< Here I am, and here's my game info! - MSG_LOBBY_ANNOUNCE, ///< Hey, I'm in the lobby! - - // Joining games - MSG_REQUEST_JOIN, ///< Let me in! Let me in! - MSG_JOIN_ACCEPT, ///< Okay, you can join. - MSG_JOIN_DENY, ///< Go away! We don't want any! - - // Leaving games - MSG_REQUEST_GAME_LEAVE, ///< I want to leave the game - MSG_REQUEST_LOBBY_LEAVE,///< I'm leaving the lobby - - // Game options, chat, etc - MSG_SET_ACCEPT, ///< I'm cool with everything as is. - MSG_MAP_AVAILABILITY, ///< I do (not) have the map. - MSG_CHAT, ///< Just spouting my mouth off. - MSG_GAME_START, ///< Hold on; we're starting! - MSG_GAME_START_TIMER, ///< The game will start in N seconds - MSG_GAME_OPTIONS, ///< Here's some info about the game. - MSG_INACTIVE, ///< I've alt-tabbed out. Unaccept me cause I'm a poo-flinging monkey. - - MSG_REQUEST_GAME_INFO, ///< For direct connect, get the game info from a specific IP Address - - // Community Product - MSG_GAME_REQUEST_PRODUCT_INFO = 1000, - MSG_GAME_RESPONSE_PRODUCT_INFO, - MSG_LOBBY_REQUEST_PRODUCT_INFO, - MSG_LOBBY_RESPONSE_PRODUCT_INFO, - MSG_MATCH_REQUEST_PRODUCT_INFO, - MSG_MATCH_RESPONSE_PRODUCT_INFO, - } messageType; - - WideChar name[g_lanPlayerNameLength+1]; ///< My name, for convenience - char userName[g_lanLoginNameLength+1]; ///< login name, for convenience - char hostName[g_lanHostNameLength+1]; ///< machine name, for convenience - - // No additional data is required for REQUEST_LOCATIONS, LOBBY_ANNOUNCE, - // REQUEST_LOBBY_LEAVE, GAME_START. - union - { - // StartTimer is sent with GAME_START_TIMER - struct - { - Int seconds; - } StartTimer; - - // GameJoined is sent with REQUEST_GAME_LEAVE - struct - { - WideChar gameName[g_lanGameNameLength+1]; - } GameToLeave; - - // GameInfo if sent with GAME_ANNOUNCE - struct - { - WideChar gameName[g_lanGameNameLength+1]; - Bool inProgress; - char options[m_lanMaxOptionsLength+1]; - Bool isDirectConnect; - } GameInfo; - - // PlayerInfo is sent with REQUEST_GAME_INFO for direct connect games. - struct - { - UnsignedInt ip; - WideChar playerName[g_lanPlayerNameLength+1]; - } PlayerInfo; - - // GameToJoin is sent with REQUEST_JOIN - struct - { - UnsignedInt gameIP; - UnsignedInt exeCRC; - UnsignedInt iniCRC; - char serial[g_maxSerialLength]; - } GameToJoin; - - // GameJoined is sent with JOIN_ACCEPT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt gameIP; - UnsignedInt playerIP; - Int slotPosition; - } GameJoined; - - // GameNotJoined is sent with JOIN_DENY - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt gameIP; - UnsignedInt playerIP; - LANAPIInterface::ReturnType reason; - } GameNotJoined; - - // Accept is sent with SET_ACCEPT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - Bool isAccepted; - } Accept; - - // Accept is sent with MAP_AVAILABILITY - struct - { - WideChar gameName[g_lanGameNameLength+1]; - UnsignedInt mapCRC; // to make sure we're talking about the same map - Bool hasMap; - } MapStatus; - - // Chat is sent with CHAT - struct - { - WideChar gameName[g_lanGameNameLength+1]; - LANAPIInterface::ChatType chatType; - WideChar message[g_lanMaxChatLength+1]; - } Chat; - - // GameOptions is sent with GAME_OPTIONS - struct - { - char options[m_lanMaxOptionsLength+1]; - } GameOptions; - - // ProductInfo is sent with REQUEST_PRODUCT_INFO and RESPONSE_PRODUCT_INFO - struct - { - UnsignedInt exeCRC; - UnsignedInt iniCRC; - UnsignedInt productVersion; - Char gitShortHash[42]; - WideChar productName[129]; - } ProductInfo; - }; -}; -#pragma pack(pop) - -static_assert(sizeof(LANMessage) <= MAX_PACKET_SIZE); diff --git a/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h index 30d12fbfece..7b9c498bba1 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPICallbacks.h @@ -68,10 +68,10 @@ extern const Color chatSystemColor; extern const Color chatSystemColor; extern const Color acceptTrueColor; extern const Color acceptFalseColor; -extern const Color gameColorPatchVersion; -extern const Color gameInProgressColorPatchVersion; -extern const Color playerColorPatchVersion; -extern const Color playerGrayedColorPatchVersion; +extern const Color gameColorCommunityPatch; +extern const Color gameInProgressColorCommunityPatch; +extern const Color playerColorCommunityPatch; +extern const Color playerGrayedColorCommunityPatch; void lanUpdateSlotList( void ); diff --git a/Core/GameEngine/Include/GameNetwork/LANPlayer.h b/Core/GameEngine/Include/GameNetwork/LANPlayer.h index 1aaf56d64bb..f4e48b98c7e 100644 --- a/Core/GameEngine/Include/GameNetwork/LANPlayer.h +++ b/Core/GameEngine/Include/GameNetwork/LANPlayer.h @@ -35,7 +35,7 @@ class LANPlayer { public: - LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; m_productVersion = 0; } + LANPlayer() { m_name = m_login = m_host = L""; m_lastHeard = 0; m_next = NULL; m_IP = 0; m_productInfoFlags = 0; } // Access functions inline UnicodeString getName( void ) { return m_name; } @@ -52,8 +52,8 @@ class LANPlayer inline void setNext( LANPlayer *next ) { m_next = next; } inline UnsignedInt getIP( void ) { return m_IP; } inline void setIP( UnsignedInt IP ) { m_IP = IP; } - inline void setProductVersion(UnsignedInt productVersion) { m_productVersion = productVersion; } - inline UnsignedInt getProductVersion() const { return m_productVersion; } + inline void setProductInfoFlags(UnsignedInt productInfoFlags) { m_productInfoFlags = productInfoFlags; } + inline UnsignedInt getProductInfoFlags() const { return m_productInfoFlags; } protected: UnicodeString m_name; ///< Player name @@ -62,5 +62,5 @@ class LANPlayer UnsignedInt m_lastHeard; ///< The last time we heard from this player (for timeout purposes) LANPlayer *m_next; ///< Linked list pointer UnsignedInt m_IP; ///< Player's IP - UnsignedInt m_productVersion; ///< Community made product version + UnsignedInt m_productInfoFlags; ///< Community made product information flags }; diff --git a/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp index dafd83bd0a9..e934fded213 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPICallbacks.cpp @@ -52,20 +52,20 @@ extern Bool LANbuttonPushed; //Colors used for the chat dialogs -const Color playerColor = GameMakeColor(255,255,255,255); -const Color gameColor = GameMakeColor(255,255,255,255); -const Color gameInProgressColor = GameMakeColor(128,128,0,255); -const Color chatNormalColor = GameMakeColor(50,215,230,255); -const Color chatActionColor = GameMakeColor(255,0,255,255); -const Color chatLocalNormalColor = GameMakeColor(255,128,0,255); -const Color chatLocalActionColor = GameMakeColor(128,255,255,255); -const Color chatSystemColor = GameMakeColor(255,255,255,255); -const Color acceptTrueColor = GameMakeColor(0,255,0,255); -const Color acceptFalseColor = GameMakeColor(255,0,0,255); -const Color gameColorPatchVersion = GameMakeColor(255,255,0,255); -const Color gameInProgressColorPatchVersion = GameMakeColor(192,192,0,255); -const Color playerColorPatchVersion = gameColorPatchVersion; -const Color playerGrayedColorPatchVersion = gameInProgressColorPatchVersion; +const Color playerColor = GameMakeColor(255,255,255,255); +const Color gameColor = GameMakeColor(255,255,255,255); +const Color gameInProgressColor = GameMakeColor(128,128,0,255); +const Color chatNormalColor = GameMakeColor(50,215,230,255); +const Color chatActionColor = GameMakeColor(255,0,255,255); +const Color chatLocalNormalColor = GameMakeColor(255,128,0,255); +const Color chatLocalActionColor = GameMakeColor(128,255,255,255); +const Color chatSystemColor = GameMakeColor(255,255,255,255); +const Color acceptTrueColor = GameMakeColor(0,255,0,255); +const Color acceptFalseColor = GameMakeColor(255,0,0,255); +const Color gameColorCommunityPatch = GameMakeColor(255,255,0,255); +const Color gameInProgressColorCommunityPatch = GameMakeColor(192,192,0,255); +const Color playerColorCommunityPatch = gameColorCommunityPatch; +const Color playerGrayedColorCommunityPatch = gameInProgressColorCommunityPatch; UnicodeString LANAPIInterface::getErrorStringFromReturnType( ReturnType ret ) @@ -644,8 +644,9 @@ void LANAPI::OnPlayerList( LANPlayer *playerList ) LANPlayer *player = m_lobbyPlayers; while (player) { - // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. - const Color color = (player->getProductVersion() > 0 || m_localIP == player->getIP()) ? playerColorPatchVersion : playerColor; + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using the community patch. + const Color color = (m_localIP == player->getIP() || BitIsSet(player->getProductInfoFlags(), GameSlot::ProductInfo::COMMUNITY_PATCH)) + ? playerColorCommunityPatch : playerColor; const Int addedIndex = GadgetListBoxAddEntryText(listboxPlayers, player->getName(), color, -1, -1); GadgetListBoxSetItemData(listboxPlayers, (void *)player->getIP(),addedIndex, 0 ); diff --git a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 6252f951af6..925f7dc1f52 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -41,14 +41,56 @@ #include "GameNetwork/LANAPICallbacks.h" #include "GameClient/MapUtil.h" +Bool LANAPI::setProductInfoStrings(const UnicodeString(&input)[4], WideChar(&output)[201]) +{ + // concatenate strings separated by null terminators + + size_t curSize = 0; + for (size_t i = 0; i < ARRAY_SIZE(input); ++i) + { + curSize += wcslcpy(output + curSize, input[i].str(), ARRAY_SIZE(output) - curSize) + 1; + if (curSize >= ARRAY_SIZE(output)) + { + return FALSE; + } + } + + return curSize <= ARRAY_SIZE(output); +} + +Bool LANAPI::getProductInfoStrings(const WideChar(&input)[201], UnicodeString*(&output)[4]) +{ + // extract strings separated by null terminators + + for (size_t curSize = 0, i = 0; i < ARRAY_SIZE(output); ++i) + { + output[i]->set(input + curSize); + + curSize += output[i]->getLength() + 1; + if (curSize >= ARRAY_SIZE(input)) + { + for (size_t j = i + 1; j < ARRAY_SIZE(output); ++j) + { + output[j]->clear(); + } + + return FALSE; + } + } + + return TRUE; +} + void LANAPI::setProductInfoFromLocalData(GameSlot *slot) { GameSlot::ProductInfo productInfo; + productInfo.flags = GameSlot::ProductInfo::COMMUNITY_PATCH; productInfo.exeCRC = TheGlobalData->m_exeCRC; productInfo.iniCRC = TheGlobalData->m_iniCRC; - productInfo.productVersion = TheVersion->getVersionNumber(); - productInfo.gitShortHash = TheVersion->getAsciiGitShortHash(); - productInfo.productName = TheVersion->getUnicodeProductString(); + productInfo.productTitle = TheVersion->getUnicodeProductTitle(); + productInfo.productVersion = TheVersion->getUnicodeProductVersion(); + productInfo.productAuthor = TheVersion->getUnicodeProductAuthor(); + productInfo.gitShortHash = TheVersion->getUnicodeGitShortHash(); slot->setProductInfo(productInfo); } @@ -56,26 +98,40 @@ void LANAPI::setProductInfoFromLocalData(GameSlot *slot) void LANAPI::setProductInfoFromMessage(LANMessage *msg, GameSlot *slot) { GameSlot::ProductInfo productInfo; + productInfo.flags = msg->ProductInfo.flags; productInfo.exeCRC = msg->ProductInfo.exeCRC; productInfo.iniCRC = msg->ProductInfo.iniCRC; - productInfo.productVersion = msg->ProductInfo.productVersion; - productInfo.gitShortHash = msg->ProductInfo.gitShortHash; - productInfo.productName = msg->ProductInfo.productName; + + UnicodeString *strings[] + { + &productInfo.productTitle, + &productInfo.productVersion, + &productInfo.productAuthor, + &productInfo.gitShortHash + }; + getProductInfoStrings(msg->ProductInfo.data, strings); slot->setProductInfo(productInfo); } -void LANAPI::sendProductInfoMessage(Int messageType, UnsignedInt senderIP) +void LANAPI::sendProductInfoMessage(LANMessage::Type messageType, UnsignedInt senderIP) { LANMessage msg; fillInLANMessage(&msg); - msg.messageType = (LANMessage::Type)messageType; + msg.messageType = messageType; + msg.ProductInfo.flags = GameSlot::ProductInfo::COMMUNITY_PATCH; msg.ProductInfo.exeCRC = TheGlobalData->m_exeCRC; msg.ProductInfo.iniCRC = TheGlobalData->m_iniCRC; - msg.ProductInfo.productVersion = TheVersion->getVersionNumber(); - strlcpy(msg.ProductInfo.gitShortHash, TheVersion->getAsciiGitShortHash().str(), ARRAY_SIZE(msg.ProductInfo.gitShortHash)); - wcslcpy(msg.ProductInfo.productName, TheVersion->getUnicodeProductString().str(), ARRAY_SIZE(msg.ProductInfo.productName)); + + const UnicodeString strings[] + { + TheVersion->getUnicodeProductTitle(), + TheVersion->getUnicodeProductVersion(), + TheVersion->getUnicodeProductAuthor(), + TheVersion->getUnicodeGitShortHash() + }; + setProductInfoStrings(strings, msg.ProductInfo.data); sendMessage(&msg, senderIP); } @@ -124,7 +180,7 @@ void LANAPI::handleLobbyProductInfoResponse(LANMessage *msg, UnsignedInt senderI return; // a fellow player in the lobby has acknowledged our request for product information - player->setProductVersion(msg->ProductInfo.productVersion); + player->setProductInfoFlags(msg->ProductInfo.flags); // update player list with colored names OnPlayerList(m_lobbyPlayers); diff --git a/Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp b/Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp index 584618c4b3c..40c282b8d7b 100644 --- a/Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANGameInfo.cpp @@ -227,9 +227,9 @@ void LANDisplayGameList( GameWindow *gameListbox, LANGameInfo *gameList ) txtGName.concat(L"]"); } - // TheSuperHackers @feature Caball009 06/11/2025 Set special color for games that are using a patched client version. - const Color color = (gameList->getSlot(0)->getProductInfo().productVersion > 0) - ? ((gameList->isGameInProgress()) ? gameInProgressColorPatchVersion : gameColorPatchVersion) + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for games that are using the community patch. + const Color color = (BitIsSet(gameList->getSlot(0)->getProductInfo().flags, GameSlot::ProductInfo::COMMUNITY_PATCH)) + ? ((gameList->isGameInProgress()) ? gameInProgressColorCommunityPatch : gameColorCommunityPatch) : ((gameList->isGameInProgress()) ? gameInProgressColor : gameColor); const Int addedIndex = GadgetListBoxAddEntryText(gameListbox, txtGName, color, -1, -1); GadgetListBoxSetItemData(gameListbox, (void *)gameList, addedIndex, 0 ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp index 973334d38f4..45f4a6d7d06 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Diplomacy.cpp @@ -573,13 +573,10 @@ void PopulateInGameDiplomacyPopup( void ) } } - // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using a patched client version. - if (slot->isHuman() && slot->getProductInfo().productVersion > 0) + // TheSuperHackers @feature Caball009 06/11/2025 Set special status for players that are using the community patch. + if (slot->isHuman() && BitIsSet(slot->getProductInfo().flags, GameSlot::ProductInfo::COMMUNITY_PATCH)) { - UnicodeString gitShortHash; - gitShortHash.translate(slot->getProductInfo().gitShortHash); - - text.format(L"%s [%s]", text.str(), gitShortHash.str()); + text.format(L"%s [%s]", text.str(), slot->getProductInfo().gitShortHash.str()); } staticTextStatus[rowNum]->winSetEnabledTextColors(frontColor, backColor); diff --git a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp index 80970952479..ba7ab4d8a4d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameNetwork/GUIUtil.cpp @@ -423,11 +423,11 @@ void UpdateSlotList( GameInfo *myGame, GameWindow *comboPlayer[], } if(slot->isHuman()) { - if (myGame->getSlot(i)->getProductInfo().productVersion > 0) + if (BitIsSet(myGame->getSlot(i)->getProductInfo().flags, GameSlot::ProductInfo::COMMUNITY_PATCH)) { - // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using a patched client version. - GadgetComboBoxSetEnabledTextColors(comboPlayer[i], playerColorPatchVersion, GameMakeColor(0, 0, 0, 255)); - GadgetComboBoxSetDisabledTextColors(comboPlayer[i], playerGrayedColorPatchVersion, GameMakeColor(0, 0, 0, 255)); + // TheSuperHackers @feature Caball009 06/11/2025 Set special color for players that are using the community patch. + GadgetComboBoxSetEnabledTextColors(comboPlayer[i], playerColorCommunityPatch, GameMakeColor(0, 0, 0, 255)); + GadgetComboBoxSetDisabledTextColors(comboPlayer[i], playerGrayedColorCommunityPatch, GameMakeColor(0, 0, 0, 255)); } UnicodeString newName = slot->getName(); From c07857dfdf40b57dea7b64e7ecd76feb847c3f4e Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:15:14 +0100 Subject: [PATCH 15/17] Fixed static assert warning. --- Core/GameEngine/Include/GameNetwork/LANAPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/GameEngine/Include/GameNetwork/LANAPI.h b/Core/GameEngine/Include/GameNetwork/LANAPI.h index 34ddd8183b7..9159375b0b3 100644 --- a/Core/GameEngine/Include/GameNetwork/LANAPI.h +++ b/Core/GameEngine/Include/GameNetwork/LANAPI.h @@ -286,7 +286,7 @@ struct LANMessage }; #pragma pack(pop) -static_assert(sizeof(LANMessage) <= MAX_PACKET_SIZE); +static_assert(sizeof(LANMessage) <= MAX_PACKET_SIZE, "LANMessage struct cannot be larger than the max packet size"); /** From 8feddcd767e71f2fdeeee2e1443903bcaa270acc Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:15:50 +0100 Subject: [PATCH 16/17] Fixed extended initializer lists syntax for C++98. --- Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 925f7dc1f52..c04961e8816 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -102,7 +102,7 @@ void LANAPI::setProductInfoFromMessage(LANMessage *msg, GameSlot *slot) productInfo.exeCRC = msg->ProductInfo.exeCRC; productInfo.iniCRC = msg->ProductInfo.iniCRC; - UnicodeString *strings[] + UnicodeString *strings[] = { &productInfo.productTitle, &productInfo.productVersion, @@ -124,7 +124,7 @@ void LANAPI::sendProductInfoMessage(LANMessage::Type messageType, UnsignedInt se msg.ProductInfo.exeCRC = TheGlobalData->m_exeCRC; msg.ProductInfo.iniCRC = TheGlobalData->m_iniCRC; - const UnicodeString strings[] + const UnicodeString strings[] = { TheVersion->getUnicodeProductTitle(), TheVersion->getUnicodeProductVersion(), From f91a0587ac33646c8368d122023b7dcdedeff212 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 7 Dec 2025 18:12:35 +0100 Subject: [PATCH 17/17] Improved LANAPI::LookupGameByHost to return latest game. --- Core/GameEngine/Source/GameNetwork/LANAPI.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Core/GameEngine/Source/GameNetwork/LANAPI.cpp b/Core/GameEngine/Source/GameNetwork/LANAPI.cpp index 44e9dfbf886..8d3b9c2efd7 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPI.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPI.cpp @@ -1137,14 +1137,23 @@ LANGameInfo * LANAPI::LookupGameByListOffset( Int offset ) LANGameInfo * LANAPI::LookupGameByHost( UnsignedInt hostIP ) { - LANGameInfo* theGame = m_games; + LANGameInfo *latestGame = m_games; + LANGameInfo *theGame = m_games; - while (theGame && theGame->getHostIP() != hostIP) + // search through games to find the last game from the host in case there are multiple + while (theGame) { + if (theGame->getHostIP() == hostIP && theGame->getLastHeard() > latestGame->getLastHeard()) + latestGame = theGame; + theGame = theGame->getNext(); } - return theGame; // NULL means we didn't find anything. + // sanity check to verify if latest game actually exists and belongs to the host + if (latestGame && latestGame->getHostIP() == hostIP) + return latestGame; + + return NULL; } void LANAPI::removeGame( LANGameInfo *game )