Table of Contents

Ultima Online Packets

Moongate speaks the classic Ultima Online wire protocol. Every message is a packet: a byte stream that starts with a one-byte opcode and is decoded into (or encoded from) a small C# record struct — incoming packets implement IIncomingPacket<T>, outgoing ones IOutgoingPacket. This page maps the protocol surface currently implemented for the ClassicUO 7.x client target.

45
implemented packets
14
incoming (client → server)
31
outgoing (server → client)
7.x
client target
Opcode Name Dir Size Description
0x02 Move Request C → S 7 bytes (fixed) One step or turn, with anti-fastwalk key.
0x06 Double Click C → S 5 bytes (fixed) The client double-clicked an entity, identified by its serial.
0x09 Single Click C → S 5 bytes (fixed) The client clicked an entity, identified by its serial.
0x11 Status Bar Info S → C Variable The player's own status window.
0x1B Login Confirm S → C 37 bytes (fixed) The first packet of the enter-world burst.
0x1D Delete Object S → C 5 bytes (fixed) The entity is gone — stop drawing it.
0x20 Draw Game Player S → C 19 bytes (fixed) Positions and renders the player's own mobile.
0x22 Movement Ack S → C 3 bytes (fixed) Confirms the step with the client's sequence number.
0x24 Draw Container S → C 7 bytes (fixed) Opens the container's gump on the client.
0x25 Add Item To Container S → C 21 bytes (fixed) Drops one item into an already-open container gump.
0x27 Lift Reject S → C 2 bytes (fixed) The lift the client asked for is refused, and why.
0x2E Worn Item S → C 15 bytes (fixed) Draws a single item on a mobile that the client already knows about.
0x3A Skill Lock Change C → S Variable The client sets the up/down/lock arrow on one skill.
0x3A Skills S → C Variable Skill list (0x3A), in the absolute-with-caps form (type 0x02): the client's whole skill list in one go.
0x3C Container Content S → C Variable Every item inside a container, in one variable-length packet.
0x4E Personal Light Level S → C 6 bytes (fixed) The light radiating around the given mobile.
0x4F Overall Light Level S → C 2 bytes (fixed) 0 is full daylight, higher is darker.
0x55 Login Complete S → C 1 bytes (fixed) The "you are now in the world" marker that unblocks the client.
0x5B Game Time S → C 4 bytes (fixed) The in-world clock shown to the client.
0x5D Character Select C → S 73 bytes (fixed) The client picks an existing character slot to enter the world with.
0x72 War Mode S → C 5 bytes (fixed) Toggles the client's combat stance.
0x73 Ping C → S 2 bytes (fixed) The client sends this periodically with a rolling sequence byte and expects the server to echo it straight back, or it eventually drops the connection.
0x73 Ping Ack S → C 2 bytes (fixed) Echoes the client's keep-alive sequence byte straight back.
0x78 Draw Object S → C Variable Draws a mobile and its equipped items on the client.
0x80 Account Login Request C → S 62 bytes (fixed) Credentials for the login server.
0x82 Login Denied S → C 2 bytes (fixed) Rejects the login with a protocol reason code.
0x83 Delete Character C → S 39 bytes (fixed) The client asks to delete the character in the given slot.
0x85 Character Delete Result S → C 2 bytes (fixed) Why a deletion was refused.
0x86 Character List Update S → C 304 bytes (fixed) The account's character list after it changed, so the client can redraw the selection screen.
0x88 Paperdoll S → C 66 bytes (fixed) Tells the client to open the character window for a mobile.
0x8C Connect To Game Server S → C 11 bytes (fixed) Redirects the client to the game port with an auth key.
0x91 Game Server Login C → S 65 bytes (fixed) The auth key from the redirect plus the account credentials.
0xA0 Select Server C → S 3 bytes (fixed) The shard index the client picked from the server list.
0xA8 Server List S → C Variable Advertises the available shards.
0xA9 Character List S → C Variable The character slots followed by the starting cities, in the extended 7.0.13+ layout.
0xB9 Support Features S → C 5 bytes (fixed) Unlocks the client feature set at login, sent right before the character list.
0xBC Season Change S → C 3 bytes (fixed) Sets the client's season and optionally plays the season-change sound.
0xBD Client Version C → S Variable The client answers the server's version request with its build string (e.g.
0xBF General Information C → S Variable A multiplexed request whose meaning is chosen by a leading SubCommand (ushort).
0xBF/0x08 Map Change S → C 6 bytes (fixed) Switches the client to the given map.
0xBF/0x18 Map Patches S → C 41 bytes (fixed) Declares the static/land map-diff block counts for the four classic facets.
0xBF/0x19 Stat Lock Info S → C 12 bytes (fixed) The up/down/lock state of the three stats, packed two bits each into a single byte.
0xEF Login Seed C → S 21 bytes (fixed) Connection seed and client version, sent first by ClassicUO.
0xF3 World Item S → C 24 bytes (fixed) Draws an item lying in the world.
0xF8 Character Creation C → S 106 bytes (fixed) The new 106-byte creation packet sent by clients 7.0.16.0 and later.

