bugfix(particlesys): Delay creation of particle emitters until ParticleSystemManager is xfer-loaded#2333
Conversation
7418b31 to
16135b2
Compare
|
| Filename | Overview |
|---|---|
| Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp | Removes constructor-time emitter creation, defers it to doDrawModule() and loadPostProcess() via a new createParticleSystem static helper; removes tossTreadEmitters() call from loadPostProcess() (safe since IDs are never serialized). Introduces a duplicate static helper that also appears in W3DTankTruckDraw.cpp. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankTruckDraw.cpp | Mirrors W3DTankDraw.cpp changes: defers tread emitter creation to doDrawModule() and loadPostProcess(), removes the constructor call and the tossTreadEmitters() pre-clear in loadPostProcess(). Contains the same duplicate static helper. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp | Particle-system creation moved from the constructor to a new createEmitters() helper called from loadPostProcess() and from update() (when upgrade is active). The original if (d->m_radiusParticleSystemTmpl) null guard before calling createParticleSystem was removed, but ParticleSystemManager::createParticleSystem handles null gracefully so this is safe. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp | Same pattern as AutoHealBehavior: emitter creation moved from the constructor to a createEmitters() helper called from loadPostProcess() and update(). Cosmetic fix: removed an extra blank line and reindented setWakeFrame call in the constructor. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/AutoHealBehavior.h | Adds createEmitters() private method declaration; minor cosmetic change to the class opening-brace placement. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/GrantStealthBehavior.h | Adds createEmitters() private method declaration. No other structural changes. |
Sequence Diagram
sequenceDiagram
participant Ctor as Constructor
participant Xfer as xfer() / XferLoad
participant PSM as ParticleSystemManager
participant LPP as loadPostProcess()
participant Update as update() / doDrawModule()
Note over Ctor,Update: Old flow (buggy)
Ctor->>PSM: createParticleSystem() — ID saved
PSM-->>Ctor: systemID (e.g. 42)
Xfer->>PSM: xfer-load saved systems (may overwrite ID 42!)
Xfer->>Ctor: restore m_radiusParticleSystemID (stale ID)
LPP->>PSM: tossTreadEmitters + createParticleSystem (redundant)
Note over Ctor,Update: New flow (fixed)
Ctor->>Ctor: m_radiusParticleSystemID = INVALID
Xfer->>PSM: xfer-load saved systems (safe, no conflict)
Xfer->>Ctor: restore m_radiusParticleSystemID (valid or INVALID)
LPP->>LPP: createEmitters() — only if ID == INVALID
LPP->>PSM: createParticleSystem()
PSM-->>LPP: new systemID
Update->>Update: createEmitters() — no-op if already created
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DTankDraw.cpp
Line: 128-142
Comment:
**Duplicated static helper function**
The `createParticleSystem` static helper introduced here is identical to the one added at line 184 of `W3DTankTruckDraw.cpp`. Since both files live in the same subsystem and the function body is byte-for-byte the same, any future change (e.g. adding a `setSaveable` flag, or adjusting attachment logic) would need to be applied in both places. Extracting this into a shared inline helper (e.g. in a local utility header or a shared `.cpp`) would eliminate the duplication and reduce maintenance risk.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 3605bfe
16135b2 to
7b4b2fd
Compare
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/GrantStealthBehavior.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/AutoHealBehavior.cpp
Outdated
Show resolved
Hide resolved
| createTreadEmitters(); | ||
| // TheSuperHackers @bugfix stephanmeesters 20/02/2026 | ||
| // If loading from savegame, delay non-saveable emitter creation until postProcess. | ||
| if (TheGameState == nullptr || TheGameState->isInLoadGame() == FALSE) |
There was a problem hiding this comment.
This is calling a function that is commented with "brutal hack", indicating that using this function is undesired. Can we solve this another way perhaps? Perhaps create particle effects later?
There was a problem hiding this comment.
Particle effects are now created later, in update or doDrawModule. When loading from savegame these happen after loadPostProcess.
I've reworked the create emitters functions a bit to be more performant and readable
There was a problem hiding this comment.
So my first thought here is that this is a workaround for a limitation of the particle system manager; changing the particle creation on object creation to a lazy update creation. This is similar to what the wheel emitters do. However, it does not fix the root issue with the particle system manager. So if someone created particles on object creation in the future again, then the issue returns. Is there a way to make the particle system manager robust against this? Or at least add an assert that signals that it is invalid?
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
…toHealBehavior.cpp Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
ee59fc0 to
1f55acd
Compare
During loading a savegame, some particle systems were created before
ParticleSystemManagerhad the opportunity to xfer-load the saved particle systems, which led to the possibility that some of these early created particle systems could be overwritten inm_systemMapofParticleSystemManager. When that happens they would no longer reliably be able to use master/slave systems. The retail game does not use master/slave on the involved effects but it could appear as an issue with some mods (as the bug report describes too).Misc findings
W3DTankDrawandW3DTankTruckDrawwould create particle systems in the constructor, only to purge and recreate them again inpostProcess, this has been changed to look if we're loading a savegame and then only create it once.GrantStealthBehaviortriggers when you do a GPS scrambler, but the behavior and associated particle effect will only last for one frame. The particle effect references a texture that's available in Generals but not in Zero Hour. So I think that so far nobody has been able to see this particle effect in action.Todo