acdream/docs/research/2026-07-26-retail-hardwareless-particle-emitter-diagnostics.md

2.8 KiB
Raw Blame History

Retail hardwareless particle-emitter resolution

Date: 2026-07-26 Oracle: matching Sept-2013 acclient.exe + acclient.pdb

Symptom

Dense scenes repeatedly logged that ParticleEmitterInfo records such as 0x320002D6 and 0x320003B60x320003B9 were “not found” for many different owners. The records were present in the installed DAT, so the message was misleading and each owner repeated the same failed resolution work.

Named-retail behavior

ParticleEmitter::SetInfo @ 0x0051CE90 begins with:

Destroy()
info = suppliedInfo

if info.hw_gfxobj_id == INVALID_DID:
    Destroy()
    return false

particleObject = makeParticleObject(info.max_particles, info.sorting_sphere)
...

ParticleEmitter::SetInfo(DataID) @ 0x0051D3C0 first loads the ParticleEmitterInfo and delegates to the overload above. ParticleManager::CreateParticleEmitter @ 0x0051B6C0 destroys the attempted emitter when either SetInfo or parenting fails. Retail does not substitute the software gfxobj_id for an absent hardware GfxObj.

Installed-DAT audit

The production DatCollection and EmitterDescRegistry were exercised through ProjectileVfxAudit --emitters:

Emitter Record Software GfxObj Hardware GfxObj Retail outcome
0x320002D6 present/parsed present 0 skip
0x320003B6 present/parsed present 0 skip
0x320003B7 present/parsed present 0 skip
0x320003B8 present/parsed present 0 skip
0x320003B9 present/parsed present 0 skip

These are authored hardwareless records, not missing DAT files.

Cross-reference

  • ACViewer ParticleEmitter.SetInfo returns false when HWGfxObjID == 0. Its render initialization explicitly comments that this case is expected and skips the absent emitter.
  • WorldBuilder contains no contradictory live-client hardware-emitter fallback. Its particle code is an editor/rendering reference, while the retail client remains the behavior oracle.

Runtime contract

EmitterDescRegistry now stores either one resolved descriptor or one classified negative result per DAT ID:

MissingEmitterInfo
InvalidHardwareGfxObjId
MissingHardwareGfxObj

The particle hook sink reports the first failure for each emitter DAT ID, not one line per owner. Hardwareless records say that retail skipped them rather than claiming the ParticleEmitterInfo was absent. No fallback asset or particle is fabricated.

Registration of an explicit runtime descriptor clears a cached negative result, preserving test/tool injection and future hot-reload behavior.

Conformance coverage

  • EmitterDescRegistry_NegativeCachesMissingDatAndRegisterClearsFailure
  • EmitterDescRegistry_ClassifiesAndCachesAuthoredHardwarelessEmitter
  • MissingEmitterAsset_ReportsOnlyOnceAcrossManyOwners