bugfix(contain): Prevent riders from being added to destroyed container object when Reinforcement Pad is destroyed before Troop Crawler drop#2746
Conversation
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Adds a DEBUG_ASSERTCRASH in addOrRemoveObjFromWorld for containment of dead/destroyed objects, and a #if !RETAIL_COMPATIBLE_CRC-guarded early return with DEBUG_CRASH in addToContain when the container is destroyed. Logic is sound. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Mirror of the Generals/ change: same DEBUG_ASSERTCRASH in addOrRemoveObjFromWorld and guarded early return in addToContain. Logic mirrors the Generals copy correctly. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp | Guards createPayload() behind a destroyed-container check in the non-retail path, but when the container is destroyed and payload creation is skipped, m_payloadCreated is never set to TRUE, causing the check to repeat every update cycle. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp | Same as the Generals/ copy — m_payloadCreated is not set to TRUE when payload creation is skipped for a destroyed container, repeating the check on every subsequent update. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[TransportContain::update] --> B{m_payloadCreated == FALSE?}
B -- No --> Z[Continue update]
B -- Yes --> C{RETAIL_COMPATIBLE_CRC?}
C -- Yes --> D[createPayload]
C -- No --> E{getObject isDestroyed?}
E -- No --> D
E -- Yes --> F[Skip payload creation\nm_payloadCreated stays FALSE]
D --> G[m_payloadCreated = TRUE]
G --> Z
F --> Z
H[OpenContain::addToContain] --> I{rider == nullptr?}
I -- Yes --> Return1[return]
I -- No --> J{RETAIL_COMPATIBLE_CRC?}
J -- No --> K{getObject isDestroyed?}
K -- Yes --> L[DEBUG_CRASH + return]
K -- No --> M{rider isDestroyed?}
J -- Yes --> M
M -- Yes --> N[DEBUG_CRASH + return]
M -- No --> O[Proceed with containment]
P[OpenContain::addOrRemoveObjFromWorld] --> Q{add == TRUE?}
Q -- Yes --> R[Register + show object]
Q -- No --> S[DEBUG_ASSERTCRASH container not dead/destroyed]
S --> T[Hide + unregister object]
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp:370-375
`m_payloadCreated` is never set to `TRUE` when payload creation is skipped because the container is destroyed. Since `createPayload()` is the only place that sets `m_payloadCreated = TRUE`, this flag stays `FALSE` for the lifetime of the destroyed object. Every subsequent `update()` call re-enters the branch, evaluates `isDestroyed()`, and silently skips again. Setting `m_payloadCreated = TRUE` when skipping is the cleaner fix — the flag's purpose is "has payload been handled", not "was payload successfully created".
```suggestion
// TheSuperHackers @bugfix Caball009 25/05/2026 Don't create payload
// for destroyed object to avoid leaving the payload in an invalid state.
if (!getObject()->isDestroyed())
{
createPayload();
}
else
{
m_payloadCreated = TRUE;
}
```
### Issue 2 of 2
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp:477-482
Same issue as in the `Generals/` copy: `m_payloadCreated` is never set to `TRUE` when payload creation is skipped because the container is destroyed. Every subsequent `update()` call will re-enter the branch and repeat the `isDestroyed()` check. Setting `m_payloadCreated = TRUE` in the skipped path avoids the repeated re-evaluation.
```suggestion
// TheSuperHackers @bugfix Caball009 25/05/2026 Don't create payload
// for destroyed object to avoid leaving the payload in an invalid state.
if (!getObject()->isDestroyed())
{
createPayload();
}
else
{
m_payloadCreated = TRUE;
}
```
Reviews (9): Last reviewed commit: "Replicated in Generals." | Re-trigger Greptile
0d39be3 to
735d827
Compare
f2480a5 to
6544358
Compare
ec82747 to
cf0daed
Compare
|
Replicated in Generals. |
When a reinforcement plane with an Infantry General Troop Crawler is destroyed en route, the plane and vehicle are destroyed, but the infantry spawned and remains in an invalid state (see issue for more details).
Beside being a gameplay and logic bug, there's also another issue because the infantry objects will hold on the container pointer even though that object has been destroyed. This can lead to use-after-free bugs and a crash (see related PR).
This PR makes 4 changes:
are now visibleshould now trigger a debug assertion.OpenContain::addToContainto prevent units from being added to a destroyed object (non-retail only).TODO: