Skip to content

refactor: key fake-player tracking by ObjectGuid instead of raw Player*#156

Closed
Nyeriah wants to merge 1 commit into
azerothcore:masterfrom
Nyeriah:refactor/fake-store-by-guid
Closed

refactor: key fake-player tracking by ObjectGuid instead of raw Player*#156
Nyeriah wants to merge 1 commit into
azerothcore:masterfrom
Nyeriah:refactor/fake-store-by-guid

Conversation

@Nyeriah

@Nyeriah Nyeriah commented May 31, 2026

Copy link
Copy Markdown
Member

Summary

Switches the fake-player tracking from unordered_map<Player*, FakePlayer> _fakePlayerStore to unordered_set<ObjectGuid> _fakePlayerGuids, drops the FakePlayer struct + GetFakePlayer() method, and adds a GUID-form ClearFakePlayer(ObjectGuid) that resolves the player via ObjectAccessor::FindPlayer and handles online + offline cleanup uniformly.

Why GUID

Raw Player* keys can outlive the player they describe. The OnPlayerLogout hook intentionally skips ClearFakePlayer while a WG war is active (so a relog can rejoin without re-faking), but the Player object is still deleted at the end of the logout flow. The entry in _fakePlayerStore survives with a dangling pointer key, and the next new Player(...) allocation can reuse that address — _fakePlayerStore.contains(newPlayer) then returns true for an unrelated character, and ClearFakePlayer would stamp the new player with the old player's stored race/morph/team/faction.

ObjectGuid is the project-wide convention for any reference that may outlive a single call/tick (see the project CLAUDE.md "Long-lived references" note); this module's Player*-keyed map was the lone exception.

Why no struct

Six of the seven FakePlayer fields are derivable on demand:

  • RealRacegetRace(true) (m_realRace survives setRace, which only modifies m_race per Unit::setRace).
  • RealTeamIDGetTeamId(true) (derived from m_realRace).
  • RealNativeMorphChrRacesEntry::model_m/model_f for the player's gender — by definition the native model for race+gender.
  • Fake{Race,Morph,TeamID} were write-once values already on the live player after fake application; only the dead-code ApplyFakeVisualsForBF (removed in refactor(WG): remove unused PrepareFakeTeamForBF/ApplyFakeVisualsForBF #155) and the debug dump ever read them back.

Only RealMorph (the DisplayId snapshot at fake time) is genuinely non-derivable; it captured the player's active transformation visual (bear form, ghost wolf, transform aura). That snapshot was fragile though — the aura it intended to preserve may have expired during the fake — so the new code derives the canonical race+gender model on clear; an active transformation aura will reapply on its next tick.

Side effects

  • OnBattlefieldWarEnd correctly drops offline entries now. Previously the loop did FindPlayer(guid) and silently skipped offline GUIDs, leaking their entries into _fakePlayerStore forever. The new GUID-form ClearFakePlayer erases the set entry whether the player is online or not.
  • Loop collapses to one line per GUID — no per-player nullptr/IsPlayerFake checks needed; the GUID form is a no-op for unfaked entries.
  • .cfbg debug no longer prints a "Fake record:" block. The block was strictly redundant with the live "Native" / "Current" lines above it (which print real race/team from getRace(true) / GetTeamId(true) and current race/team/display from the live player fields). SQL help text updated accordingly.

Test plan

  • Build module under AzerothCore (-DMODULES=static) — confirm no unresolved symbols.
  • Cross-faction BG entry → fake morph applies; BG end → race/team/display restored to native.
  • Cross-faction WG entry → fake morph applies on war accept; war end → race/team/display restored for all war participants (including any who logged out mid-war once they relog).
  • Logout in WG during war → relog post-war → character loads native race/faction from DB (already covered by the existing save path, since SaveToDB writes getRace(true) = m_realRace, not the fake m_race).
  • .cfbg debug still prints sensible state for both faked and unfaked targets.

Replaces `unordered_map<Player*, FakePlayer> _fakePlayerStore` with
`unordered_set<ObjectGuid> _fakePlayerGuids`, drops the `FakePlayer`
struct and `GetFakePlayer()` method, and adds a GUID-form
`ClearFakePlayer(ObjectGuid)` overload that resolves the player via
`ObjectAccessor::FindPlayer` and handles both online and offline cleanup.

Why GUID
--------
- Raw Player* keys can survive a logged-out player (e.g. WG logout
  during an active war intentionally skips ClearFakePlayer); the next
  Player allocation may reuse that address and inherit the stale entry,
  contaminating a different character. AzerothCore's project convention
  is to store ObjectGuid for any reference that may outlive a single
  call/tick; this module's Player*-keyed map was the lone exception.

Why no struct
-------------
- RealRace / RealTeamID derive from `getRace(true)` / `GetTeamId(true)`
  (m_realRace survives `setRace`, which only touches m_race).
- RealNativeMorph derives from `ChrRacesEntry::model_m`/`model_f` for
  the player's gender -- canonically the native model.
- Fake{Race,Morph,TeamID} were write-once values already on the live
  player and never read back outside the debug command.
- RealMorph (DisplayId at fake time) was the only non-derivable field;
  it captured transient transformation visuals (bear form, ghost wolf,
  transform aura). Restoring that snapshot was fragile -- the aura it
  intended to preserve may have expired during the fake -- so the new
  code derives the canonical race+gender model on clear. An active
  transformation aura will reapply on its next tick.

Side effects
------------
- `OnBattlefieldWarEnd` collapses to one call per GUID and now
  correctly drops entries for players who logged out during the war
  (previously leaked because the FindPlayer + IsPlayerFake dance
  silently skipped offline GUIDs).
- `.cfbg debug` no longer prints a "Fake record" block; the live
  "Native" / "Current" lines above it already covered every field the
  block printed.
- SQL command help text updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Nyeriah Nyeriah closed this May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant