fix(net): conform character entry and session shutdown
This commit is contained in:
parent
bacc7e45a9
commit
aea957f845
12 changed files with 1016 additions and 95 deletions
|
|
@ -0,0 +1,157 @@
|
|||
# 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue