Skip to content

Conversation

@Caball009
Copy link

@Caball009 Caball009 commented Jan 10, 2026

Sufficiently high weapon speeds expose an out-of-bounds access bug for DumbProjectile, which is the reason for non-deterministic behavior across clients (i.e. mismatches).

This PR fixes the bug properly in no retail mode, and makes behavior deterministic for patched clients in retail mode.

TODO:

  • [ ] Replicate in Generals
    Generals doesn't have this particular out-of-bounds bug.

@Caball009 Caball009 added Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Stability Concerns stability of the runtime NoRetail This fix or change is not applicable with Retail game compatibility labels Jan 10, 2026
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.

Looks logical in principle.

@xezon xezon changed the title bugfix(projectile): Fix out-of-bounds access bug in DumbProjectile which may cause mismatches bugfix(projectile): Fix out-of-bounds access in DumbProjectile which causes mismatch with very high speed weapons at small hit distances Jan 10, 2026
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.

Code looks correct to me.

@xezon xezon added Gen Relates to Generals ZH Relates to Zero Hour labels Jan 10, 2026
@Caball009
Copy link
Author

Caball009 commented Jan 10, 2026

I hadn't at looked the Generals code until now.

Coord3D prevPos = m_flightPath[0];
Coord3D curPos = m_flightPath[1];

It doesn't have this particular OOB bug, but I'm not entirely sure why the code was updated for Zero Hour or how the behavior changed.

@Caball009
Copy link
Author

Maybe we can just leave the Generals code 'as is', and look at it in more detail later as part of a unification effort for this code.

@xezon
Copy link

xezon commented Jan 12, 2026

Is the code very different yes?

@Caball009
Copy link
Author

Check the links two comments back. It's not that different, but it doesn't have this particular OOB bug.

xezon
xezon previously approved these changes Jan 12, 2026
@xezon
Copy link

xezon commented Jan 12, 2026

@greptileai

@greptile-apps
Copy link

greptile-apps bot commented Jan 12, 2026

Greptile Overview

Greptile Summary

Fixes out-of-bounds vector access in DumbProjectileBehavior::update() that occurred with very high weapon speeds producing flight paths with fewer than 2 points.

Key changes:

  • Added bounds check (m_flightPath.size() >= 2) before accessing indices 0 and 1 when orienting projectile on first frame
  • Fallback uses m_flightPathStart and m_flightPathEnd for orientation calculation when flight path has < 2 points
  • Sets flightStep = m_flightPathEnd in fallback to ensure correct final position
  • Includes DEBUG_CRASH warning for retail compatibility mode, as unpatched clients will still have non-deterministic behavior
  • Makes behavior deterministic for patched clients, eliminating multiplayer mismatches

Confidence Score: 4/5

  • This PR is safe to merge with low risk - fixes a critical out-of-bounds bug that caused multiplayer mismatches
  • The fix correctly addresses the out-of-bounds access by checking vector size before accessing indices. The fallback logic is sound and uses existing member variables (m_flightPathStart/m_flightPathEnd). Score is 4 instead of 5 because the edge case handling (size < 2) is rare and the retail compatibility implications need careful testing.
  • No files require special attention - the single-file change is well-contained and properly documented

Important Files Changed

Filename Overview
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp Adds bounds checking before accessing flight path indices to prevent out-of-bounds access with very high speed weapons

Sequence Diagram

sequenceDiagram
    participant Update as DumbProjectileBehavior::update()
    participant FlightPath as m_flightPath vector
    participant Fallback as Fallback coordinates

    Update->>Update: Check m_currentFlightPathStep >= size()
    alt Flight path exhausted
        Update->>Update: detonate()
    else Flight path available
        Update->>FlightPath: Access m_flightPath[m_currentFlightPathStep]
        Note over Update: flightStep = current position
        
        alt m_currentFlightPathStep > 0
            Update->>FlightPath: Access m_flightPath[step-1] and [step]
            Update->>Update: Calculate orientation from previous to current
        else m_currentFlightPathStep == 0 (first frame)
            alt m_flightPath.size() >= 2
                Update->>FlightPath: Access m_flightPath[0] and [1]
                Update->>Update: Calculate orientation from [0] to [1]
            else m_flightPath.size() < 2 (OUT OF BOUNDS BUG)
                Note over Update: BEFORE FIX: Would access [1] and crash
                Update->>Update: Check RETAIL_COMPATIBLE_CRC
                Update->>Update: DEBUG_CRASH warning
                Update->>Fallback: Use m_flightPathStart and m_flightPathEnd
                Update->>Update: Set flightStep = m_flightPathEnd
                Update->>Update: Calculate orientation deterministically
            end
        end
        
        Update->>Update: Set transform matrix / position
    end
Loading

@xezon xezon dismissed their stale review January 16, 2026 22:27

Dismissing Approval because this needs one more looking regarding m_flightPathSegments ==1

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

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.

This is one of currently 3 open PRs with fixes for uninitialized variables, which come with a small risk of introducing new mismatches. These should be merged together without other PRs in between:

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.

Ok. Should be good then.

@Caball009
Copy link
Author

Caball009 commented Jan 25, 2026

I'll put my last comment here, so it doesn't get buried.

With fewer than 2 points, there's no visible projectile, but that's to be expected. There's also no bullet impact when force firing on the ground. Where the damage is applied needs more testing, because it seems like it's applied to a much wider field.

@tintinhamans

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

@tintinhamans
Copy link

@greptileai

Apparently 'flightStep' is used as the next position for the projectile.
@Caball009
Copy link
Author

With fewer than 2 points, there's no visible projectile, but that's to be expected. There's also no bullet impact when force firing on the ground. Where the damage is applied needs more testing, because it seems like it's applied to a much wider field.

Fixed. The projectile is still invisible, but that's alright. The projectile impact and damage are now properly applied to the projectile's destination.

This PR is ready to be merged whenever the other PRs are.

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.

Tank projectiles cause mismatch

3 participants