Skip to content

refactor(gamelogic): Break switch cases of GameLogic::logicMessageDispatcher into separate functions#2702

Open
xezon wants to merge 10 commits into
TheSuperHackers:mainfrom
xezon:xezon/refactor-gamelogicdispatch
Open

refactor(gamelogic): Break switch cases of GameLogic::logicMessageDispatcher into separate functions#2702
xezon wants to merge 10 commits into
TheSuperHackers:mainfrom
xezon:xezon/refactor-gamelogicdispatch

Conversation

@xezon
Copy link
Copy Markdown

@xezon xezon commented May 12, 2026

This change breaks all switch cases of GameLogic::logicMessageDispatcher into separate functions for ease of readability and maintainability.

The logic is unchanged.

TODO

  • Replicate in Generals

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Refactor Edits the code with insignificant behavior changes, is never user facing labels May 12, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 12, 2026

Greptile Summary

This PR extracts all switch cases of GameLogic::logicMessageDispatcher into individual on* member functions for readability and maintainability. The function implementations are in GameLogicDispatch.cpp and the declarations are added to both Generals/GameLogic.h and GeneralsMD/GameLogic.h. The PR also moves #include "GameLogic/AI.h" from the .cpp into both headers since AIGroupPtr is now used in function signatures, and introduces a getMessagePlayer static helper to reduce duplication.

  • ~50 extracted functions each correspond to one message type handled by the dispatcher; logic is preserved verbatim with break replaced by return false/return true as appropriate.
  • Issues flagged in a previous review round — missing msgPlayer declarations inside #if !RETAIL_COMPATIBLE_CRC blocks, missing AIGroupPtr& parameter in ALLOW_SURRENDER functions, and the Generals/GameLogic.h signature mismatch — are all addressed in this revision.

Confidence Score: 5/5

Safe to merge — this is a pure mechanical extraction refactor with no changes to game logic.

Every extracted function is a direct transcription of its corresponding switch-case body. The previously flagged compile errors (missing msgPlayer declarations in non-retail CRC builds, missing AIGroupPtr parameter in ALLOW_SURRENDER functions, Generals header signature mismatch) are all resolved in this revision. The onLogicCrc early-exit path uses return false instead of break, which is semantically equivalent since the switch dispatcher always executes its own break after the call. Include dependencies are correctly updated in both header files.

No files require special attention.

Important Files Changed

Filename Overview
Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp ~1600 lines of switch-case body moved into ~50 extracted member functions; logic is faithful to the original. Previously flagged issues (missing msgPlayer, missing currentlySelectedGroup param in ALLOW_SURRENDER functions, onLogicCrc early-exit) are all resolved. The static getMessagePlayer helper is correctly used.
Generals/Code/GameEngine/Include/GameLogic/GameLogic.h Adds declarations for all extracted on* methods including the three ALLOW_SURRENDER functions with the correct AIGroupPtr& parameter; adds #include "GameLogic/AI.h" required for AIGroupPtr. Matches the GeneralsMD header.
GeneralsMD/Code/GameEngine/Include/GameLogic/GameLogic.h Mirrors the Generals header: adds declarations for all extracted on* methods with correct signatures and adds #include "GameLogic/AI.h". Unchanged from the correct version used as the reference in the prior review.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["logicMessageDispatcher(msg)"] --> B["getMessagePlayer(msg)\nvalidate msgPlayer != null"]
    B --> C["AIGroup selection setup"]
    C --> D["switch(msgType)"]
    D --> E1["MSG_NEW_GAME → onNewGame(msg)"]
    D --> E2["MSG_CLEAR_GAME_DATA → onClearGameData(msg, grp)"]
    D --> E3["MSG_SET_RALLY_POINT → onSetRallyPoint(msg)"]
    D --> E4["MSG_DO_SPECIAL_POWER → onDoSpecialPower(msg, grp)"]
    D --> E5["MSG_DO_SPECIAL_POWER_AT_LOCATION → onDoSpecialPowerAtLocation(msg, grp)"]
    D --> E6["MSG_DO_SPECIAL_POWER_AT_OBJECT → onDoSpecialPowerAtObject(msg, grp)"]
    D --> E7["MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION → onDoSpecialPowerOverrideDestination(msg, grp)"]
    D --> E8["MSG_ENTER / MSG_EXIT → onEnter / onExit(msg, grp)"]
    D --> E9["MSG_LOGIC_CRC → onLogicCrc(msg)"]
    D --> E10["MSG_DOZER_CONSTRUCT → onDozerConstruct(msg, grp)"]
    D --> E11["~40 more cases..."]
    E1 --> F["return bool\n(ignored at call site)"]
    E9 --> F
    F --> G["Post-switch:\nRETAIL_COMPATIBLE_AIGROUP\ngroup cleanup"]
