CNA-BUG-265: SignedInGamer::SignedIn does not replay already signed-in gamers to a late subscriber, unlike XNA 4.0
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
XNA 4.0 raises SignedIn during Update and its add accessor replays each already signed-in gamer to a new handler; CNA raises it inside Initialize on a plain event, so a handler added afterwards never hears about the four stub gamers.
- Identifier
CNA-BUG-265- Category
- Bug
- Subsystem
- Networking & gamer services
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Microsoft::Xna::Framework::GamerServices::SignedInGamer::SignedIn (event) and GamerServicesDispatcher::Initialize/Update
Expected behaviour
In the genuine XNA 4.0 GamerServices assembly (read from its decompiled IL), sign-in changes are raised from GamerServicesDispatcher.Update, and SignedInGamer.SignedIn has a custom add accessor: after combining the delegate it loops over Gamer.SignedInGamers and invokes the newly added handler once for every gamer already signed in. The XNA documentation likewise says the event is raised during calls to Update. A game can therefore subscribe at any time and still learn who is signed in. CNA already reproduces such a replay for NetworkSession::GamerJoined, through an EventHandler replay hook, and NET.md documents that behaviour.
Actual behaviour at TARGET
SignedInGamer::SignedIn in SignedInGamer.cpp is a plain System::EventHandler with no replay hook, and GamerServicesDispatcher::Update is empty. Initialize raises OnSignIn once for each of its four stub gamers, synchronously inside the call, and a handler added afterwards is never called for them; the game must read Gamer::getSignedInGamersProperty() instead. The CNA presence demo subscribes 'before Initialize() runs so every SignedIn firing during startup is observed', and the C route cna_signed_in_gamer_subscribe_signed_in_ext adds its handler the same way. SignedOut is never raised in production, because OnSignOut has no caller outside SignedInGamerTestAccess. SignedInGamerTest.SignedInEventFires raises the event only after subscribing, so no test pins late-subscriber behaviour.
Source locations
modules/gamer-services/src/Xna/SignedInGamer.cpp— SignedIn declared as a plain event; OnSignIn and OnSignOutmodules/gamer-services/src/Xna/GamerServicesDispatcher.cpp— Initialize raises OnSignIn for the four stub gamersmodules/gamer-services/include/Microsoft/Xna/Framework/GamerServices/SignedInGamer.hpp— SignedIn and SignedOut events; OnSignOut has zero callersmodules/net/src/Xna/NetworkSession.cpp— GamerJoined replay hook, the precedent for XNA's replaymodules/c-api/src/CnaCApiGamers.cpp— cna_signed_in_gamer_subscribe_signed_in_ext adds a plain handlermodules/gamer-services/examples/demo_gamerservices_signin_presence/src/PresenceGame.cpp— subscribes before Initialize as a workaroundmodules/gamer-services/tests/Microsoft/Xna/Framework/GamerServices/GamerServicesGamerTests.cpp— SignedInEventFires raises after subscribing onlyCLAUDE.md— the rule that XNA wins where FNA and XNA disagree
Evidence
XNA behaviour read from the decompiled IL of the genuine XNA 4.0 GamerServices assembly (SignedInGamer.SignedIn add accessor, GamerServicesDispatcher.Update) and from the XNA documentation of the event; CNA behaviour read from the sources named. The SignedInGamer header cites FNA's plain event declaration as its model, and CLAUDE.md says matching FNA is no defence for a behaviour XNA does not have. Nothing was built or executed.
Focused reproduction
No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.
Current tests
SignedInGamerTest.SignedInEventFires raises the event only after subscribing, so no test pins the behaviour for a handler added after the gamers signed in.
Regression test
Initialize gamer services, then subscribe to SignedInGamer::SignedIn and expect the handler to be called once for every gamer already in Gamer::getSignedInGamersProperty(), as XNA's add accessor does.
Blast radius
Games that subscribe to SignedIn after gamer services were initialised (for example in LoadContent or a later screen): they never learn who is signed in unless they also read Gamer::getSignedInGamersProperty(). Games that subscribe before Initialize, as CNA's presence demo does, are unaffected.
Workaround
Subscribe before gamer services are initialised, or read Gamer::getSignedInGamersProperty() once after subscribing.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- User guide
- XNA compatibility
- Deep dives
- GamerServices contract
- Known issues
- Bug index