From d9eaaeb28cadf7ea2643f114d72ab102271b8de6 Mon Sep 17 00:00:00 2001 From: Okladnoj Date: Wed, 5 Aug 2026 13:21:34 +0300 Subject: [PATCH 1/2] fix(spectregunship): Decouple gattling targeting logic from particle crc With RETAIL_COMPATIBLE_CRC=0 the headless dummy particle manager does not load particle system templates, so data->m_gattlingStrafeFXParticleSystem is null. The gattling targeting block was gated on that pointer, so a headless client skipped it and left m_gattlingTargetPosition and m_okToFireHowitzerCounter unchanged, diverging in logic crc from a graphical client; the howitzer GameLogicRandomValue calls then cascade the desync further. Run the targeting logic whenever the gattling is firing in the non-retail branch, independent of the template, and keep the template check only on the client-side particle creation. The original condition is preserved under RETAIL_COMPATIBLE_CRC so retail stays byte-identical even if an INI omits the particle template. Matches the pattern used in EMPUpdate, SpecialAbilityUpdate and TransitionDamageFX. --- .../GameLogic/Object/Update/SpectreGunshipUpdate.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp index e9d29e47845..c40a0229bb7 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp @@ -616,7 +616,12 @@ UpdateSleepTime SpectreGunshipUpdate::update() // GATTLING TARGETING LOGIC------------------------------------------ const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem; + // TheSuperHackers @fix The particle system is now decoupled from the logic crc +#if RETAIL_COMPATIBLE_CRC if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) +#else + if (gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) +#endif { @@ -645,7 +650,12 @@ UpdateSleepTime SpectreGunshipUpdate::update() const Player *localPlayer = rts::getObservedOrLocalPlayer(); //Make sure the gunship is visible to the player before drawing effects. + // TheSuperHackers @fix The particle system is now decoupled from the logic crc +#if RETAIL_COMPATIBLE_CRC if ( gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) +#else + if ( tmp && gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) +#endif { // This makes the client smoke effects of the gattling cannon strafing the ground toward the attack position From cd564252ce439f797cb4c5dc8c844cc290b7a90c Mon Sep 17 00:00:00 2001 From: Okladnoj Date: Wed, 5 Aug 2026 17:55:06 +0300 Subject: [PATCH 2/2] refactor(spectregunship): Confine particle template to retail crc branch Apply review suggestions: move the tmp declaration into the retail branch, restore the original shroud check, and pass the template directly to createParticleSystem, which already null-checks it. Behavior is unchanged. --- .../GameLogic/Object/Update/SpectreGunshipUpdate.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp index c40a0229bb7..b2368da9f35 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp @@ -615,9 +615,9 @@ UpdateSleepTime SpectreGunshipUpdate::update() // GATTLING TARGETING LOGIC------------------------------------------ - const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem; - // TheSuperHackers @fix The particle system is now decoupled from the logic crc #if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @fix The particle system is now decoupled from the logic crc + const ParticleSystemTemplate *tmp = data->m_gattlingStrafeFXParticleSystem; if (tmp && gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) #else if (gattling && gattling->testStatus( OBJECT_STATUS_IS_FIRING_WEAPON) ) @@ -650,16 +650,11 @@ UpdateSleepTime SpectreGunshipUpdate::update() const Player *localPlayer = rts::getObservedOrLocalPlayer(); //Make sure the gunship is visible to the player before drawing effects. - // TheSuperHackers @fix The particle system is now decoupled from the logic crc -#if RETAIL_COMPATIBLE_CRC if ( gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) -#else - if ( tmp && gunship->getShroudedStatus( localPlayer->getPlayerIndex() ) <= OBJECTSHROUD_PARTIAL_CLEAR ) -#endif { // This makes the client smoke effects of the gattling cannon strafing the ground toward the attack position - ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp); + ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(data->m_gattlingStrafeFXParticleSystem); if (sys) { Coord3D impactPosition;