Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,13 @@ void AIStateMachine::clear()
m_goalWaypoint = nullptr;
m_goalSquad = nullptr;

#if !RETAIL_COMPATIBLE_CRC

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Better also put it behind a PRESERVE_... macro.

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 didn't do this because the temporary state reset logic is not necessarily exclusive to the issue at hand. I could make it more broad/generic and have something like PRESERVE_UNINTERRUPTIBLE_TEMP_AI_STATES, but that's not really indicative of the bug it's targeting.

if (m_temporaryState)
m_temporaryState->onExit(EXIT_RESET);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Any idea if this could also affect some other ai behaviors? State is really vast, used by many different things.

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.

The only other setTemporaryState cases are AI_FOLLOW_EXITPRODUCTION_PATH (only when AI player exits an attacked Tunnel), AI_MOVE_OUT_OF_THE_WAY, and several AI_BUSY cases in the deploy logic. I've been unable to find any issues or regressions with these states.


m_temporaryState = nullptr;
Comment on lines +1020 to +1023

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Temporary state double-exit 🐞 Bug ☼ Reliability

When !RETAIL_COMPATIBLE_CRC is enabled, AIStateMachine::clear() calls
m_temporaryState->onExit(EXIT_RESET) after StateMachine::clear() already exits the current
state; because states are singletons per StateID, setting a temporary state to the current StateID
would invoke onExit() twice on the same State instance.
Agent Prompt
## Issue description
`AIStateMachine::clear()` now exits the temporary state after calling `StateMachine::clear()`. If `m_temporaryState` aliases the state that was current (same StateID), the same `State` object can receive `onExit(EXIT_RESET)` twice.

## Issue Context
`StateMachine` stores exactly one `State*` per `StateID` and returns that same pointer from `internalGetState()`. Both `setState()` and `AIStateMachine::setTemporaryState()` use this mechanism, so same-ID implies same instance.

## Fix Focus Areas
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[344-363]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[483-525]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[914-961]

## Suggested change
- Capture `StateID oldCurrent = getCurrentStateID();` before calling `StateMachine::clear()`.
- After `StateMachine::clear()`, only call `m_temporaryState->onExit(EXIT_RESET)` if `m_temporaryState != nullptr` AND `m_temporaryState->getID() != oldCurrent`.
- Always set `m_temporaryState = nullptr` (and optionally reset `m_temporaryStateFramEnd = 0`) afterward.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

#endif

AIUpdateInterface* ai = getOwner()->getAI();
if (ai)
ai->friend_notifyStateMachineChanged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,9 @@ void OpenContain::scatterToNearbyPosition(Object* rider)
// set position of the object at center of building and move them toward pos
rider->setPosition( theContainer->getPosition() );
ai->ignoreObstacle(theContainer);
#if !RETAIL_COMPATIBLE_CRC
ai->friend_setGoalObject(nullptr);
#endif
ai->aiMoveToPosition( &pos, CMD_FROM_AI );

}
Expand Down
7 changes: 7 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,13 @@ void AIStateMachine::clear()
m_goalWaypoint = nullptr;
m_goalSquad = nullptr;

#if !RETAIL_COMPATIBLE_CRC
if (m_temporaryState)
m_temporaryState->onExit(EXIT_RESET);

Comment on lines +1024 to +1027

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Evacuation fix behind retail_compatible_crc 📎 Requirement gap ≡ Correctness

The new evac/command-responsiveness cleanup that clears m_temporaryState / the AI goal object is
wrapped in #if !RETAIL_COMPATIBLE_CRC, but this repo defaults RETAIL_COMPATIBLE_CRC to 1, so
default (retail-compatible) builds won’t include the fix and the reported bug may remain. This risks
failing the requirement that infantry immediately obey attack commands after emerging from destroyed
fireport vehicles.
Agent Prompt
## Issue description
The responsiveness bugfix is currently compiled out in the default configuration because the new cleanup/goal-reset logic is guarded by `#if !RETAIL_COMPATIBLE_CRC` while `RETAIL_COMPATIBLE_CRC` defaults to `1` in this repo, so retail-compatible/default builds won’t contain the fix.

## Issue Context
PR Compliance ID 2 requires infantry to respond immediately to player attack commands after emerging from destroyed fireport vehicles. The PR introduces new cleanup behavior (clearing `m_temporaryState` / resetting temporary state exit and clearing the AI goal object during `scatter`) intended to restore responsiveness, but because it is behind `!RETAIL_COMPATIBLE_CRC` it only takes effect when explicitly building with retail CRC compatibility disabled. This affects both Generals and GeneralsMD implementations of `AIStateMachine::clear()` and `OpenContain::scatterToNearbyPosition()`, and the CMake config referenced does not appear to override the default macro value.

## Fix Focus Areas
- Core/GameEngine/Include/Common/GameDefines.h[90-96]
- cmake/config-build.cmake[70-74]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1024-1029]
- Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[669-679]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[768-775]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[771-774]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

m_temporaryState = nullptr;
#endif

AIUpdateInterface* ai = getOwner()->getAI();
if (ai)
ai->friend_notifyStateMachineChanged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ void OpenContain::scatterToNearbyPosition(Object* rider)
// set position of the object at center of building and move them toward pos
rider->setPosition( theContainer->getPosition() );
ai->ignoreObstacle(theContainer);
#if !RETAIL_COMPATIBLE_CRC
ai->friend_setGoalObject(nullptr);
#endif
ai->aiMoveToPosition( &pos, CMD_FROM_AI );

}
Expand Down
Loading