How the protocol works

  • Opcodes. The first byte of every packet identifies it. The same opcode can mean different things per direction (for example 0x73 is a ping from the client and a ping acknowledgement from the server).
  • Fixed vs variable length. Most packets have a fixed size known from the opcode. Variable-length packets carry their total length as a big-endian ushort right after the opcode.
  • Login vs game phase. The client first talks to the login server (account auth, shard list, server select), receives a redirect (0x8C), then reconnects to the game server and enters the world.
  • The 0xBF multiplexer. General Information (0xBF) is a container: a ushort sub-command selects the actual meaning (map patches, stat locks, and many more). Moongate models each implemented sub-command as its own outgoing packet class.

How a packet is implemented

Each packet is a self-contained readonly record struct that knows how to read or write itself, and declares its documentation family with the [PacketDocumentation] attribute:

/// <summary>
/// Double click (0x06): the client double-clicked an entity, identified by its serial. 5 bytes fixed.
/// Whether the target is a mobile or an item is decided later from the serial's range.
/// </summary>
[PacketDocumentation(PacketFamilyType.InteractionKeepalive, Length = 5)]
public readonly record struct DoubleClickPacket(Serial Target) : IIncomingPacket<DoubleClickPacket>
{
    public static byte PacketId => 0x06;

    public static DoubleClickPacket Read(ref SpanReader reader)
    {
        reader.ReadByte(); // packet id
        var target = new Serial(reader.ReadUInt32());

        return new(target);
    }
}

The reference pages in this section are generated from those classes by scripts/generate-packet-docs.cs — run dotnet run scripts/generate-packet-docs.cs from the repository root after adding or changing a packet.

Packet families

Every packet belongs to a family — click a card for the family's packet list.

Login & shard select

0x80 · 0x82 · 0x8C · 0x91 · 0xA0 · 0xA8 · 0xBD · 0xEF

Seed, account auth, server list, shard select, game-server handoff.

Characters

0x5D · 0x83 · 0x85 · 0x86 · 0xA9 · 0xF8

Character list, creation, selection, deletion and list updates.

Enter world

0x1B · 0x55 · 0xB9

Login confirm, feature flags, and the login-complete marker.

World state

0x1D · 0x4E · 0x4F · 0x5B · 0xBC · 0xBF

Light levels, game time, season, map change/patches, object removal.

Items & containers

0x24 · 0x25 · 0x27 · 0x2E · 0x3C · 0xF3

World items, worn items, container gumps, contents and lift rejects.

Movement

0x02 · 0x20 · 0x22 · 0x78

Move requests, acks, and mobile position updates.

Status & skills

0x11 · 0x3A · 0x72 · 0x88 · 0xBF

Mobile status, paperdoll, skills, war mode, stat/skill locks.

Interaction & keepalive

0x06 · 0x09 · 0x73 · 0xBF

Single/double click, the 0xBF request multiplexer, and ping round-trips.