Network
Network Protocol
Attorney Online's network protocol is one with a rich and colorful past. It has mostly remained intact since AO1 due to a constant demand for backward compatibility; even to this day, AO1 clients are still able to find servers from the lobby and join a game.
- Network Protocol
- Master server protocol
Handshake
Hard drive ID
Client: HI#{hdid}#%
Sends the client's hard drive ID (supposedly a unique identifier) to the server for ban tracking. This increases the work needed to evade a ban, as it is not as simple as using a VPN.
Version information
Server: ID#{player number}#{version}#%
Client: ID#{client}#{version}#%
Sends the player number (not used) and the server version to the client. The client replies with its name and version.
Response: Player count, feature list
Player count
Server: PN#{players}#{max}#%
Server: PN#{players}#{max}#{server_description}#%
(since 2.10)
Specifies the number of players in the server and the player limit. The player limit is not enforced in the client, and often not in the server either.
Since 2.10 the server description is appended to the packet to support server descriptions in the favourite tab.
Feature list
Server: FL#{feature1}#{feature2}#...#%
Lists the features supported by the server, essentially acting as a list of feature flags.
(The FL packet was introduced in 2.3.3; prior to 2.3.3, features depended on detecting a compatible server version.)
Features introduced in 2.1.0 (the first AO2 release):
yellowtext
: Enables the use of yellow text.flipping
: Enables the use of emote flipping.customobjections
: Enables the use of a single custom objection namedcustom
.fastloading
: Enables the use of "fast loading" instead of the legacy loading protocol.noencryption
: Disables FantaCrypt for the remainder of the session.
Introduced in 2.3 - 2.5:
deskmod
: Allows forcing desk/no desk. Introduced in 2.3.2.evidence
: Enables evidence. Introduced in 2.4.0.
Introduced in 2.6:
cccc_ic_support
: Enables 2.6 extensions for the in-character command.arup
: Indicates that the server broadcasts area updates.casing_alerts
: Enables casing alerts.modcall_reason
: Enables calling moderators with a custom reason.
Introduced in 2.8:
looping_sfx
: Enables looping SFX extensions for the in-character command.additive
: Enables additive text.effects
: Enables effect overlays.
Introduced in 2.9:
y_offset
: Enables vertical offset support.expanded_desk_mods
: Enables desk modifiers 2 through 5.
Introduced in 2.9.1:
auth_packet
: Enables the use of theAUTH
packet.
Asset link
Server: ASS#{asset_link}#%
Specifies the asset link of the server. It allows server to set a custom content repository, which is used for WebAO or client music streaming.
Resource counts
Client: askchaa#%
Server: SI#{char_cnt}#{evi_cnt}#{music_cnt}#%
Requests character, evidence, and track counts ahead of time for memory allocation.
Character list
Client: RC#%
Server: SC#{char_name}&{char_desc}#...#%
Requests a full list of characters. Note that char_desc
is obsolete and likely not present.
Evidence list
Server: LE#{name}&{description}&{image}#...#%
See the evidence list packet.
Music list
Client: RM#%
Server: SM#{music_name}#...#%
Requests a full list of music tracks.
Note that areas are also considered tracks. Typically, tracks are also contained in categories, which are prefixed with multiple equals signs (==
).
Music tracks must contain file extensions.
Final confirmation
Client: RD#%
Server: DONE#%
The client confirms that it has joined the server and is ready to join the default room. Typically, before DONE
is sent, the list of taken characters is also sent, among other information such as the message of the day (displayed in OOC chat).
Character selection
Taken characters
Server: CharsCheck#{taken}#...#%
The server sends an array of numbers representing whether or not each character of that index is taken or not.
taken
may be one of the following:
-1
: character is taken0
: character is free
Choose character
Client: CC#0#{char_id}#{hdid}#%
Server: PV#{player_id}#CID#{char_id}#%
The client selects a character to use in the room. Note that hdid
is an obsolete parameter. (The first parameter was once a player ID, but player IDs are also obsolete.)
If the character cannot be used, the server will not respond. Otherwise, the server responds with the character selected.
Note: A
PV
response may be sent at any time to force a character switch, and the character ID may not necessarily be the one requested by the player.
In-character commands
In-character message
Client:
MS#
{desk_mod}#
{preanim}#
{character}#
{emote}#
{message}#
{side}#
{sfx-name}#
{emote_modifier}#
{char_id}#
{sfx-delay}#
{shout_modifier}#
{evidence}#
{flip}#
{realization}#
{text_color}#
{showname}#
{other_charid}#
{self_offset}#
{noninterrupting_preanim}#
{sfx_looping}#
{screenshake}#
{frames_shake}#
{frames_realization}#
{frames_sfx}#
{additive}#
{effect}#%
Server:
MS#
{desk_mod}#
{preanim}#
{character}#
{emote}#
{message}#
{side}#
{sfx-name}#
{emote_modifier}#
{char_id}#
{sfx-delay}#
{shout_modifier}#
{evidence}#
{flip}#
{realization}#
{text_color}#
{showname}#
{other_charid}#
{other_name}#
{other_emote}#
{self_offset}#
{other_offset}#
{other_flip}#
{noninterrupting_preanim}#
{sfx_looping}#
{screenshake}#
{frames_shake}#
{frames_realization}#
{frames_sfx}#
{additive}#
{effect}#%
An in-character (IC) message is a basic form of viewport event in which a animation is displayed on the screen with various parameters. Line breaks are included for cleanliness and are not present in the actual packet.
Note that the only difference between the client and server messages is that the client does not have the
other_name
orother_emote
fields.
Reference of fields:
desk_mod: Whether or not to override desk appearance.
chat
: Positions "def", "pro", and "wit" default to desk and the positions "hld", "hlp" and "jud" to no desk.0
: desk is hidden1
: desk is shown2
: desk is hidden during preanim, shown when it ends3
: desk is shown during preanim, hidden when it ends4
: desk is hidden during preanim, character is centered and pairing is ignored, when it ends desk is shown and pairing is restored5
: desk is shown during preanim, when it ends character is centered and pairing is ignored
preanim: The animation that plays before the character starts talking. Does not include file extension.
character: The folder name of the character that is talking.
emote: The emote that should play. Does not include
(a)
/(b)
prefixes or file extensions.message: The chat message as to be displayed in the chatbox and the IC log. Note that the message may contain markup that must be parsed.
side: Which side the character is on. See the character authoring page for valid positions.
sfx_name: Name of the sound effect that should play during the preanimation (if the preanimation is enabled).
emote_modifier: A number that dictates emote behavior:
0
: do not play preanimation; overridden to 2 by a non-0
objection modifier1
: play preanimation (and sfx)2
: play preanimation and play objection3
: unused4
: unused5
: no preanimation and zoom6
: objection and zoom, no preanim
char_id: Character identifier; dictates the index of the character in the character list (starting from zero).
sfx_delay: Dictates how long in milliseconds the client should wait after the preanimation has started playing before playing the associated sound effect.
objection_modifier: Dictates if the player uses a shout.
0
: nothing1
: "Hold it!"2
: "Objection!"3
: "Take that!"4&{name}
: custom shout (since 2.8)
evidence: ID of the evidence presented. 0 is no evidence presented, so evidence ID effectively starts from 1.
flip: Dictates if the emote should be flipped.
0
: no flip1
: flip
realization: Dictates whether a realization flash and sound effect should play or not.
0
: no realization1
: realization
text_color: Dictates which color the text of the chat message should be.
0
: white1
: green2
: red3
: orange4
: blue (disables talking animation)5
: yellow6
: rainbow (removed in 2.8)
showname: If used, this will show a custom name (showname) for the character.
other_charid: The character ID of the person the player wishes to pair up with.
other_name: The folder name of the character the player is pairing up with (in case of INI swapping).
other_emote: The emote the user's pair was doing. Note that by default, zooms (that are correctly defined as such) do not update this value, so a pair of zooms will not appear. Zooms also enjoy special privileges, in that (assuming they are correctly defined, again) they make the pair disappear in the client and get centered.
self_offset: the percentage by which the character is shifted horizontally, from
-100
(one whole screen's worth to the left) to100
(one whole screen's worth to the right). This parameter also stores vertical offset, which is self-explanatory.{x_offset}
: <2.9{x_offset}&{y_offset}
: 2.9+
other_offset: The user's pair's
self_offset
, basically.other_flip: The user's pair's
char_id2/flip
, basically.noninterrupting_preanim: If
1
, the text begins at the same time as the preanimation.sfx_looping: If
1
, the sound effect loops until another emote is played.screenshake: If
1
, the screen shakes (TODO: on preanimation or on chat?).frames_shake: A list of frames for which the screen should shake (TODO: list format).
frames_realization: A list of frames for which the screen should flash (TODO: list format).
frames_sfx: A list of frames for which the sound effect should play.
additive: If
1
, does not clear the chatbox's previous message.effect: The overlay effect to be displayed.
All sections from showname
onwards is 2.6+. sfx_looping
onwards is 2.8+.
Note that servers may modify specific values of this message. For example, disemvoweling and shaking modify your text, and
/force_nonint_pres
forces your preanims to be noninterrupting. Therefore, to determine if your message was successfully sent, the character ID should be compared instead of the message text or the entire packet.
Background
Server: BN#{background}%
Server: BN#{background}#{position}%
(since 2.8)
Sets the background of the viewport, and optionally the position.
Clients can set the background with a server chat command, such as /bg
.
Music
Client: MC#{songname}#{char_id}#{showname}#{effects}#%
Server: MC#{songname}#{char_id}#{showname}#{looping}#{channel}#{effects}#%
Plays the specified track (with file extension) and records the event to the IC log.
Since 2.6, a showname
piece is added at the end. This is for the chatlog to be able to correctly store and show who changed the music, so that it does not end up confusing players with the character's name instead of the showname.
Since 2.8, the track is expected to loop clientside. Before 2.8, the track was not expected to loop, and thus servers had to add a looping feature where the MC
packet is sent to replay the track.
- looping: if greater than zero, indicates client-side looping
- channel: channel number to play on (0-3)
- Typically, channel 0 is used as the main BGM channel, whereas channel 1 is used for area-specific soundscapes.
- effects: a bit field of transition effects:
- Bit 0: fade in
- Bit 1: fade out
- Bit 2: sync position
The canonical "empty track" is ~stop.mp3
.
Penalty (health) bars
Client: HP#{bar}#{value}#%
Server: same
Updates the penalty bar. This is typically only allowed when the player is in the judge (jud
) position.
- bar: the bar to be updated
1
: defense bar (red bar)2
: prosecution bar (blue bar)
- value: the value to set the bar to (0-10)
Witness Testimony/Cross Examination (WT/CE)
Client: RT#{animation}#%
Server: same
Overlays a non-looping special animation. This is typically only allowed when the player is in the judge (jud
) position.
animation may be one of the following:
testimony1
- "Witness Testimony"testimony2
- "Cross Examination"judgeruling#0
- "Not Guilty" (since 2.6)judgeruling#1
- "Guilty" (since 2.6)testimony1#1
- Hides the "Testimony" indicator (since 2.9)- Anything else - Custom splash. SFX name and animation filename are exactly the string provided (since 2.9)
Set position
Server: SP#{side}#%
Sets the position dropdown on the client to side
. Added in 2.8.
Available positions
Server: SD#{side1}*{side2}*...#%
Overrides the position dropdown on the client to the specified list of positions. Added in 2.8.
Out-of-character message
Client: CT#{name}#{message}#%
Server: CT#{name}#{message}#{is_from_server}#%
Represents a simple message with name and text. OOC is used to convey information without interrupting ongoing gameplay.
is_from_server
is an addition that arrived with 2.6. If 1
, the name is colored with the theme's designated OOC server color.
Evidence
Supported by 2.4 onward. Every evidence item has three attributes: name, description and image.
Note that the whole evidence list is broadcasted every time the client performs any evidence operation, to ensure synchronization between clients. The server also sends the evidence list when the client joins an area.
List
Server: LE#{name}&{description}&{image}#...#%
Add
Client: PE#{name}#{description}#{image}#%
Remove
Client: DE#{id}#%
Edit
Client: EE#{id}#{name}#{description}#{image}#%
Areas
Switch area
Client: MC#{area_name}#{char_id}#%
Switches to a different area/room. As areas are more or less a hack built into the music list, the MC
packet is reused for this purpose.
The server will typically reset the health bars, background, and evidence.
If the specified area does not exist, the packet is ignored.
Area updates
Server:
ARUP#0#{area1_players}#{area2_players}#...#%
ARUP#1##{area1_status}##{area2_status}##...#%
ARUP#2##{area1_cm}##{area2_cm}##...#%
ARUP#3##{area1_locked}##{area2_locked}##...#%
Indicates additional information about all areas.
The first argument indicates the information being sent about every area:
0
: player counts1
: area statuses- Recognized statuses:
IDLE
,LOOKING-FOR-PLAYERS
,CASING
,RECESS
,RP
,GAMING
- Recognized statuses:
2
: case managers (CMs)- This should be set to
FREE
if there are no case managers in the area.
- This should be set to
3
: locked states (string, not boolean)- Recognized lock states:
FREE
,SPECTATABLE
,LOCKED
- Recognized lock states:
This packet has as many pieces (plus one) as there are areas on the server. It describes, in order, every area's given property.
For instance, a packet of ARUP#0#4#3#7#2#0#0#%
would mean that:
- There are 4 players in the first area (or, technically, in the zeroth area)
- There are 3 in the second,
- There are 7 in the third,
- There are 2 in the fourth,
- And 0 in the last two.
This packet was added in 2.6.
Music list
Server: FM#{track1}#{track2}#...#%
Like the standard music list packet, but excludes areas.
This can be used to update the music list on a per-area basis.
This packet was added in 2.8.
Area list
Server: FM#{area1}#{area2}#...#%
Like the standard music list packet, but excludes music and only lists areas.
This can be used to add and remove areas dynamically.
This packet was added in 2.8.
Casing
Case preferences update
Client: SETCASE#{caselist}#{cm}#{def}#{pro}#{judge}#{jury}#{steno}#%
Updates the user's casing alert preferences.
Parameters:
- caselist: the list of cases this user is willing to host (assuming they are also willing to CM) (not used)
- cm:
1
if the user is willing to host cases (not used) - def:
1
if the user is willing to defend a case / play as a defense attorney (or a co-defense attorney) - pro:
1
if the user is willing to prosecute a case / play as a prosecutor (or a co-prosecutor) - judge:
1
if the user is willing to judge a case - jury:
1
if the user is willing to be a member of the jury in a case - steno:
1
if the user is willing to be the stenographer of a case
It is up to the server to decide what to do with this packet. By default, tsuserver does nothing with this information (though it is stored, for some reason) and sends all case alerts to every connected client.
Additionally, this packet is sent everytime the "Casing" tickbox on the game area is toggled. When it is turned off, a SETCASE#""#0#0#0#0#0#0#%
packet is sent (indicating that the user is not interested in casing).
Case alert
Client: CASEA#{case_title}#{need_def}#{need_pro}#{need_judge}#{need_jury}#{need_steno}#%
Server: same, except {message}
instead of {case_title}
Sends an alert to players about a case needing participants.
Where the arguments are:
- case_title: the title of the case being played
- message: the title of the case, but may be modified by the server -- for example, by default, tsuserver adds the "X user needs this and that for Turnabout Y." text instead.
- need_def:
1
if the user needs a defense attorney, - need_pro:
1
if the user needs a prosecutor, - need_judge:
1
if the user needs a judge, - need_jury:
1
if the user needs jurors, - need_steno:
1
if the user need a stenographer.
The above packet, when sent from clientside, requests the server to sent the serverside packet to all relevant users.
Users are targeted if:
- they marked themselves as at least one of the roles the announcement is looking for (using the
SETCASE
packet). (akashi) - they are currently connected to the server, regardless of casing preferences (tsuserver3)
This packet may be rate-limited.
Moderator commands
Authenticate
Server: AUTH#{state: int}#%
Parameters: state:
1
or higher: Successful log-in. Displays the guard button.0
: Unsuccessful log-in attempt.-1
or lower: Log-out. Hides the guard button.
Call mod
Client: ZZ#{reason}#%
Server: ZZ#{message}#%
Alerts all mods on guard with a sound cue.
The call mod message typically contains information such as which character called, which area they called from, and a user-specified message.
Kick
Server: KK#{reason}#%
Notifies a client that they were kicked.
Ban
Server: KB#{reason}#%
Notifies a client that they were kicked and banned.
Server: BD#{reason}#%
Notifies a client that they cannot join because they are banned.
Popup
Server: BB#{message}
Notifies a client with a popup containing the specified message.
Keep alive
Client: CH#<char_id: int>#%
Server: CHECK#%
Sent to ensure that the server and client are still alive. Some servers expect the client to send this packet as often as 10 seconds.
Subtheme switching
Server: ST#{subtheme}#{reload}#%
Instructs the client to switch to a given subtheme. The client can ignore this if the user's subtheme is manually set.
Parameters:
- subtheme: The subtheme to switch to.
- reload: If
1
, the client will reload theme.
Timers
Server: TI#{timer_id: int}#{command: int}#{time: int}#%
Instructs the client to manipulate the timers on its UI.
Parameters:
- timer_id: The ID of the timer to manipulate,
0
-4
. Typically,0
is used as a "global" timer (be it server-wide or per-"hub") - command:
0
: Start/resume/sync timer attime
1
: Pause timer attime
2
: Show timer3
: Hide timer
- time: The time to display on the timer, in milliseconds.
Judge controls
Server: JD#{state: int}#%
Instructs the client to show or hide the judge controls.
Parameters:
- state:
-1
: Show or hide the judge controls, depending on the client's position.0
: Hide the judge controls.1
: Show the judge controls.
Escape codes
Escape codes allows characters like '#' to be sent in messages.
%
:<percent>
#
:<num>
$
:<dollar>
&
:<and>
Obsolete
FantaCrypt
AO1 encrypted the headers of the packets sent by the client to the server.
- FantaCrypt seeds a PRNG with a known value (the decryption key), where the lower two bytes are used in the PRNG, and the upper byte of those two bytes is used in the cipher.
- The current key is XOR'ed with the current byte.
- To get the key value for the next byte, the byte and key are added together.
- Then, some multiplication and addition is done to that value. (This PRNG behavior is similar to a linear congruential generator.)
- Finally, everything but the lower two bytes are discarded.
However, there was a major flaw in the implementation of FantaCrypt: the server supplies the client with the initial key to use for every packet. Thus, it is totally vulnerable to a replay attack.
Many clients do not actually even implement the FantaCrypt algorithm, but instead use a hardcoded key of 5, which is sent as 0x34 in the 'decryptor' packet. This is because the 'decryptor' packet argument is the key to be used, but encrypted. The decryptor value is always encrypted with a magic number key value: 322 decimal. Moreover, FantaCrypt is only used in messages sent by the client to the server and not vice versa.
The encryption algorithm was plagiarized from a Stack Overflow answer.
The protocol for FantaCrypt is as follows:
Server: decryptor#{key}#%
Client: <encrypted traffic>
Slow loading
Before "fast loading" character and music requests were developed, a slower, paginated set of requests was used:
Message | Description | Typical response |
---|---|---|
askchar2#% | Get first page of characters | CI#0#...#% |
AN#{n}#% | Get page n of characters | CI#{n}#...#% , or EI#1#...#% if nonexistent page |
AE#{n}#% | Get n th evidence | EI#{n}#...#% , or EM#0#...#% if nonexistent page |
EM#{n}#% | Get page n of tracks | EM#{n}#...#% , or CharsCheck#...#% and remainder of handshake |
Mod password
Server: OPPASS#{modpass in hex}#%
Previously, servers would send the mod password in hex encoding to the clients; the reason for this was to allow a clientside login check to show the moderator UI. It is entirely unknown how it was considered logical to send the mod password in plain text to all clients.
IP list
Server: IL#{ip}|{char_name}|{char_id}#...#%
Sends a list of all players. This is an infrequently used command and is typically sent as a response to /ip
(an obsolete command).
The server might opt to send an OOC message instead.
Mute
Server: MU#{char_id}#%
(mute)
Server: UM#{char_id}#%
(unmute)
Shows an overlay on the client using char_id
that indicates that they are muted.
Master server protocol
Function | Message | Direction |
---|---|---|
Chat | CT#[username]#[message]#% | Server |
Version check | VC#% | Server |
Client version (AO2) | ID#[client software]#[version]#% | Server |
Hard drive ID (or anything in practice) | HI#[hdid]#% | Server |
Get all servers (AO2) | ALL#% | Server |
Server entry (paginated) (AO1) | SN#[entry number]#[ip]#[server version]#[port]#[name]#[desc]#% | Client |
Server entry (all) (AO2) | ALL#[[name]&[desc]&[ip]&[port]#]% | Client |
Server version | SV#[version]#% | Client |
Ping | PING#% | Server |
Server not advertised (reply to ping) | NOSERV#% | Advertiser |
Heartbeat | SCC#[port]#[name]#[description]#[server software]#% | Server |
Heartbeat success | PSDD#0#% | Advertiser |
Keepalive | CHECK#% | Advertiser |
Global ban | DOOM#% | Client |
Paginated list (obsolete)
Client: askforservers#%
(returns first server on the list starting from 0)
Server: first server
Client: SR#{n}#%
Server: server of index n
Evidence list
Client: RE#%
Clients had a packet to request the server evidence list. However, this is deprecated and unused.
Thanks to stonedDiscord and Aleks for initial AO1 reverse engineering; OmniTroid for AO2 protocol documentation; and Cerapter for 2.6 protocol documentation.