Repository navigation
Client protocol
How the 16042 client talks to a server: its connections, the envelopes and keys every message travels in, and the conversation from logging in to the end of the loading screen. It covers what the message audit has verified so far; world messages join it as their areas are checked. Each message's fields, with their widths and meanings, are in the generated message reference, which lists every message by opcode and links the page for its area; pregame's are all on Messages-Pregame.
A client talks to three servers in turn, and keeps more than one connection open at once.
| Connection | Opened | Carries |
|---|---|---|
| Sign-on | First, to the address on the client's command line | A text exchange that proves the password and hands the client a game token |
| Login | After sign-on | The login itself, then the hand-off to a realm. It stays open after the hand-off |
| World | When the login server hands the client to a realm | Character select, realm select and the game itself |
sequenceDiagram
participant C as Client
participant L as Login server
participant W as World server
L->>C: ServerHello 0x0003, bare
C->>L: LoginRequest 0x0592
L->>C: LoginAccepted 0x0591
L->>C: RealmHandoff 0x03DB
C->>W: opens the world connection
W->>C: ServerHello 0x0003
C->>W: WorldHello 0x058F, then re-keys
C->>W: CharacterListRequest 0x07E0
W->>C: CharacterList 0x0117
C->>W: CharacterSelect 0x07DD
W->>C: EnterWorld 0x00AD
The diagram leaves out the envelopes below, which wrap every message after the first greeting.
Everything after the first greeting is encrypted. The client sends every message inside a ClientEnvelope (0x0244): a 32-bit length in clear, then the message, encrypted with the key of the connection it's sent on. Servers answer inside one of two envelopes, and the client picks the key by the envelope's opcode, not by the connection it arrives on:
| Envelope | Opcode | Key |
|---|---|---|
LoginEnvelope |
0x0076 |
The login connection's |
WorldEnvelope |
0x03DC |
The world connection's |
The client drops any server message that arrives bare, except a ServerHello on the login connection.
The keys:
- The login connection uses a key fixed by the build.
- A world connection starts under that same fixed key.
- Once the client has greeted a world server, it switches that connection to a key derived from the session key the login server handed it.
Two more wrappers sit inside the envelope. Character creation travels as a NestedMessage (0x025C): one already-encoded message, unencrypted inside the envelope. It's the only message the client sends that way. In the world, the message inside the envelope is 0x038C, which carries the game message and the routing tag that says which part of the server owns it (Server core design lists the tags).
Every connection opens with the server's ServerHello (0x0003). On the login connection the client checks two of its fields and ends the login unless both match:
| Field | Must be |
|---|---|
| The build | 16042 |
| The protocol version | 0x097998A0 |
On a world connection the client checks neither. It reads only the realm id, and only on the world connection the login opens, where it keeps that id as its current realm. The rest of the greeting's fields carry no meaning to the client.
Before any of the messages below, the client signs on over a separate text exchange. It proves the account's password with SRP6 without ever sending it. Both sides then switch the exchange to RC4 under the key that proof produced, and the client receives a game token for the login.
Note
Sign-on isn't part of the message audit yet: it's a text protocol, not the bit-packed messages the audit covers. What's here is carried over from the old code, which a real client signs on against. Our one fix to it so far makes the proof's key always 64 bytes, where the old code produced 63 on about one login in 256.
- The login server greets the client with a bare
ServerHello. If the greeting passes its checks, the client answers withLoginRequest(0x0592), once it has an account name: its build, the account name as typed, the user id and game token sign-on gave it, and a description of the client and its machine. - The server accepts with
LoginAccepted(0x0591) or refuses withLoginDenied(0x063D). A refusal carries a result the client turns into a message of its own, then it closes its connections and returns to the login screen. - After accepting, the server sends
RealmHandoff(0x03DB). Realm notices (RealmNotices,0x0763) can arrive at any point.
RealmHandoff tells the client where to go next: the world server's address and port, the session key, a value to echo back, and the realm's name, flags and PvP type.
- The client opens a connection to that world server and waits there for its
ServerHello. The login connection stays open. - It answers with
WorldHello(0x058F): the echo value and the session key, both sent back unchanged, and the account name the login server accepted. - Right after sending it, the client switches the world connection to the key derived from the session key.
Each time the client enters character select it sends CharacterListRequest (0x07E0): after a world connection's greeting, on choosing the current realm or backing out of realm select, and after leaving the world. The server answers with CharacterList (0x0117), which replaces the client's list. It holds the account's characters and its character creation options.
| The client sends | The server answers | Notes |
|---|---|---|
CharacterCreate (0x025B), inside a NestedMessage
|
CharacterCreateResult (0x00DC) |
On success the client selects the new character itself |
CharacterDelete (0x0352) |
CharacterDeleteResult (0x00E6) |
|
CharacterRename (0x0786) |
CharacterRenameResult (0x014B) |
A name that fails the client's own check is refused without sending |
CharacterSelect (0x07DD) |
EnterWorld (0x00AD), or CharacterSelectFailed (0x0162) |
See Entering the world |
Each answer is taken only while its request is pending.
The server can also send these on character select, unprompted:
| Message | Opcode | Does |
|---|---|---|
DeletedCharacterList |
0x0116 |
The account's deleted characters |
CreationLevelLimit |
0x0036 |
A level that limits the creation options the client offers |
SubscriptionState |
0x033D |
The account's subscription state |
CharacterDisabled |
0x00E7 |
Marks one listed character disabled or enabled |
LoginQueueStatus, LoginQueueFinished
|
0x0717, 0x0715
|
The login queue's progress and its end |
RealmAnnouncement |
0x03E1 |
A realm-wide announcement, taken on every screen |
PtrCopyQueued |
0x06EA |
A copy of a character to the test realm was queued |
While the client sits on character select or realm select, it sends PregameKeepAlive (0x0241) at most about once a minute.
- From character select, the client asks for the realm list with
RealmListRequest(0x07A4), and realm select takes over the connection. The server answers withRealmList(0x0761), which also carries the realm notices. - The player's choice goes back as
RealmChoice(0x07DF).- The current realm: the client carries on in character select without waiting.
-
Another realm: the server answers with
RealmRedirect(0x07A1): the new world server, a session key and the realm's details. The client connects there and waits for its greeting, as in the hand-off above.
Transfers. While the subscription hasn't expired, the client can ask which realms a character could move to with TransferDestinationsRequest (0x03EE). The answer is TransferDestinationList (0x0862), and a transfer's outcome comes back as RealmTransferResult (0x0765).
EnterWorld (0x00AD) answers a pending CharacterSelect. The client shows the world's loading screen and loads the world, around a point the message gives. That point isn't the character's position, which arrives later in the world's own messages. The same message changes world from inside the game.
Then the server puts the player into the world, and the client says when it's ready. The loading screen stays up until the server's last message:
sequenceDiagram
participant C as Client
participant W as World server
W->>C: EnterWorld 0x00AD
Note over C: loading screen, loads the world
W->>C: entity create 0x0262, the player's unit
W->>C: PlayerUnit 0x019B
W->>C: WorldSettings 0x00F1
W->>C: MovementControl 0x0636
W->>C: PathState 0x06BC
W->>C: the player's own state 0x025E
C->>W: WorldReady 0x00F2
W->>C: WorldEntryComplete 0x0061
Note over C: leaves the loading screen
- The player's own unit arrives as an entity create (
0x0262), andPlayerUnit(0x019B) names it as the player: the unit the client moves. The client refusesPlayerUnitwhen no unit has its id, so the create comes first. -
WorldSettings(0x00F1) can come any time afterEnterWorld. It carries the world's difficulty and prime level for the interface, and how often the client sends its movement while it moves a unit. -
MovementControl(0x0636) hands the client a unit to move. The client sends the message's ticket straight back (0x0635), takes the unit over and starts sending its movement. -
PathState(0x06BC) sets the player's path: the active one, each path's XP and which paths are unlocked. The player's own state (0x025E) follows. - Once the world is loaded,
PlayerUnithas named the player andWorldSettingshas arrived, the client sendsWorldReady(0x00F2), once per world. - The server answers with
WorldEntryComplete(0x0061). No other message lets the client leave the loading screen.
Until PlayerUnit arrives, the client ignores MovementControl, drops PathState and refuses the player's own state, so those three always follow it. What comes after the loading screen, from moving to chatting, belongs to the areas the audit checks next; milestone one's list of messages is on the Roadmap.
| Message | Opcode | Taken on | The client |
|---|---|---|---|
Kicked |
0x036A |
Character select, realm select and in the world | Drops its connections and shows the reason |
WorldLogout |
0x0594 |
In the world only; the pregame screens drop it | Ends the session |
| Term | Means here |
|---|---|
| Login connection | The connection to the login server, after sign-on |
| World connection | A connection to a realm's world server |
| Envelope | The encrypted wrapper every message after the first greeting travels in |
| Nested message | One already-encoded message carried unencrypted inside an envelope; only character creation uses it |
| The build's fixed key | The key every login and new world connection starts under |
| Session key | The key the login server hands the client for the world connection, sent back in WorldHello
|
| Part | Basis |
|---|---|
| Every message named here | Verified to the Fields rung: two independent readings of the client agree on every stated fact, and the area was reviewed |
The entity create (0x0262) and the player's own state (0x025E) |
Not verified yet. Their place in world entry is established from the client; their fields aren't settled |
| The order and the conditions on each step | Established from the client, not yet seen in a game. They become the check at milestone one |
| Sign-on | Carried over from the old code, which a real client signs on against; not yet part of the audit |
| Piece | Path |
|---|---|
| The greeting and the client's envelopes | Source/NexusTomorrow.Network/Message/Model/ |
| The login server's messages | Source/NexusTomorrow.Network.Auth/Message/Model/ |
Character select, realm select and EnterWorld
|
Source/NexusTomorrow.Network.World/Message/Model/Pregame/ |
| The rest of world entry, one file per message until the client's areas are settled | Source/NexusTomorrow.Network.World/Message/Model/ |
| The sign-on exchange | Source/NexusTomorrow.Network.Sts/ |
| SRP6 and the packet cipher | Source/NexusTomorrow.Cryptography/ |
| The check that holds each message to its rung | Tests/NexusTomorrow.Network.Codec.Tests/Verification/ |
Guides
Explanation
Reference
Design
About this wiki