# Retail live-session lifecycle pseudocode **Scope:** GameWindow Slice 3 character-list, character-entry, and transport- disconnect conformance corrections. ## Named-retail oracle - `CharacterIdentity::UnPack @ 0x004FE880` - `CharacterSet::UnPack @ 0x004FE340` - `CharacterSet::GetGreyedOutFor @ 0x004FDFA0` - `gmCharacterManagementUI::EnterGame @ 0x004ED440` - `gmCharGenMainUI::Update @ 0x004E8460` - `CPlayerSystem::Handle_Login__CharacterSet @ 0x0055F6D0` - `Proto_UI::SendEnterWorldRequest @ 0x00546A00` - `CPlayerSystem::Handle_Character__EnterGame_ServerReady @ 0x0055E4F0` - `Proto_UI::SendEnterWorld @ 0x00546BC0` - `CPlayerSystem::LogOnCharacter @ 0x0055F890` - `CPlayerSystem::UseTime @ 0x00563110` - `Proto_UI::LogOffCharacter @ 0x00546A20` - `CPlayerSystem::RequestLogOff @ 0x00562DD0` - inbound F653 dispatch `@ 0x0055C963` - `CPlayerSystem::ExecuteLogOff @ 0x0055D780` - `ClientNet::ExitWorldDisconnect @ 0x00541E00` - `ClientNet::LogOffServer @ 0x00543EF0` - `SharedNet::SendOptionalHeader @ 0x00543160` The verbatim layouts are `CharacterIdentity` and `CharacterSet` in `docs/research/named-retail/acclient.h` (types 3206 and 3209). ## CharacterSet wire decode ```text CharacterIdentity.UnPack(cursor): identity.guid = read_u32(cursor) identity.name = read_PString(cursor) identity.secondsGreyedOut = read_u32(cursor) align cursor to 4 bytes CharacterSet.UnPack(cursor): result.status = read_u32(cursor) activeCount = read_u32(cursor) result.active = empty repeat activeCount times: result.active.append(CharacterIdentity.UnPack(cursor)) deletedCount = read_u32(cursor) result.deleted = empty repeat deletedCount times: result.deleted.append(CharacterIdentity.UnPack(cursor)) result.numAllowedCharacters = read_u32(cursor) result.account = read_PString(cursor) result.useTurbineChat = read_u32(cursor) result.hasThroneOfDestiny = read_u32(cursor) ``` The two values previously described as "leading/trailing padding" are the retail status and deleted-character count. ACE currently writes zero for both, which hid the schema error in acdream and in Holtburger's current reader. Cross-checks: - ACE `GameMessageCharacterList` writes the same active identity records, allowed-slot count, canonical `Session.Account`, and booleans, but currently emits status/deleted-count as zero and no deleted records. - Holtburger `CharacterListData` agrees on active records and the tail, but likewise labels both zero values as padding. The named retail `CharacterSet` layout and `UnPack` control flow win where the references differ. ## Retail selection and acdream unattended entry ```text gmCharacterManagementUI.EnterGame(): playerSystem = GetPlayerSystem() if playerSystem exists and selectedGuid != 0 and CharacterSet.GetGreyedOutFor( CharacterSet.GetSlot(selectedGuid)) <= 0: show entering-world dialog playerSystem.LogOnCharacter(selectedGuid) ``` Retail ordinarily requires an explicit selected active character. The only automatic retail path found is `gmCharGenMainUI::Update`: after character creation it walks the active list, skips entries greyed for a positive number of seconds, matches the newly-created name case-insensitively, and logs that GUID on. acdream does not yet have the retained character-management screen. Its unattended auto-entry is therefore an explicit adaptation with a narrow rule: ```text for each identity in active wire order: if identity.guid != 0 and identity.secondsGreyedOut == 0: select this active-list index stop if none matched: do not enter a character ``` Deleted entries are never candidates. This preserves the established "first available character" behavior without treating a pending-deletion character as available. ## Canonical account used by EnterWorld ```text CPlayerSystem.LogOnCharacter(selectedGuid), when ready: selectedGuid = persistentData.selectedCharacter account = playerSystem.account // populated from CharacterSet.account Proto_UI.SendEnterWorld(account, selectedGuid) ClientNet.EnterWorld() ``` The account string in the final F657 message comes from the server-returned `CharacterSet.account`, not from the spelling typed into the startup login form. acdream must therefore make `WorldSession.EnterWorld` consume `Characters.AccountName` internally. ## Logout and negotiated transport disconnect ```text ClientNet.LogOffServer(): packet = make optional-header packet(flags = Disconnect) for each negotiated ReceiverData: SharedNet.SendOptionalHeader(packet, receiver.address, receiver) logOffSent = true logonReceiverId = 0 ExitWorldDisconnect() ``` The in-world character path remains: ```text send F653 + active character GUID wait for server's opcode-only F653 send negotiated transport Disconnect release transport ``` ACE's `GameMessageCharacterLogOff` confirms the server form is exactly four bytes. Its `NetworkSession` terminates a session when it receives a packet with the transport Disconnect flag. The transport Disconnect is connection-scoped, not character-state-scoped. Once acdream has accepted ConnectRequest receiver id/iteration, disposal sends it whether the session is still selecting a character, entering the world, in world, or failed later. F653 remains restricted to an active in-world character. A pre-negotiation failure has no receiver identity and only closes the local socket. ## Deliberately unchanged registered gaps - UN-6: the fixed 200 ms ConnectResponse delay remains registered. - TS-28: LoginComplete readiness remains a later protocol slice. - AD-44: closing exits rather than returning to a retained character screen.