Loading

Reviews (6): Last reviewed commit: "Fix line spacing" | Re-trigger Greptile

@xezon
Copy link
Copy Markdown
Author

xezon commented May 12, 2026

Generals fails to compile until replicated.

Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Comment thread Generals/Code/GameEngine/Include/GameLogic/GameLogic.h
@Caball009
Copy link
Copy Markdown

Do we need the braces per case? It makes the function a lot longer.

@xezon
Copy link
Copy Markdown
Author

xezon commented May 13, 2026

Do we need the braces per case? It makes the function a lot longer.

Is not strictly necessary. Braces are only needed if a variable needs declaration in the top switch case scope. Do you want it removed?

@xezon xezon force-pushed the xezon/refactor-gamelogicdispatch branch from 91c7c5d to 5d14b30 Compare May 13, 2026 19:41
@Caball009
Copy link
Copy Markdown

I don't have a strong opinion on it either way, but I lean toward removing them to make the function shorter.

@xezon
Copy link
Copy Markdown
Author

xezon commented May 22, 2026

Needs rebase.

@xezon xezon force-pushed the xezon/refactor-gamelogicdispatch branch from 5d14b30 to 883093b Compare May 22, 2026 19:19
@xezon
Copy link
Copy Markdown
Author

xezon commented May 22, 2026

Rebased without conflicts.

return true;
}

bool GameLogic::onDoWeapon(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is currentlySelectedGroup passed as AIGroup*& everywhere?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because some of them do actually release the AI group and set the pointer to null. To keep the code simple, I passed it as reference to all of them. It reduces the likelyhood of mistakes this way, because we then do not need to verify that all the correct functions get it as reference.

Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp Outdated
Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp Outdated
return true;
}

bool GameLogic::onInternetHack(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

Copy link
Copy Markdown
Author

@xezon xezon May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok but that is fine. We can keep msg anyway to keep it consistent with all the others. Plus there is a commented code line that uses msg.

Many times in this class.

return true;
}

bool GameLogic::onDoCheer(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

return true;
}

bool GameLogic::onEvacuate(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

return true;
}

bool GameLogic::onExecuteRailedTransport(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

return true;
}

bool GameLogic::onToggleOvercharge(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

return true;
}

bool GameLogic::onReturnToPrison(GameMessage *msg, AIGroupPtr &currentlySelectedGroup)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMessage *msg is unused.

Copy link
Copy Markdown

@Caball009 Caball009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete review.

  1. There's a bunch of unused function parameters. Could remove or mark with MAYBE_UNUSED.
  2. Player *msgPlayer = getMessagePlayer(msg) can be made const in some functions.
  3. currentlySelectedGroup can be passed by pointer instead of pointer ref, I think.

Comment on lines 735 to 738
case GameMessage::MSG_SELECTED_GROUP_COMMAND:
{

break;

}
Copy link
Copy Markdown

@Caball009 Caball009 May 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case could be removed technically.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will keep it because the original had it too.

Comment thread Core/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp
@xezon
Copy link
Copy Markdown
Author

xezon commented May 25, 2026

  1. There's a bunch of unused function parameters. Could remove or mark with MAYBE_UNUSED.

I would like to keep passing all the message pointers because it is the common expectation that the message data is needed to properly act on a message - when it has message arguments.

  1. Player *msgPlayer = getMessagePlayer(msg) can be made const in some functions.

I checked and would like to keep it as is, because some functions do use it in const context but the constness is not consistently applied to all getter methods across the code base. I think that would need a polishing pass first.

  1. currentlySelectedGroup can be passed by pointer instead of pointer ref, I think.

I looked over it one more time and it strictly needs passing as reference only to onClearGameData. We could remove it from the others but that then would mean for the non retail compatible code it would Add_Ref and Release_Ref on function calls, because of RefCountPtr<AIGroup>. I think we can keep passing by reference here. It is ok.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants