From 78d3a108849264236e8e6ab24d0e8f001abcfbbf Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 13:17:31 +0100 Subject: [PATCH 01/14] bugfix(projectile): Fix out-of-bounds access bug in DumbProjectile causing mismatches --- .../Behavior/DumbProjectileBehavior.cpp | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index f53b524ee03..19fa4b7b3ad 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -446,7 +446,21 @@ Bool DumbProjectileBehavior::calcFlightPath(Bool recalcNumSegments) Real flightDistance = flightCurve.getApproximateLength(); m_flightPathSegments = ceil( flightDistance / m_flightPathSpeed ); } - flightCurve.getSegmentPoints( m_flightPathSegments, &m_flightPath ); + +#if RETAIL_COMPATIBLE_CRC + flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); +#else + if (m_flightPathSegments >= 2) + { + flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); + } + else + { + m_flightPathSegments = 0; + m_flightPath.clear(); + } +#endif + DEBUG_ASSERTCRASH(m_flightPathSegments == m_flightPath.size(), ("m_flightPathSegments mismatch")); #if defined(RTS_DEBUG) @@ -584,7 +598,13 @@ UpdateSleepTime DumbProjectileBehavior::update() return UPDATE_SLEEP_NONE; } - if( m_currentFlightPathStep >= m_flightPath.size() ) +#if RETAIL_COMPATIBLE_CRC + const Bool preventOutOfBounds = FALSE; +#else + const Bool preventOutOfBounds = m_flightPath.size() <= 1; +#endif + + if( preventOutOfBounds || m_currentFlightPathStep >= m_flightPath.size() ) { // No more steps to use. Would go out of bounds on vector, so have to do something. // We could allow physics to take over and make us fall, but the point of this whole task @@ -649,7 +669,26 @@ UpdateSleepTime DumbProjectileBehavior::update() // so lets orient it the same as if it were on frame 1! { Coord3D prevPos = m_flightPath[0]; - Coord3D curPos = m_flightPath[1]; + Coord3D curPos; + +#if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. + // If there's only one element, set current position to a fixed garbage value in a poor attempt to imitate retail behavior. + // Using a fixed value means that the behavior is deterministic for patched clients. + if (m_flightPath.size() >= 2) + { + curPos = m_flightPath[1]; + } + else + { + DEBUG_CRASH(("Vector must contain two or more elements; check the weapon speed value")); + + const Coord3D indeterminateValue = Coord3D(-NAN, -NAN, -NAN); + curPos = indeterminateValue; + } +#else + curPos = m_flightPath[1]; +#endif Vector3 curDir(curPos.x - prevPos.x, curPos.y - prevPos.y, curPos.z - prevPos.z); curDir.Normalize(); // buildTransformMatrix wants it this way From f72e7ca023f0c42d5ce12da6f4fe62d3e4cfe8f8 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:32:37 +0100 Subject: [PATCH 02/14] Removed attempt at garbage values. --- .../Object/Behavior/DumbProjectileBehavior.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 19fa4b7b3ad..a9831b2226d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -668,13 +668,14 @@ UpdateSleepTime DumbProjectileBehavior::update() //long, blurry projectile graphics which look badly oriented on step 0 of the flight path // so lets orient it the same as if it were on frame 1! { - Coord3D prevPos = m_flightPath[0]; - Coord3D curPos; + const Coord3D prevPos = m_flightPath[0]; #if RETAIL_COMPATIBLE_CRC + Coord3D curPos; + // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. - // If there's only one element, set current position to a fixed garbage value in a poor attempt to imitate retail behavior. - // Using a fixed value means that the behavior is deterministic for patched clients. + // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. + // If there's only one element, set current position to a fixed value so that the behavior is deterministic for patched clients. if (m_flightPath.size() >= 2) { curPos = m_flightPath[1]; @@ -682,12 +683,10 @@ UpdateSleepTime DumbProjectileBehavior::update() else { DEBUG_CRASH(("Vector must contain two or more elements; check the weapon speed value")); - - const Coord3D indeterminateValue = Coord3D(-NAN, -NAN, -NAN); - curPos = indeterminateValue; + curPos = prevPos; } #else - curPos = m_flightPath[1]; + const Coord3D curPos = m_flightPath[1]; #endif Vector3 curDir(curPos.x - prevPos.x, curPos.y - prevPos.y, curPos.z - prevPos.z); From 41925ddfb34ba2cddf940d5e30272b85a26fcdeb Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:33:14 +0100 Subject: [PATCH 03/14] Added 'TheSuperHackers' comment. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index a9831b2226d..7127857e885 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -447,6 +447,8 @@ Bool DumbProjectileBehavior::calcFlightPath(Bool recalcNumSegments) m_flightPathSegments = ceil( flightDistance / m_flightPathSpeed ); } + // TheSuperHackers @bugfix Caball009 10/01/2026 The way flight paths are used requires at least two curve points. + // getSegmentPoints will create only 1 element with default values for m_flightPath if m_flightPathSegments equals 1. #if RETAIL_COMPATIBLE_CRC flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); #else From 5def8f80eb055fe43a364be86a60e2d4fe31097f Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:39:34 +0100 Subject: [PATCH 04/14] Improved comment. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 7127857e885..b9f7c7108ec 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -677,7 +677,7 @@ UpdateSleepTime DumbProjectileBehavior::update() // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. - // If there's only one element, set current position to a fixed value so that the behavior is deterministic for patched clients. + // If there's only one element, set current position to a valid value so that the behavior is deterministic for patched clients. if (m_flightPath.size() >= 2) { curPos = m_flightPath[1]; From aaaa3afe6394676d0e587adda78a9b873a9e0439 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:50:01 +0100 Subject: [PATCH 05/14] Fixed invalid direction. --- .../GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index b9f7c7108ec..4072fe4ddb6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -670,9 +670,9 @@ UpdateSleepTime DumbProjectileBehavior::update() //long, blurry projectile graphics which look badly oriented on step 0 of the flight path // so lets orient it the same as if it were on frame 1! { - const Coord3D prevPos = m_flightPath[0]; #if RETAIL_COMPATIBLE_CRC + Coord3D prevPos; Coord3D curPos; // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. @@ -680,14 +680,18 @@ UpdateSleepTime DumbProjectileBehavior::update() // If there's only one element, set current position to a valid value so that the behavior is deterministic for patched clients. if (m_flightPath.size() >= 2) { + prevPos = m_flightPath[0]; curPos = m_flightPath[1]; } else { DEBUG_CRASH(("Vector must contain two or more elements; check the weapon speed value")); - curPos = prevPos; + + prevPos = Coord3D(0.0f, 0.0f, 0.0f); + curPos = Coord3D(0.0f, 1.0f, 0.0f); } #else + const Coord3D prevPos = m_flightPath[0]; const Coord3D curPos = m_flightPath[1]; #endif From 21d47d5d2d1adf00a280bd9d5edbf9c8a043a9b6 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:55:10 +0100 Subject: [PATCH 06/14] Changed initializer list to member function. --- .../GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 4072fe4ddb6..a234e374b60 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -687,8 +687,8 @@ UpdateSleepTime DumbProjectileBehavior::update() { DEBUG_CRASH(("Vector must contain two or more elements; check the weapon speed value")); - prevPos = Coord3D(0.0f, 0.0f, 0.0f); - curPos = Coord3D(0.0f, 1.0f, 0.0f); + prevPos.set(0.0f, 0.0f, 0.0f); + curPos.set(0.0f, 1.0f, 0.0f); } #else const Coord3D prevPos = m_flightPath[0]; From b7cf3ed6e6216fcbaec6493e4a3696b805d84613 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sat, 10 Jan 2026 14:57:43 +0100 Subject: [PATCH 07/14] Removed blank line. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index a234e374b60..2ad61695191 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -670,7 +670,6 @@ UpdateSleepTime DumbProjectileBehavior::update() //long, blurry projectile graphics which look badly oriented on step 0 of the flight path // so lets orient it the same as if it were on frame 1! { - #if RETAIL_COMPATIBLE_CRC Coord3D prevPos; Coord3D curPos; From 56e2c9ceeea54f47762f101dc05992e99dcf6005 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:48:56 +0100 Subject: [PATCH 08/14] Changed logic to account for delay by a single frame. --- .../Behavior/DumbProjectileBehavior.cpp | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 2ad61695191..64bf509f0e6 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -447,21 +447,9 @@ Bool DumbProjectileBehavior::calcFlightPath(Bool recalcNumSegments) m_flightPathSegments = ceil( flightDistance / m_flightPathSpeed ); } - // TheSuperHackers @bugfix Caball009 10/01/2026 The way flight paths are used requires at least two curve points. - // getSegmentPoints will create only 1 element with default values for m_flightPath if m_flightPathSegments equals 1. -#if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @info The way flight paths are used requires at least two curve points. + // DumbProjectileBehavior::update has been modified to handle cases where the flight path consists of one or zero curve points. flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); -#else - if (m_flightPathSegments >= 2) - { - flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); - } - else - { - m_flightPathSegments = 0; - m_flightPath.clear(); - } -#endif DEBUG_ASSERTCRASH(m_flightPathSegments == m_flightPath.size(), ("m_flightPathSegments mismatch")); @@ -600,13 +588,8 @@ UpdateSleepTime DumbProjectileBehavior::update() return UPDATE_SLEEP_NONE; } -#if RETAIL_COMPATIBLE_CRC - const Bool preventOutOfBounds = FALSE; -#else - const Bool preventOutOfBounds = m_flightPath.size() <= 1; -#endif - - if( preventOutOfBounds || m_currentFlightPathStep >= m_flightPath.size() ) + // TheSuperHackers @info This check also covers the case where the flight path consists of zero curve points. + if( m_currentFlightPathStep >= m_flightPath.size() ) { // No more steps to use. Would go out of bounds on vector, so have to do something. // We could allow physics to take over and make us fall, but the point of this whole task @@ -676,7 +659,7 @@ UpdateSleepTime DumbProjectileBehavior::update() // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. - // If there's only one element, set current position to a valid value so that the behavior is deterministic for patched clients. + // If there are fewer than two elements, set current position to a valid value so that the behavior is deterministic for patched clients. if (m_flightPath.size() >= 2) { prevPos = m_flightPath[0]; @@ -690,6 +673,13 @@ UpdateSleepTime DumbProjectileBehavior::update() curPos.set(0.0f, 1.0f, 0.0f); } #else + if (m_flightPath.size() <= 2) + { + // there is no valid flight path to calculate but detonation needs to be delayed by a single frame + ++m_currentFlightPathStep; + return UPDATE_SLEEP_NONE; + } + const Coord3D prevPos = m_flightPath[0]; const Coord3D curPos = m_flightPath[1]; #endif From 2827c3cb2dc2657bcab2cbba0930438a88510783 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:56:51 +0100 Subject: [PATCH 09/14] Fixed size check. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 64bf509f0e6..db7badc6311 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -673,7 +673,7 @@ UpdateSleepTime DumbProjectileBehavior::update() curPos.set(0.0f, 1.0f, 0.0f); } #else - if (m_flightPath.size() <= 2) + if (m_flightPath.size() < 2) { // there is no valid flight path to calculate but detonation needs to be delayed by a single frame ++m_currentFlightPathStep; From 5bd555aef5034fdeef2ea1d58687acb522a1487d Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:09:34 +0100 Subject: [PATCH 10/14] Moved assertion out of retail compatibility block. Moved code comment. --- .../Object/Behavior/DumbProjectileBehavior.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index db7badc6311..315a64f11ac 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -653,13 +653,15 @@ UpdateSleepTime DumbProjectileBehavior::update() //long, blurry projectile graphics which look badly oriented on step 0 of the flight path // so lets orient it the same as if it were on frame 1! { + // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. + // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. + // If there are fewer than two elements, set current position to a valid value so that the behavior is deterministic for patched clients. + DEBUG_ASSERTCRASH(m_flightPath.size() >= 2, ("Vector is expected to contain two or more elements; check the weapon speed value")); + #if RETAIL_COMPATIBLE_CRC Coord3D prevPos; Coord3D curPos; - // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. - // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. - // If there are fewer than two elements, set current position to a valid value so that the behavior is deterministic for patched clients. if (m_flightPath.size() >= 2) { prevPos = m_flightPath[0]; @@ -667,8 +669,6 @@ UpdateSleepTime DumbProjectileBehavior::update() } else { - DEBUG_CRASH(("Vector must contain two or more elements; check the weapon speed value")); - prevPos.set(0.0f, 0.0f, 0.0f); curPos.set(0.0f, 1.0f, 0.0f); } From a97e907762a08822ad5f914ef83f2b64b1422e93 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:53:52 +0100 Subject: [PATCH 11/14] Simplified by unifying retail and non-retail code. --- .../Behavior/DumbProjectileBehavior.cpp | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 315a64f11ac..664ba55777c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -450,7 +450,6 @@ Bool DumbProjectileBehavior::calcFlightPath(Bool recalcNumSegments) // TheSuperHackers @info The way flight paths are used requires at least two curve points. // DumbProjectileBehavior::update has been modified to handle cases where the flight path consists of one or zero curve points. flightCurve.getSegmentPoints(m_flightPathSegments, &m_flightPath); - DEBUG_ASSERTCRASH(m_flightPathSegments == m_flightPath.size(), ("m_flightPathSegments mismatch")); #if defined(RTS_DEBUG) @@ -655,10 +654,7 @@ UpdateSleepTime DumbProjectileBehavior::update() { // TheSuperHackers @bugfix Caball009 10/01/2026 Check vector size before accessing the second element to prevent out of bounds access. // The non-deterministic behavior for retail clients cannot be fixed, so this will remain a source of potential mismatches in retail compatibility mode. - // If there are fewer than two elements, set current position to a valid value so that the behavior is deterministic for patched clients. - DEBUG_ASSERTCRASH(m_flightPath.size() >= 2, ("Vector is expected to contain two or more elements; check the weapon speed value")); - -#if RETAIL_COMPATIBLE_CRC + // Use the flight path start and end coordinates if needed, so that the behavior is deterministic for patched clients. Coord3D prevPos; Coord3D curPos; @@ -669,20 +665,11 @@ UpdateSleepTime DumbProjectileBehavior::update() } else { - prevPos.set(0.0f, 0.0f, 0.0f); - curPos.set(0.0f, 1.0f, 0.0f); - } -#else - if (m_flightPath.size() < 2) - { - // there is no valid flight path to calculate but detonation needs to be delayed by a single frame - ++m_currentFlightPathStep; - return UPDATE_SLEEP_NONE; - } + DEBUG_CRASH(("Vector is expected to contain two or more elements; check the weapon speed value")); - const Coord3D prevPos = m_flightPath[0]; - const Coord3D curPos = m_flightPath[1]; -#endif + prevPos = m_flightPathStart; + curPos = m_flightPathEnd; + } Vector3 curDir(curPos.x - prevPos.x, curPos.y - prevPos.y, curPos.z - prevPos.z); curDir.Normalize(); // buildTransformMatrix wants it this way From b4999b8ee8e13f18bd730c5c52d8324268950975 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 25 Jan 2026 22:41:57 +0100 Subject: [PATCH 12/14] Put assertion behind retail only macro. --- .../GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 664ba55777c..bb592db902b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -665,7 +665,10 @@ UpdateSleepTime DumbProjectileBehavior::update() } else { - DEBUG_CRASH(("Vector is expected to contain two or more elements; check the weapon speed value")); +#if RETAIL_COMPATIBLE_CRC + DEBUG_CRASH(("A mismatch is likely to happen if this code path is used in a match with unpatched clients." + " Vector is expected to contain two or more elements; check the weapon speed value.")); +#endif prevPos = m_flightPathStart; curPos = m_flightPathEnd; From 7e14f8fd0c60c00e711436cddb49b671655ac36a Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Sun, 25 Jan 2026 23:05:51 +0100 Subject: [PATCH 13/14] Set flightStep with correct values. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index bb592db902b..3bf3b975427 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -670,6 +670,7 @@ UpdateSleepTime DumbProjectileBehavior::update() " Vector is expected to contain two or more elements; check the weapon speed value.")); #endif + flightStep = m_flightPathStart; prevPos = m_flightPathStart; curPos = m_flightPathEnd; } From 518625bc2ef993a1a8dae58a5e13cf4a229d07c8 Mon Sep 17 00:00:00 2001 From: Caball009 <82909616+Caball009@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:15:44 +0100 Subject: [PATCH 14/14] Fixed incorrect final position for the projectile. Apparently 'flightStep' is used as the next position for the projectile. --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index 3bf3b975427..e18bdd1bf5d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -670,9 +670,9 @@ UpdateSleepTime DumbProjectileBehavior::update() " Vector is expected to contain two or more elements; check the weapon speed value.")); #endif - flightStep = m_flightPathStart; prevPos = m_flightPathStart; curPos = m_flightPathEnd; + flightStep = m_flightPathEnd; } Vector3 curDir(curPos.x - prevPos.x, curPos.y - prevPos.y, curPos.z - prevPos.z);