Comment on page
Launcher/Client Protocol
This page describes the communication protocol employed by
launcher.exe
, Tl.exe
, and TERA.exe
.- C/C++-like primitive types and
struct
s will be used. - Integers (
uint8_t
,int8_t
,uint16_t
,int16_t
, etc) are little endian. - Characters (i.e.
char8_t
andchar16_t
) are UTF-8 and UTF-16 respectively, and little endian. - Strings (i.e.
u8string
andu16string
) are a series of validchar8_t
andchar16_t
characters respectively, followed by a NUL character. - Fields are laid out in the declared order with no implied padding anywhere.
Note that strings are not always NUL-terminated. For this reason, the document will explicitly call out whether a NUL terminator is present.
The role of each program is as follows:
launcher.exe
: The publisher-specific game launcher which performs authentication and server list URL resolution. Serves requests fromTl.exe
and receives game events.Tl.exe
: The (mostly) publisher-agnostic game launcher which requests authentication data and the server list URL fromlauncher.exe
. Serves requests and forwards game events fromTERA.exe
.TERA.exe
: The actual game client. Sends requests and game events toTl.exe
.
These programs all communicate via window messages, specifically
WM_COPYDATA
. The dwData
field specifies the message ID, while lpData
and cbData
contain the payload pointer and length.Messages sent between
Tl.exe
and launcher.exe
consist of text encoded as UTF-8. A NUL terminator is present in most messages, but not all. Responses from launcher.exe
should always use the same message ID that Tl.exe
used in the corresponding request message. Notably, only the Hello Handshake and Game Event Notification messages have a static ID; the request messages use a message counter as the message ID, so the contents must be parsed to understand them.The protocol starts off with a handshake sent from
Tl.exe
. This message contains the NUL-terminated string Hello!!
.launcher.exe
should respond with a NUL-terminated Hello!!
or Steam!!
to indicate the method of authentication in use. The former uses classic authentication while the latter uses Steam. (Steam authentication will not be documented here.)Tl.exe
will occasionally notify launcher.exe
of various game events sent by TERA.exe
. The format of these messages can be described with the following regular expressions:^csPopup\(\)$
: Signals thatlauncher.exe
should open the customer support website (e.g. in the default Web browser).^gameEvent\((\d+)\)$
: Indicates some kind of notable action taken by the user in the game. The value in parentheses is a code specifying the type of event; the possible values are documented in the section onTERA.exe
messages.^endPopup\((\d+)\)$
: Indicates that the client has exited. The value in parentheses is an exit reason code (not the same as the process exit code); the possible values are documented in the section onTERA.exe
messages.^promoPopup\(\d+\)$
: The exact purpose of this notification is currently unknown.
launcher.exe
should not respond to these messages.Tl.exe
will request the server list URL from launcher.exe
. This message does not have a static message ID. The message contains the NUL-terminated string slsurl
.launcher.exe
should respond with the NUL-terminated server list URL.The Web server at the URL should be prepared to receive an HTTP GET request, potentially with a query string (which can be ignored). The response should use the
application/xml
media type. Note that Tl.exe
does not support chunked transfer encoding.<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="https://vezel.dev/novadrop/tl/ServerList"
targetNamespace="https://vezel.dev/novadrop/tl/ServerList"
elementFormDefault="qualified">
<xsd:complexType name="serverlist">
<xsd:sequence>
<xsd:element name="server" type="serverlist_server" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="serverlist_server">
<xsd:sequence>
<xsd:element name="id" type="serverlist_server_id" />
<xsd:element name="ip" type="serverlist_server_ip" />
<xsd:element name="port" type="unsignedShort" />
<xsd:element name="category" type="serverlist_server_category" />
<xsd:element name="name" type="serverlist_server_name" />
<xsd:element name="crowdness" type="serverlist_server_crowdness" />
<xsd:element name="open" type="serverlist_server_open" />
<xsd:element name="permission_mask" type="serverlist_server_permission_mask" />
<xsd:element name="server_stat" type="serverlist_server_server_stat" />
<xsd:element name="popup" type="xsd:string" />
<xsd:element name="language" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="serverlist_server_id">
<xsd:restriction base="xsd:unsignedInt">
<xsd:minInclusive value="1" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="serverlist_server_ip">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="serverlist_server_category">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="sort" type="xsd:unsignedInt" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="serverlist_server_name">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="raw_name" type="xsd:string" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="serverlist_server_crowdness">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="sort" type="xsd:unsignedInt" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="serverlist_server_open">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="sort" type="xsd:unsignedInt" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:simpleType name="serverlist_server_permission_mask">
<xsd:restriction base="xsd:string">
<xsd:pattern value="0x[0-9A-Fa-f]{8}" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="serverlist_server_server_stat">
<xsd:restriction base="xsd:string">
<xsd:pattern value="0x[0-9A-Fa-f]{8}" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="serverlist" type="serverlist" />
</xsd:schema>
Tl.exe
will request JSON-encoded authentication data from launcher.exe
. This message does not have a static message ID. The message contains one of several NUL-terminated strings.gamestr
is only sent when the game is initially launched. This request asks for the full authentication data.ticket
, last_svr
, and char_cnt
are only sent when returning to the server list from an arbiter server.ticket
: Requests the authentication ticket. This can be the same ticket as before, butlauncher.exe
may also choose to authenticate anew and retrieve a fresh ticket.last_svr
: Requests the ID of the last server the the account connected to.char_cnt
: Requests character counts for each server in the server list.
launcher.exe
should respond with the NUL-terminated JSON payload.{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://vezel.dev/novadrop/tl/authentication.schema.json",
"title": "AuthenticationInfo",
"type": "object",
"properties": {
"result-message": {
"type": "string"
},
"result-code": {
"type": "integer",
"minimum": 100,
"maximum": 599
},
"account_bits": {
"type": "string",
"pattern": "^0x[0-9A-Fa-f]{10}$"
},
"ticket": {
"type": "string"
},
"last_connected_server_id": {
"type": "integer",
"minimum": 0
},
"access_level": {
"type": "integer",
"minimum": 0
},
"master_account_name": {
"type": "string"
},
"chars_per_server": {
"anyOf": [
{
"type": "object",
"properties": {},
"additionalProperties": false
},
{
"$ref": "#/$defs/chars_on_server"
},
{
"type": "array",
"items": {
"$ref": "#/$defs/chars_on_server"
}
}
]
},
"user_permission": {
"type": "integer",
"minimum": 0
},
"game_account_name": {
"type": "string"
}
},
"required": [
"result-code",
"master_account_name",
"game_account_name"
],
"$defs": {
"chars_on_server": {
"type": "object",
"properties": {
"id": {
"type": "string",
"pattern": "^[1-9]\\d*$"
},
"char_count": {
"type": "string",
"pattern": "^\\d+$"
}
},
"additionalProperties": false,
"required": [
"id",
"char_count"
]
}
}
}
Some properties are only required depending on the message:
gamestr
: All properties are required.ticket
: Theticket
property is required.last_svr
: Thelast_connected_server_id
property is required.char_cnt
: Thechars_per_server
property is required.
result-message
, account_bits
, access_level
, and user_permission
are completely optional.Tl.exe
will request a URL to be opened in the client's embedded Web browser. This message does not have a static message ID and is not NUL-terminated.The message can be described by the regular expression
^getWebLinkUrl\((\d+),(.*)\)$
. The first group is the ID of a UIWindow
node under the CoherentGTWeb
data center sheet, and the second group is a set of arguments specific to that URL.launcher.exe
should respond with a URL to be opened (without a NUL terminator), or an empty payload to reject the request.Messages sent between
TERA.exe
and Tl.exe
use a simple binary protocol. All messages have static message IDs; responses have different IDs from requests.TERA.exe
will request the (game) account name from Tl.exe
.struct LauncherAccountNameRequest
{
};
Tl.exe
should respond with the account name.struct LauncherAccountNameResponse
{
u16string account_name;
};
account_name
is the name of the game account. It is not NUL-terminated.TERA.exe
will request the session ticket from Tl.exe
.struct LauncherSessionTicketRequest
{
};
Tl.exe
should respond with the session ticket.struct LauncherAccountNameResponse
{
u8string session_ticket;
};
session_ticket
is the authentication session ticket. It is not NUL-terminated.TERA.exe
will request the server list from Tl.exe
.struct LauncherServerListRequest
{
LauncherServerListSortCriteria sort_criterion;
};
sorting
specifies how Tl.exe
should sort the server list. Valid values are as follows:enum LauncherServerListSortCriteria : int32_t
{
LAUNCHER_SERVER_LIST_SORT_CRITERIA_NONE = -1,
LAUNCHER_SERVER_LIST_SORT_CRITERIA_CHARACTERS = 0,
LAUNCHER_SERVER_LIST_SORT_CRITERIA_CATEGORY = 1,
LAUNCHER_SERVER_LIST_SORT_CRITERIA_NAME = 2,
LAUNCHER_SERVER_LIST_SORT_CRITERIA_POPULATION = 4,
};
A few notes on sorting:
- The sort should be stable.
LAUNCHER_SERVER_LIST_SORT_CRITERIA_NONE
indicates thatTl.exe
is free to sort the list arbitrarily.- The resultant list should be maintained between requests and further sorted on each request, unless
LAUNCHER_SERVER_LIST_SORTING_NONE
is sent to reset the sorting. - If the same sorting is requested multiple times and is any sorting other than
LAUNCHER_SERVER_LIST_SORT_CRITERIA_NONE
, the list should simply be reversed without applying sorting.
struct LauncherServerListResponse
{
uint8_t data[];
};
Server List Structures
The server list response can be described with the following message definitions:
syntax = "proto2";
message ServerList
{
message ServerInfo
{
required fixed32 id = 1;
required bytes name = 2;
required bytes category = 3;
required bytes title = 4;
required bytes queue = 5;
required bytes population = 6;
required fixed32 address = 7;
required fixed32 port = 8;
required fixed32 available = 9;
required bytes unavailable_message = 10;
optional bytes host = 11;
}
repeated ServerInfo servers = 1;
required fixed32 last_server_id = 2;
required fixed32 sort_criterion = 3;
}
A few notes on these definitions:
- They will not work correctly as
proto3
due torequired
semantics. bytes
fields are reallyu16string
without NUL terminators.id
must be a positive (non-zero) value.port
should be in theuint16_t
range.available
is really abool
, so only the values0
and1
are allowed.- Either
address
orhost
must be set; not neither and not both.- For
address
, a value of0
has 'not set' semantics since the field is not markedoptional
.
sort_criterion
should be theLauncherServerListSortCriteria
value that was sent in the request.
TERA.exe
will notify Tl.exe
when entering the lobby (i.e. successfully connecting to an arbiter server) or when entering the world on a particular character.struct LauncherEnterLobbyNotification
{
};
struct LauncherEnterWorldNotification
{
u16string character_name;
};
The two cases can be distinguished by looking at the payload length.
character_name
is the NUL-terminated name of the character that the user is entering the world on.- Create Room (
0x8
) - Join Room (
0xa
) - Leave Room (
0xc
) - Set Volume (
0x13
) - Set Microphone (
0x14
) - Silence User (
0x15
)
These messages were only present in some regions and were likely never actually used. It is currently unknown what their payloads contain.
The following responses exist for the above TeamSpeak requests:
- Create Room Result (
0x9
) - Join Room Result (
0xb
) - Leave Room Result (
0xd
)
TERA.exe
asks Tl.exe
to open a website in the default Web browser.struct LauncherOpenWebsiteCommand
{
uint32_t id;
};
id
specifies the kind of website that should be opened. The possible values are currently unknown.This request is the
TERA.exe
equivalent of the request sent by Tl.exe
to launcher.exe
.struct LauncherWebURLRequest
{
uint32_t id;
u16string arguments;
};
id
refers to a UIWindow
node under the CoherentGTWeb
data center sheet.arguments
specifies the NUL-terminated arguments specific to the URL.struct LauncherWebURLResponse
{
uint32_t id;
u16string url;
};
id
is the same value that was sent in the request.url
is the NUL-terminated URL to open. It can be the empty string or the special string |
to indicate that no URL should be opened.TERA.exe
will notify Tl.exe
that it has launched.struct LauncherGameStartNotification
{
uint32_t source_revision;
uint32_t unknown_1;
u16string windows_account_name;
};
source_revision
is the SrcRegVer
value from the client's ReleaseRevision.txt
file.The meaning of
unknown_1
is currently unknown.windows_account_name
is the NUL-terminated name of the current Windows user account.TERA.exe
will occasionally notify Tl.exe
of various notable actions taken by the user.struct LauncherGameEventNotification
{
LauncherGameEvent event;
};
event
specifies the kind of event that occurred. Valid values are as follows:enum LauncherGameEvent : uint32_t
{
LAUNCHER_GAME_EVENT_ENTERED_INTO_CINEMATIC = 1001,
LAUNCHER_GAME_EVENT_ENTERED_SERVER_LIST = 1002,
LAUNCHER_GAME_EVENT_ENTERING_LOBBY = 1003,
LAUNCHER_GAME_EVENT_ENTERED_LOBBY = 1004,
LAUNCHER_GAME_EVENT_ENTERING_CHARACTER_CREATION = 1005,
LAUNCHER_GAME_EVENT_LEFT_LOBBY = 1006,
LAUNCHER_GAME_EVENT_DELETED_CHARACTER = 1007,
LAUNCHER_GAME_EVENT_CANCELED_CHARACTER_CREATION = 1008,
LAUNCHER_GAME_EVENT_ENTERED_CHARACTER_CREATION = 1009,
LAUNCHER_GAME_EVENT_CREATED_CHARACTER = 1010,
LAUNCHER_GAME_EVENT_ENTERED_WORLD = 1011,
LAUNCHER_GAME_EVENT_FINISHED_LOADING_SCREEN = 1012,
LAUNCHER_GAME_EVENT_LEFT_WORLD = 1013,
LAUNCHER_GAME_EVENT_MOUNTED_PEGASUS = 1014,
LAUNCHER_GAME_EVENT_DISMOUNTED_PEGASUS = 1015,
LAUNCHER_GAME_EVENT_CHANGED_CHANNEL = 1016,
};
TERA.exe
will notify Tl.exe
that it is exiting.struct LauncherGameExitNotification
{
uint32_t length;
uint32_t code;
LauncherGameExitReason reason;
};
length
is the length of the payload. It must be 12
.code
is the exit code of the TERA.exe
process. This can be 0
or 1
.reason
provides a more specific exit reason. Some known values are as follows:enum LauncherGameExitReason : uint32_t
{
LAUNCHER_GAME_EXIT_REASON_SUCCESS = 0x0,
LAUNCHER_GAME_EXIT_REASON_INVALID_DATA_CENTER = 0x6,
LAUNCHER_GAME_EXIT_REASON_CONNECTION_DROPPED = 0x8,
LAUNCHER_GAME_EXIT_REASON_INVALID_AUTHENTICATION_INFO = 0x9,
LAUNCHER_GAME_EXIT_REASON_OUT_OF_MEMORY = 0xa,
LAUNCHER_GAME_EXIT_REASON_SHADER_MODEL_3_UNAVAILABLE = 0xc,
LAUNCHER_GAME_EXIT_REASON_SPEED_HACK_DETECTED = 0x10,
LAUNCHER_GAME_EXIT_REASON_UNSUPPORTED_VERSION = 0x13,
LAUNCHER_GAME_EXIT_REASON_ALREADY_ONLINE = 0x106,
};
(The above enumeration is an incomplete list.)
TERA.exe
will notify Tl.exe
that it has crashed (e.g. because of a memory access violation). Note that, since a crash could mean things are arbitrarily broken, this message may not be produced if the crash is sufficiently severe.struct LauncherGameCrashNotification
{
u16string details;
};
details
contains various details about the crash such as the instruction pointer and exception type. It is not NUL-terminated.TERA.exe
will notify Tl.exe
that the anti-cheat module is starting.struct LauncherAntiCheatStartingNotification
{
};
TERA.exe
will notify Tl.exe
that the anti-cheat module has successfully started.struct LauncherAntiCheatStartedNotification
{
};
TERA.exe
will notify Tl.exe
that the anti-cheat module failed to start.struct LauncherAntiCheatStartedNotification
{
uint64_t error;
};
error
contains the error code from the anti-cheat module.TERA.exe
asks Tl.exe
to open the customer support website in the default Web browser.struct LauncherOpenSupportWebsiteCommand
{
};
The exact purpose of this message is currently unknown.
Last modified 3mo ago