Skip to content

Client protocol

rebaserHEAD edited this page Oct 16, 2026 · 5 revisions

explanation, committed verified 2026-10-16 bootstrap f1e5c15 run 2026-10-16

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.

The shape

Three connections

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
Loading

The diagram leaves out the envelopes below, which wrap every message after the first greeting.

Envelopes and keys

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).

The greeting checks the build

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.

Pregame, step by step

Signing on

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.

Logging in

  1. The login server greets the client with a bare ServerHello. If the greeting passes its checks, the client answers with LoginRequest (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.
  2. The server accepts with LoginAccepted (0x0591) or refuses with LoginDenied (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.
  3. After accepting, the server sends RealmHandoff (0x03DB). Realm notices (RealmNotices, 0x0763) can arrive at any point.

Handing off to the world server

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.

  1. The client opens a connection to that world server and waits there for its ServerHello. The login connection stays open.
  2. It answers with WorldHello (0x058F): the echo value and the session key, both sent back unchanged, and the account name the login server accepted.
  3. Right after sending it, the client switches the world connection to the key derived from the session key.

Character select

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.

Realm select and transfers

  1. From character select, the client asks for the realm list with RealmListRequest (0x07A4), and realm select takes over the connection. The server answers with RealmList (0x0761), which also carries the realm notices.
  2. 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).

Entering the world

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
Loading
  1. The player's own unit arrives as an entity create (0x0262), and PlayerUnit (0x019B) names it as the player: the unit the client moves. The client refuses PlayerUnit when no unit has its id, so the create comes first.
  2. WorldSettings (0x00F1) can come any time after EnterWorld. 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.
  3. 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.
  4. 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.
  5. Once the world is loaded, PlayerUnit has named the player and WorldSettings has arrived, the client sends WorldReady (0x00F2), once per world.
  6. 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.

Leaving

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

Vocabulary

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

How sure this is

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

Where the code lives

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/

Clone this wiki locally