fix(particlesys): Simplify ParticleSystemManagerDummy setup#2740
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/GameClient/ParticleSys.h | Adds isDummy() virtual method, makes createParticleSystem virtual, and expands ParticleSystemManagerDummy with conditional init/reset/update overrides and a RETAIL_COMPATIBLE_CRC static-singleton path for createParticleSystem; ID=0 (INVALID_PARTICLE_SYSTEM_ID) is intentionally used so the singleton is never tracked by the manager's internal lists. |
| Core/GameEngine/Source/Common/ReplaySimulation.cpp | Removes the updateHeadless() call from the replay loop; no longer needed since the dummy manager's update() is now a no-op and createParticleSystem no longer adds systems to tracked lists. |
| Core/GameEngine/Source/GameClient/Drawable/Update/BeaconClientUpdate.cpp | Guards two DEBUG_ASSERTCRASH calls with isDummy() to suppress expected template-not-found failures in headless mode. |
| Core/GameEngine/Source/GameClient/FXList.cpp | Guards DEBUG_ASSERTCRASH for missing particle template with isDummy() check, consistent with BeaconClientUpdate change. |
| GeneralsMD/Code/GameEngine/Include/GameClient/GameClient.h | Removes the updateHeadless() declaration, now that the dummy manager itself suppresses particle work. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp | Removes the updateHeadless() implementation; the detailed explanatory comment is gone with it. |
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
Core/GameEngine/Include/GameClient/ParticleSys.h:873-884
**Shared mutable static singleton returned to callers**
`createParticleSystem` returns `&dummySystem` — a single static object shared by every caller. Code in `FXList.cpp` calls `setLocalTransform`, `rotateLocalTransformX/Y/Z`, and `setPosition` on the returned pointer inside a loop where each iteration overwrites the previous iteration's state on the same object. `BeaconClientUpdate.cpp` calls `attachToDrawable` and `tintAllColors` on it, and stores `system->getSystemID()` which always equals `INVALID_PARTICLE_SYSTEM_ID (0)` — causing the beacon to re-enter the creation path on every subsequent call.
This is benign in single-threaded headless mode since `update()` is a no-op and nothing is rendered, but a brief comment explaining that the returned pointer is a non-owning, non-unique token (and that callers must not delete or track it by ID) would prevent future misuse when this class is re-used.
Reviews (1): Last reviewed commit: "fix(particlesys): Simplify ParticleSyste..." | Re-trigger Greptile
| virtual ParticleSystem *createParticleSystem(const ParticleSystemTemplate *sysTemplate, Bool createSlaves = TRUE) override | ||
| { | ||
| #if RETAIL_COMPATIBLE_CRC | ||
| if (sysTemplate == nullptr) | ||
| return nullptr; | ||
| static StaticParticleSystemTemplate dummyTemplate; | ||
| static StaticParticleSystem dummySystem(&dummyTemplate); | ||
| return &dummySystem; | ||
| #else | ||
| return nullptr; | ||
| #endif | ||
| } |
There was a problem hiding this comment.
Shared mutable static singleton returned to callers
createParticleSystem returns &dummySystem — a single static object shared by every caller. Code in FXList.cpp calls setLocalTransform, rotateLocalTransformX/Y/Z, and setPosition on the returned pointer inside a loop where each iteration overwrites the previous iteration's state on the same object. BeaconClientUpdate.cpp calls attachToDrawable and tintAllColors on it, and stores system->getSystemID() which always equals INVALID_PARTICLE_SYSTEM_ID (0) — causing the beacon to re-enter the creation path on every subsequent call.
This is benign in single-threaded headless mode since update() is a no-op and nothing is rendered, but a brief comment explaining that the returned pointer is a non-owning, non-unique token (and that callers must not delete or track it by ID) would prevent future misuse when this class is re-used.
Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Include/GameClient/ParticleSys.h
Line: 873-884
Comment:
**Shared mutable static singleton returned to callers**
`createParticleSystem` returns `&dummySystem` — a single static object shared by every caller. Code in `FXList.cpp` calls `setLocalTransform`, `rotateLocalTransformX/Y/Z`, and `setPosition` on the returned pointer inside a loop where each iteration overwrites the previous iteration's state on the same object. `BeaconClientUpdate.cpp` calls `attachToDrawable` and `tintAllColors` on it, and stores `system->getSystemID()` which always equals `INVALID_PARTICLE_SYSTEM_ID (0)` — causing the beacon to re-enter the creation path on every subsequent call.
This is benign in single-threaded headless mode since `update()` is a no-op and nothing is rendered, but a brief comment explaining that the returned pointer is a non-owning, non-unique token (and that callers must not delete or track it by ID) would prevent future misuse when this class is re-used.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
We expect single threaded use right now.
We could add thread_local for non VC6 builds to protect future multithreaded users, but it is useless because ParticleSystemManager::createParticleSystem is also not thread safe.
| struct StaticParticleSystem : public ParticleSystem | ||
| { | ||
| StaticParticleSystem(const StaticParticleSystemTemplate *sysTemplate) | ||
| : ParticleSystem(sysTemplate, ParticleSystemID(0), TRUE) {} |
There was a problem hiding this comment.
Perhaps set createSlaves here to FALSE
This change simplifies the ParticleSystemManagerDummy setup. It no longer creates and updates particle systems in headless mode with the dummy implementation. With
RETAIL_COMPATIBLE_CRCit still INI parses the particle system templates to make sure the Logic CRC is not affected.TODO