Skip to content

fix(logic): Add nullptr check to thisPlayer in GameLogic::logicMessageDispatcher#2383

Open
Caball009 wants to merge 5 commits intoTheSuperHackers:mainfrom
Caball009:fix_thisPlayer_logicMessageDispatcher
Open

fix(logic): Add nullptr check to thisPlayer in GameLogic::logicMessageDispatcher#2383
Caball009 wants to merge 5 commits intoTheSuperHackers:mainfrom
Caball009:fix_thisPlayer_logicMessageDispatcher

Conversation

@Caball009
Copy link

This PR changes the nullptr assertion on thisPlayer in GameLogic::logicMessageDispatcher to an early return. Although there are a couple of places in this function that check explicitly for it, a lot of code assumes that this pointer is not null, so it makes sense to check that up front.

Commit 1 adds the check.
Commit 2 removes code that's become redundant (implicitly).
Commit 3 changes msg->getPlayerIndex to thisPlayer->getPlayerIndex for the sole purpose of consistency with code that already uses the latter.

TODO:

  • Replicate in Generals.

@Caball009 Caball009 added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour labels Mar 2, 2026
@Mauller
Copy link

Mauller commented Mar 3, 2026

on 645 we have

case GameMessage::MSG_ENABLE_RETALIATION_MODE:
{
	//Logically turns on or off retaliation mode for a specified player.
	Int playerIndex = msg->getArgument( 0 )->integer;
	Bool enableRetaliation = msg->getArgument( 1 )->boolean;

	Player *player = ThePlayerList->getNthPlayer( playerIndex );
	if( player )
	{
		player->setLogicalRetaliationModeEnabled( enableRetaliation );
	}
	break;
}

I wonder if this needs clarifying that player here is a targetPlayer and not the same as thisPlayer

I am guessing that Int playerIndex = msg->getArgument( 0 )->integer; returns a different value compared to msg->getPlayerID

@stephanmeesters
Copy link

stephanmeesters commented Mar 3, 2026

I wonder if this needs clarifying that player here is a targetPlayer and not the same as thisPlayer

I am guessing that Int playerIndex = msg->getArgument( 0 )->integer; returns a different value compared to msg->getPlayerID

I have a PR planned for this one which makes the change that player = thisPlayer, which seemed to pass 1k replays, but wanted to test that a bit more

@greptile-apps
Copy link

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR hardens GameLogic::logicMessageDispatcher by replacing the debug-only DEBUG_ASSERTCRASH on thisPlayer with an upfront nullptr guard that issues a DEBUG_CRASH and early-returns, making the function safe in both debug and release builds. Follow-up commits remove the now-redundant per-case null checks and standardise msg->getPlayerIndex() calls to thisPlayer->getPlayerIndex() for consistency.

Key changes:

  • Upfront guard (GameLogicDispatch.cpp:353–357): thisPlayer == nullptr now causes an early return instead of an assertion-crash, closing the gap for release builds where the assertion was a no-op.
  • Redundant check removal: Eliminates local player variables and per-case null checks in MSG_CREATE_SELECTED_GROUP, MSG_REMOVE_FROM_SELECTED_GROUP, MSG_DESTROY_SELECTED_GROUP, MSG_CREATE_TEAM*, MSG_SELECT_TEAM*, MSG_ADD_TEAM*, and the MSG_PURCHASE_SCIENCE sanity condition.
  • Consistency cleanup: Replaces msg->getPlayerIndex() with thisPlayer->getPlayerIndex() in MSG_SELF_DESTRUCT loop comparison and MSG_LOGIC_CRC cache indexing.
  • As noted in the PR TODO, the equivalent change still needs to be replicated in the Generals/ directory.

Confidence Score: 5/5

  • This PR is safe to merge — the changes are logically sound, well-scoped, and reduce crash risk in release builds.
  • The upfront guard is the correct approach and was clearly needed given how much of the function assumed thisPlayer was non-null. All removed null checks are provably redundant after the early return. The msg->getPlayerIndex() vs thisPlayer->getPlayerIndex() substitutions are semantically equivalent since thisPlayer is looked up via msg->getPlayerIndex() in the first place. No custom rules are violated by the introduced code.
  • No files require special attention.

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp Adds an upfront nullptr guard for thisPlayer with an early return, then removes now-redundant per-case null checks across MSG_CREATE_SELECTED_GROUP, MSG_REMOVE_FROM_SELECTED_GROUP, MSG_DESTROY_SELECTED_GROUP, MSG_CREATE_TEAM*, MSG_SELECT_TEAM*, MSG_ADD_TEAM*, and MSG_PURCHASE_SCIENCE. Also replaces scattered msg->getPlayerIndex() calls with thisPlayer->getPlayerIndex() for consistency.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[logicMessageDispatcher called] --> B[Resolve thisPlayer via\nThePlayerList->getNthPlayer]
    B --> C{thisPlayer == nullptr?}
    C -- Yes --> D[DEBUG_CRASH + early return]
    C -- No --> E[Setup currentlySelectedGroup\nif in-game network msg]
    E --> F[Switch on msgType]
    F --> G[MSG_CREATE_SELECTED_GROUP\nuses thisPlayer->getPlayerMask]
    F --> H[MSG_REMOVE_FROM_SELECTED_GROUP\nuses thisPlayer->getPlayerMask]
    F --> I[MSG_DESTROY_SELECTED_GROUP\nsetCurrentlySelectedAIGroup]
    F --> J[MSG_CREATE/SELECT/ADD_TEAM*\nuses thisPlayer directly]
    F --> K[MSG_LOGIC_CRC\ncached via thisPlayer->getPlayerIndex]
    F --> L[MSG_PURCHASE_SCIENCE\nsanity: SCIENCE_INVALID only]
    F --> M[... other cases ...]
Loading

Last reviewed commit: 7c8b814

Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

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

Nice cleanup. But there is CRC mismatch risk.

Also there are 2 more msg->getPlayerIndex() in commented and compiled out code.

for (; i<ThePlayerList->getPlayerCount(); ++i)
{
if (i != msg->getPlayerIndex())
if (i != thisPlayer->getPlayerIndex())
Copy link

Choose a reason for hiding this comment

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

Instead of doing thisPlayer->getPlayerIndex() you could also add a const int playerIndex = msg->getPlayerIndex() at the begin of the function and then use that.

Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

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

Very hot. Needs replication in Generals.

@xezon xezon added the Fix Is fixing something, but is not user facing label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants