From 4f9968f1198c4a9265722a93f1e9d9429d439923 Mon Sep 17 00:00:00 2001 From: Jaredl-Dev <260281143+Jaredl-Dev@users.noreply.github.com> Date: Sun, 16 Aug 2026 21:17:07 -0700 Subject: [PATCH] Fix Supply Drop crate payouts at 60Hz --- .../Include/GameLogic/Module/DeletionUpdate.h | 1 + .../GameLogic/Object/Contain/ParachuteContain.cpp | 11 +++++++++++ .../Source/GameLogic/Object/Update/DeletionUpdate.cpp | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DeletionUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DeletionUpdate.h index 86238247091..67cc5c954d9 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DeletionUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DeletionUpdate.h @@ -72,6 +72,7 @@ class DeletionUpdate : public UpdateModule // virtual destructor prototype provided by memory pool declaration void setLifetimeRange( UnsignedInt minFrames, UnsignedInt maxFrames ); + void restartLifetime(); UnsignedInt getDieFrame() { return m_dieFrame; } virtual UpdateSleepTime update() override; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cpp index 1432b5790d9..366595b160a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/ParachuteContain.cpp @@ -39,6 +39,7 @@ #include "GameLogic/AIPathfind.h" #include "GameLogic/Locomotor.h" #include "GameLogic/Module/AIUpdate.h" +#include "GameLogic/Module/DeletionUpdate.h" #include "GameLogic/Module/ParachuteContain.h" #include "GameLogic/Module/PhysicsUpdate.h" #include "GameLogic/Object.h" @@ -469,6 +470,16 @@ void ParachuteContain::onRemoving( Object *rider ) const ParachuteContainModuleData* d = getParachuteContainModuleData(); // object is no longer held inside a transport +#if defined(GENERALS_ONLINE_HIGH_FPS_SERVER) + // Info: At 60Hz, a short-lived object can reach its DeletionUpdate lifetime while parachuting, then be destroyed + // before its delayed OCLUpdate runs after landing. Restart an expired lifetime before releasing the object. + static NameKeyType key_DeletionUpdate = NAMEKEY( "DeletionUpdate" ); + DeletionUpdate *dup = (DeletionUpdate*)rider->findUpdateModule( key_DeletionUpdate ); + if (dup && dup->getDieFrame() <= TheGameLogic->getFrame()) + { + dup->restartLifetime(); + } +#endif rider->clearDisabled( DISABLED_HELD ); rider->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_PARACHUTING ) ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DeletionUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DeletionUpdate.cpp index 5c9c19e8f3e..9fabbc46e9d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DeletionUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DeletionUpdate.cpp @@ -67,6 +67,14 @@ void DeletionUpdate::setLifetimeRange( UnsignedInt minFrames, UnsignedInt maxFra #endif } +//------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------- +void DeletionUpdate::restartLifetime() +{ + const DeletionUpdateModuleData *d = getDeletionUpdateModuleData(); + setLifetimeRange( d->m_minFrames, d->m_maxFrames ); +} + //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UnsignedInt DeletionUpdate::calcSleepDelay(UnsignedInt minFrames, UnsignedInt maxFrames)