From 6c8d33686362b2c06dbccfa07a296148c112baa6 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:35:24 +0200 Subject: [PATCH 1/6] tweak(msg): Reduce number of MSG_DESTROY_SELECTED_GROUP messages. --- .../Code/GameEngine/Include/Common/Player.h | 3 +++ .../GameEngine/Include/GameClient/InGameUI.h | 2 +- .../GameEngine/Source/Common/RTS/Player.cpp | 5 +++++ .../GameEngine/Source/GameClient/InGameUI.cpp | 18 ++++++++---------- .../GameClient/MessageStream/SelectionXlat.cpp | 4 +++- 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Player.h b/GeneralsMD/Code/GameEngine/Include/Common/Player.h index 5808a9bfa6a..1b3b268a7c7 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Player.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Player.h @@ -645,6 +645,9 @@ class Player : public Snapshot // adds the given AIGroup to the current selection of this player. void addAIGroupToCurrentSelection(AIGroup *group); + // returns false if player has object(s) currently selected + Bool isCurrentlySelectedGroupEmpty() const; + // return the requested hotkey squad Squad *getHotkeySquad(Int squadNumber); diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h index 675fd35f8de..097e2752982 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/InGameUI.h @@ -448,7 +448,7 @@ friend class Drawable; // for selection/deselection transactions // Drawable selection mechanisms virtual void selectDrawable( Drawable *draw ); ///< Mark given Drawable as "selected" virtual void deselectDrawable( Drawable *draw ); ///< Clear "selected" status from Drawable - virtual void deselectAllDrawables( Bool postMsg = true ); ///< Clear the "select" flag from all drawables + virtual void deselectAllDrawables( Bool updateGameLogic = true ); ///< Clear the "select" flag from all drawables virtual Int getSelectCount() { return m_selectCount; } ///< Get count of currently selected drawables virtual Int getMaxSelectCount() { return m_maxSelectCount; } ///< Get the max number of selected drawables virtual UnsignedInt getFrameSelectionChanged() { return m_frameSelectionChanged; } ///< Get the max number of selected drawables diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index 8a509789ee8..86abe451516 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -3819,6 +3819,11 @@ void Player::addAIGroupToCurrentSelection(AIGroup *group) { } } +Bool Player::isCurrentlySelectedGroupEmpty() const +{ + return m_currentSelection->getSizeOfGroup() == 0; +} + //------------------------------------------------------------------------------------------------- /** addTypeOfProductionCostChange adds a production change to the typeof list */ //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 5f3a8b18b27..291b5e4965a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -3553,7 +3553,7 @@ void InGameUI::deselectDrawable( Drawable *draw ) //------------------------------------------------------------------------------------------------- /** Clear all drawables' "select" status */ //------------------------------------------------------------------------------------------------- -void InGameUI::deselectAllDrawables( Bool postMsg ) +void InGameUI::deselectAllDrawables( Bool updateGameLogic ) { const DrawableList *selected = getAllSelectedDrawables(); @@ -3576,16 +3576,14 @@ void InGameUI::deselectAllDrawables( Bool postMsg ) // our selection can no longer consist of exactly one angry mob m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID; - - ///@todo don't we want to not emit this message if there wasn't a group at all? (CBD) - /** @todo also, we probably are sending this message too much, we should come up with - some kind of "selections are dirty" status that we can check once per frame and send - the correct group info over the network ... could be tricky tho (or impossible) given - the order of operations of things happening in the code (CBD) */ - if( postMsg ) + if (updateGameLogic) { - // TheSuperHackers @tweak Originally this message had one boolean argument, but it wasn't used for anything. - TheMessageStream->appendMessage( GameMessage::MSG_DESTROY_SELECTED_GROUP ); + // TheSuperHackers @tweak Avoid sending this message when no objects are currently selected. + if (!ThePlayerList->getLocalPlayer()->isCurrentlySelectedGroupEmpty()) + { + // TheSuperHackers @tweak Originally this message had one boolean argument, but it wasn't used for anything. + TheMessageStream->appendMessage(GameMessage::MSG_DESTROY_SELECTED_GROUP); + } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp index 3bd19e0762e..a8342902982 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp @@ -750,7 +750,9 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa { if (!addToGroup) { - deselectAll(); + // TheSuperHackers @tweak Update the game client but not the game logic. + // It isn't required for the latter, as a new group will override the current selection. + TheInGameUI->deselectAllDrawables(FALSE); } GameMessage *newMsg = TheMessageStream->appendMessage(GameMessage::MSG_CREATE_SELECTED_GROUP); From db6a8e6db6414d872b377cda74721801f45373e0 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 1 May 2026 01:10:18 +0200 Subject: [PATCH 2/6] Removed function 'deselectAll'. --- .../GameClient/MessageStream/SelectionXlat.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp index a8342902982..5836b4fdea4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp @@ -216,18 +216,6 @@ static Bool canSelectWrapper( Drawable *draw, void *userData ) return CanSelectDrawable( draw, dragSelecting ); } -//----------------------------------------------------------------------------- -/** - * Deselect all drawables, and emit a "TEAM_DESTROY" message, since - * the "team" was the group of currently selected units. - */ -static void deselectAll() -{ - - // deselect it all - TheInGameUI->deselectAllDrawables(); -} - //----------------------------------------------------------------------------- /** * Select the given drawable, without playing its sound. @@ -237,7 +225,7 @@ static Bool selectSingleDrawableWithoutSound( Drawable *draw ) { // since we are single selecting a drawable, unselect everything else - deselectAll(); + TheInGameUI->deselectAllDrawables(); // do the drawable selection TheInGameUI->selectDrawable( draw ); @@ -953,7 +941,7 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa { if( !TheInGameUI->getPreventLeftClickDeselectionInAlternateMouseModeForOneClick() ) { - deselectAll(); + TheInGameUI->deselectAllDrawables(); m_lastGroupSelGroup = -1; } else @@ -1020,7 +1008,7 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa else if (!TheGlobalData->m_useAlternateMouse) { //No GUI command mode, so deselect everyone if we're in regular mouse mode. - deselectAll(); + TheInGameUI->deselectAllDrawables(); } } } From 4c917cdcae97e450abdbcac2a9b9b158e239b81f Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 1 May 2026 01:12:50 +0200 Subject: [PATCH 3/6] Updated other callsites of `InGameUI::deselectAllDrawables` when used before new group creation. --- .../GUI/ControlBar/ControlBarCommandProcessing.cpp | 2 +- .../Code/GameEngine/Source/GameClient/InGameUI.cpp | 2 +- .../Source/GameClient/MessageStream/CommandXlat.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp index 5e2c41a3d48..e9f3f5664ce 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/ControlBar/ControlBarCommandProcessing.cpp @@ -646,7 +646,7 @@ CBCommandStatus ControlBar::processCommandUI( GameWindow *control, } //deselect other units - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // create a new group. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 291b5e4965a..1f85b61d179 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -5886,7 +5886,7 @@ void InGameUI::selectNextIdleWorker() if(selectThisObject) { DEBUG_ASSERTCRASH(selectThisObject->getContainedBy() == nullptr, ("InGameUI::selectNextIdleWorker Selected idle object should not be contained")); - deselectAllDrawables(); + deselectAllDrawables(FALSE); GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 830faa70ac9..a6bcf3eca1b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -2585,7 +2585,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if(newDrawable != nullptr ) { //deselect other units - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // create a new group. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); @@ -2701,7 +2701,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if(newDrawable != nullptr ) { //deselect other units - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // select the unit // create a new group. @@ -2814,7 +2814,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if(newDrawable != nullptr ) { //deselect other units - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // select the unit // create a new group. @@ -2929,7 +2929,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if(newDrawable != nullptr ) { //deselect other units - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // select the unit // create a new group. @@ -2982,7 +2982,7 @@ GameMessageDisposition CommandTranslator::translateGameMessage(const GameMessage if ( heroDraw == nullptr ) break; - TheInGameUI->deselectAllDrawables(); + TheInGameUI->deselectAllDrawables(FALSE); // create a new group. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP ); From 445319173cb03ec4b791844e94d3aa91e8cb9187 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Fri, 1 May 2026 01:16:24 +0200 Subject: [PATCH 4/6] Removed TSH comment. --- .../Source/GameClient/MessageStream/SelectionXlat.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp index 5836b4fdea4..484ebdfe945 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp @@ -738,8 +738,6 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa { if (!addToGroup) { - // TheSuperHackers @tweak Update the game client but not the game logic. - // It isn't required for the latter, as a new group will override the current selection. TheInGameUI->deselectAllDrawables(FALSE); } From 8c156f60f6657909f2e0776e623f26172fad7b3b Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 3 May 2026 19:34:39 +0200 Subject: [PATCH 5/6] Added additional check for selected drawables. To avoid issues with fast deselection when the game's paused, or when the runahead is significant in multiplayer. --- GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 1f85b61d179..25ec31bd9f4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -3556,6 +3556,7 @@ void InGameUI::deselectDrawable( Drawable *draw ) void InGameUI::deselectAllDrawables( Bool updateGameLogic ) { const DrawableList *selected = getAllSelectedDrawables(); + const Bool emptyDrawableSelection = selected->empty(); // loop through all the selected drawables for ( DrawableListCIt it = selected->begin(); it != selected->end(); ) @@ -3579,7 +3580,7 @@ void InGameUI::deselectAllDrawables( Bool updateGameLogic ) if (updateGameLogic) { // TheSuperHackers @tweak Avoid sending this message when no objects are currently selected. - if (!ThePlayerList->getLocalPlayer()->isCurrentlySelectedGroupEmpty()) + if (!emptyDrawableSelection || !ThePlayerList->getLocalPlayer()->isCurrentlySelectedGroupEmpty()) { // TheSuperHackers @tweak Originally this message had one boolean argument, but it wasn't used for anything. TheMessageStream->appendMessage(GameMessage::MSG_DESTROY_SELECTED_GROUP); From fcb6ef13b4235d39fade44c7871d621479cdceb2 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 3 May 2026 19:56:39 +0200 Subject: [PATCH 6/6] Added logic to avoid double deselection on left mouse click. --- GeneralsMD/Code/GameEngine/Include/Common/Player.h | 3 --- .../GameEngine/Include/GameClient/SelectionXlat.h | 1 + .../Code/GameEngine/Source/Common/RTS/Player.cpp | 5 ----- .../Code/GameEngine/Source/GameClient/InGameUI.cpp | 6 +++--- .../GameClient/MessageStream/SelectionXlat.cpp | 12 +++++++++++- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/Common/Player.h b/GeneralsMD/Code/GameEngine/Include/Common/Player.h index 1b3b268a7c7..5808a9bfa6a 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/Player.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/Player.h @@ -645,9 +645,6 @@ class Player : public Snapshot // adds the given AIGroup to the current selection of this player. void addAIGroupToCurrentSelection(AIGroup *group); - // returns false if player has object(s) currently selected - Bool isCurrentlySelectedGroupEmpty() const; - // return the requested hotkey squad Squad *getHotkeySquad(Int squadNumber); diff --git a/GeneralsMD/Code/GameEngine/Include/GameClient/SelectionXlat.h b/GeneralsMD/Code/GameEngine/Include/GameClient/SelectionXlat.h index bf9900a9f42..22256d85dc7 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameClient/SelectionXlat.h +++ b/GeneralsMD/Code/GameEngine/Include/GameClient/SelectionXlat.h @@ -42,6 +42,7 @@ class SelectionTranslator : public GameMessageTranslator friend Bool killThemKillThemAllWrapper( Drawable *draw, void *userData ); private: + Bool m_pendingDeselection; Bool m_leftMouseButtonIsDown; Bool m_dragSelecting; UnsignedInt m_lastGroupSelTime; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index 86abe451516..8a509789ee8 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -3819,11 +3819,6 @@ void Player::addAIGroupToCurrentSelection(AIGroup *group) { } } -Bool Player::isCurrentlySelectedGroupEmpty() const -{ - return m_currentSelection->getSizeOfGroup() == 0; -} - //------------------------------------------------------------------------------------------------- /** addTypeOfProductionCostChange adds a production change to the typeof list */ //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp index 25ec31bd9f4..5627ba3d2a6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/InGameUI.cpp @@ -3556,7 +3556,7 @@ void InGameUI::deselectDrawable( Drawable *draw ) void InGameUI::deselectAllDrawables( Bool updateGameLogic ) { const DrawableList *selected = getAllSelectedDrawables(); - const Bool emptyDrawableSelection = selected->empty(); + const Bool emptySelectedDrawables = selected->empty(); // loop through all the selected drawables for ( DrawableListCIt it = selected->begin(); it != selected->end(); ) @@ -3579,8 +3579,8 @@ void InGameUI::deselectAllDrawables( Bool updateGameLogic ) if (updateGameLogic) { - // TheSuperHackers @tweak Avoid sending this message when no objects are currently selected. - if (!emptyDrawableSelection || !ThePlayerList->getLocalPlayer()->isCurrentlySelectedGroupEmpty()) + // TheSuperHackers @tweak Only send this message when objects are currently selected. + if (!emptySelectedDrawables) { // TheSuperHackers @tweak Originally this message had one boolean argument, but it wasn't used for anything. TheMessageStream->appendMessage(GameMessage::MSG_DESTROY_SELECTED_GROUP); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp index 484ebdfe945..bbda06ad681 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp @@ -249,6 +249,7 @@ SelectionTranslator *TheSelectionTranslator = nullptr; //----------------------------------------------------------------------------- SelectionTranslator::SelectionTranslator() { + m_pendingDeselection = FALSE; m_leftMouseButtonIsDown = FALSE; m_dragSelecting = FALSE; m_lastGroupSelTime = 0; @@ -738,6 +739,7 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa { if (!addToGroup) { + m_pendingDeselection = FALSE; TheInGameUI->deselectAllDrawables(FALSE); } @@ -939,7 +941,7 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa { if( !TheInGameUI->getPreventLeftClickDeselectionInAlternateMouseModeForOneClick() ) { - TheInGameUI->deselectAllDrawables(); + m_pendingDeselection = TRUE; m_lastGroupSelGroup = -1; } else @@ -1318,6 +1320,14 @@ GameMessageDisposition SelectionTranslator::translateGameMessage(const GameMessa #endif } + // TheSuperHackers @tweak Avoid double deselection when selecting a new object with another object selected, + // originally triggered by RAW_MOUSE_LEFT_BUTTON_UP and MOUSE_LEFT_CLICK, respectively. + if (msg->getType() == GameMessage::MSG_MOUSE_LEFT_CLICK && m_pendingDeselection) + { + m_pendingDeselection = FALSE; + TheInGameUI->deselectAllDrawables(); + } + return disp; }