Skip to content

bugfix(contain): Prevent objects from being added to destroyed container object#2746

Open
Caball009 wants to merge 7 commits into
TheSuperHackers:mainfrom
Caball009:fix_bug_addToContain
Open

bugfix(contain): Prevent objects from being added to destroyed container object#2746
Caball009 wants to merge 7 commits into
TheSuperHackers:mainfrom
Caball009:fix_bug_addToContain

Conversation

@Caball009
Copy link
Copy Markdown

@Caball009 Caball009 commented May 25, 2026

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:

  1. Objects that are spawned in such a state are now visible.
  2. Added code to prevent the creation of the transport payload (e.g. the Mini-Gunners) if the container object is destroyed (non-retail only).
  3. Added an early return to OpenContain::addToContain to prevent units from being added to a destroyed object (non-retail only).
  4. Added debug assertions for dead container objects or occupants.

TODO:

  • Replicate in Generals.

@Caball009 Caball009 added Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Stability Concerns stability of the runtime NoRetail This fix or change is not applicable with Retail game compatibility labels May 25, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 25, 2026

Greptile Summary

This PR adds defensive guards to prevent infantry units from entering an invalid state when a reinforcement plane (with a Transport Crawler payload) is destroyed mid-flight. The root cause is that TransportContain::update could call createPayload() on an already-destroyed container, and addToContain would then accept those units into the destroyed container, leaving dangling pointers and causing use-after-free bugs.

  • OpenContain::addOrRemoveObjFromWorld: In RTS_DEBUG builds, skips hiding the drawable when the container is already effectively dead/destroyed, so spawned infantry remain visible rather than becoming invisible phantoms.
  • OpenContain::addToContain: Adds an early return (and DEBUG_CRASH assertion) under !RETAIL_COMPATIBLE_CRC to reject any unit that would be loaded into a destroyed container; also wraps the pre-existing destroyed-rider guard in braces and adds a matching DEBUG_CRASH.
  • TransportContain::update: Under !RETAIL_COMPATIBLE_CRC, skips createPayload() entirely when the transport object is destroyed, preventing the infantry units from being created in the first place.

Confidence Score: 5/5

Safe to merge — all changes are either non-retail-only guards or debug-only assertions with no impact on retail CRC or gameplay logic.

Both changed files introduce narrowly scoped, preprocessor-guarded defensive checks. The retail path in TransportContain::update is left untouched behind #if RETAIL_COMPATIBLE_CRC, preserving CRC compatibility with unpatched clients. The new early return in addToContain and the DEBUG_CRASH assertions are gated behind !RETAIL_COMPATIBLE_CRC, so they only fire in non-retail builds. The addOrRemoveObjFromWorld visibility fix is further restricted to RTS_DEBUG builds only. No existing retail code paths are altered.

No files require special attention — both files touch only guarded, non-retail code paths.

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Adds early-return guard and DEBUG_CRASH for destroyed container in addToContain (non-retail), wraps destroyed-rider guard in braces with matching assertion, and makes occupants visible in debug builds when container is dead during addOrRemoveObjFromWorld.
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/TransportContain.cpp Wraps createPayload() call in !RETAIL_COMPATIBLE_CRC guard that skips payload creation if the transport container is already destroyed, preventing invalid-state infantry units from being spawned.

Sequence Diagram

sequenceDiagram
    participant Plane as Reinforcement Plane
    participant TC as TransportContain::update()
    participant OC as OpenContain::addToContain()
    participant Obj as Infantry Object

    Plane->>Plane: Destroyed mid-flight
    TC->>TC: "m_payloadCreated == FALSE"

    Note over TC: RETAIL_COMPATIBLE_CRC path
    TC->>OC: createPayload() always called
    OC->>Obj: Spawn infantry
    OC->>OC: addToContain — container destroyed
    OC-->>Obj: Infantry in invalid state

    Note over TC: Non-retail path (this PR)
    TC->>TC: "getObject()->isDestroyed()?"
    TC--xTC: Skip createPayload()

    Note over OC: Fallback guard non-retail
    OC->>OC: "getObject()->isDestroyed()?"
    OC-->>OC: DEBUG_CRASH + early return

    Note over OC: RTS_DEBUG visibility fix
    OC->>Obj: setDrawableHidden skipped
    Obj-->>Obj: Infantry remains visible
Loading

Reviews (7): Last reviewed commit: "Changed macro." | Re-trigger Greptile

Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HelixContain.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/GarrisonContain.cpp Outdated
@Caball009 Caball009 changed the title fix(contain): Prevent objects from being added to destroyed container object bugfix(contain): Prevent objects from being added to destroyed container object May 25, 2026
@Caball009 Caball009 force-pushed the fix_bug_addToContain branch from 0d39be3 to 735d827 Compare May 26, 2026 14:25
@Caball009 Caball009 force-pushed the fix_bug_addToContain branch from f2480a5 to 6544358 Compare May 26, 2026 15:09
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp Outdated
{
#if RTS_DEBUG
// TheSuperHackers @tweak This shouldn't happen but don't make the occupant invisible if it does.
if (!(getObject()->isEffectivelyDead() || getObject()->isDestroyed()))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we really need this visual deviance in Debug as well? I suggest turn this into a regular assert. It will be far more obvious in debug.

DEBUG_ASSERTCRASH(!getObject()->isEffectivelyDead() && !getObject()->isDestroyed(), ("..."));

But check if this is already covered by earlier asserts. Maybe it is not necessary to assert here.

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

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility Stability Concerns stability of the runtime ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

China Assault Troop Crawler can become a firing ghost unit after Reinforcement Pad drop

2 participants