summaryrefslogtreecommitdiff
path: root/docs/protocol.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/protocol.md')
-rw-r--r--docs/protocol.md366
1 files changed, 366 insertions, 0 deletions
diff --git a/docs/protocol.md b/docs/protocol.md
new file mode 100644
index 00000000..c14ecb81
--- /dev/null
+++ b/docs/protocol.md
@@ -0,0 +1,366 @@
1# Enigma protocol
2Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being
3concatenated one after the other.
4
5In this document, data will be represented in C-like pseudocode. The primitive data types will be the same as those
6defined by Java's [DataOutputStream](https://docs.oracle.com/javase/7/docs/api/java/io/DataOutputStream.html), i.e. in
7big-endian order for multi-byte integers (`short`, `int` and `long`). The one exception is for Strings, which do *not*
8use the same modified UTF format as in `DataOutputStream`, I repeat, the normal `writeUTF` method in `DataOutputStream`
9(and the corresponding method in `DataInputStream`) should *not* be used. Instead, there is a custom `utf` struct for
10Strings, see below.
11
12## Login protocol
13```
14Client Server
15| |
16| Login |
17| >>>>>>>>>>>>> |
18| |
19| SyncMappings |
20| <<<<<<<<<<<<< |
21| |
22| ConfirmChange |
23| >>>>>>>>>>>>> |
24```
251. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client,
26 as well as allowing the client to declare metadata about itself, such as the username.
271. After validating the login packet, the server sends all its mappings to the client, and the client will apply them.
281. Upon receiving the mappings, the client sends a `ConfirmChange` packet with `sync_id` set to 0, to confirm that it
29 has received the mappings and is in sync with the server. Once the server receives this packet, the client will be
30 allowed to modify mappings.
31
32The server will not accept any other packets from the client until this entire exchange has been completed.
33
34## Kicking clients
35When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which
36contains the reason why the client was kicked (so the client can display it to the user). This is not required though -
37the server may simply terminate the connection.
38
39## Changing mappings
40This section uses the example of renaming, but the same pattern applies to all mapping changes.
41```
42Client A Server Client B
43| | |
44| RenameC2S | |
45| >>>>>>>>> | |
46| | |
47| | RenameS2C |
48| | >>>>>>>>>>>>> |
49| | |
50| | ConfirmChange |
51| | <<<<<<<<<<<<< |
52```
53
541. Client A validates the name and updates the mapping client-side to give the impression there is no latency >:)
551. Client A sends a rename packet to the server, notifying it of the rename.
561. The server assesses the validity of the rename. If it is invalid for whatever reason (e.g. the mapping was locked or
57 the name contains invalid characters), then the server sends an appropriate packet back to client A to revert the
58 change, with `sync_id` set to 0. The server will ignore any `ConfirmChange` packets it receives in response to this.
591. If the rename was valid, the server will lock all clients except client A from being able to modify this mapping, and
60 then send an appropriate packet to all clients except client A notifying them of this rename. The `sync_id` will be a
61 unique non-zero value identifying this change.
621. Each client responds to this packet by updating their mappings locally to reflect this change, then sending a
63 `ConfirmChange` packet with the same `sync_id` as the one in the packet they received, to confirm that they have
64 received the change.
651. When the server receives the `ConfirmChange` packet, and another change to that mapping hasn't occurred since, the
66 server will unlock that mapping for that client and allow them to make changes again.
67
68## Packets
69```c
70struct Packet {
71 unsigned short packet_id;
72 data[]; // depends on packet_id
73}
74```
75The IDs for client-to-server packets are as follows:
76- 0: `Login`
77- 1: `ConfirmChange`
78- 2: `Rename`
79- 3: `RemoveMapping`
80- 4: `ChangeDocs`
81- 5: `MarkDeobfuscated`
82- 6: `Message`
83
84The IDs for server-to-client packets are as follows:
85- 0: `Kick`
86- 1: `SyncMappings`
87- 2: `Rename`
88- 3: `RemoveMapping`
89- 4: `ChangeDocs`
90- 5: `MarkDeobfuscated`
91- 6: `Message`
92- 7: `UserList`
93
94### The utf struct
95```c
96struct utf {
97 unsigned short length;
98 byte data[length];
99}
100```
101- `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of
102 Unicode characters in the string.
103- `data`: A standard UTF-8 encoded byte array representing the string.
104
105### The Entry struct
106```c
107enum EntryType {
108 ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3;
109}
110struct Entry {
111 unsigned byte type;
112 boolean has_parent;
113 if<has_parent> {
114 Entry parent;
115 }
116 utf name;
117 boolean has_javadoc;
118 if<has_javadoc> {
119 utf javadoc;
120 }
121 if<type == ENTRY_FIELD || type == ENTRY_METHOD> {
122 utf descriptor;
123 }
124 if<type == ENTRY_LOCAL_VAR> {
125 unsigned short index;
126 boolean parameter;
127 }
128}
129```
130- `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`.
131- `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their
132 containing class as their parent. Local variables have a method as a parent.
133- `name`: The class/field/method/variable name.
134- `javadoc`: The javadoc of an entry, if present.
135- `descriptor`: The field/method descriptor.
136- `index`: The index of the local variable in the local variable table.
137- `parameter`: Whether the local variable is a parameter.
138
139### The Message struct
140```c
141enum MessageType {
142 MESSAGE_CHAT = 0,
143 MESSAGE_CONNECT = 1,
144 MESSAGE_DISCONNECT = 2,
145 MESSAGE_EDIT_DOCS = 3,
146 MESSAGE_MARK_DEOBF = 4,
147 MESSAGE_REMOVE_MAPPING = 5,
148 MESSAGE_RENAME = 6
149};
150typedef unsigned byte message_type_t;
151
152struct Message {
153 message_type_t type;
154 union { // Note that the size of this varies depending on type, it is not constant size
155 struct {
156 utf user;
157 utf message;
158 } chat;
159 struct {
160 utf user;
161 } connect;
162 struct {
163 utf user;
164 } disconnect;
165 struct {
166 utf user;
167 Entry entry;
168 } edit_docs;
169 struct {
170 utf user;
171 Entry entry;
172 } mark_deobf;
173 struct {
174 utf user;
175 Entry entry;
176 } remove_mapping;
177 struct {
178 utf user;
179 Entry entry;
180 utf new_name;
181 } rename;
182 } data;
183};
184```
185- `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`,
186 `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`.
187- `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT`
188- `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT`
189- `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT`
190- `edit_docs`: Sent when a user edits the documentation of an entry. Use in case `type` is `MESSAGE_EDIT_DOCS`
191- `mark_deobf`: Sent when a user marks an entry as deobfuscated. Use in case `type` is `MESSAGE_MARK_DEOBF`
192- `remove_mapping`: Sent when a user removes a mapping. Use in case `type` is `MESSAGE_REMOVE_MAPPING`
193- `rename`: Sent when a user renames an entry. Use in case `type` is `MESSAGE_RENAME`
194- `user`: The user that performed the action.
195- `message`: The message the user sent.
196- `entry`: The entry that was modified.
197- `new_name`: The new name for the entry.
198
199
200### Login (client-to-server)
201```c
202struct LoginC2SPacket {
203 unsigned short protocol_version;
204 byte checksum[20];
205 unsigned byte password_length;
206 char password[password_length];
207 utf username;
208}
209```
210- `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be
211 kicked immediately. Currently always equal to 0.
212- `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file
213 the server has open, the client will be kicked.
214- `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type.
215 If this password is incorrect, the client will be kicked.
216- `username`: the username of the user logging in. If the username is not unique, the client will be kicked.
217
218### ConfirmChange (client-to-server)
219```c
220struct ConfirmChangeC2SPacket {
221 unsigned short sync_id;
222}
223```
224- `sync_id`: the sync ID to confirm.
225
226### Rename (client-to-server)
227```c
228struct RenameC2SPacket {
229 Entry obf_entry;
230 utf new_name;
231 boolean refresh_class_tree;
232}
233```
234- `obf_entry`: the obfuscated name and descriptor of the entry to rename.
235- `new_name`: what to rename the entry to.
236- `refresh_class_tree`: whether the class tree on the sidebar of Enigma needs refreshing as a result of this change.
237
238### RemoveMapping (client-to-server)
239```c
240struct RemoveMappingC2SPacket {
241 Entry obf_entry;
242}
243```
244- `obf_entry`: the obfuscated name and descriptor of the entry to remove the mapping for.
245
246### ChangeDocs (client-to-server)
247```c
248struct ChangeDocsC2SPacket {
249 Entry obf_entry;
250 utf new_docs;
251}
252```
253- `obf_entry`: the obfuscated name and descriptor of the entry to change the documentation for.
254- `new_docs`: the new documentation for this entry, or an empty string to remove the documentation.
255
256### MarkDeobfuscated (client-to-server)
257```c
258struct MarkDeobfuscatedC2SPacket {
259 Entry obf_entry;
260}
261```
262- `obf_entry`: the obfuscated name and descriptor of the entry to mark as deobfuscated.
263
264### Message (client-to-server)
265```c
266struct MessageC2SPacket {
267 utf message;
268}
269```
270- `message`: The text message the user sent.
271
272### Kick (server-to-client)
273```c
274struct KickS2CPacket {
275 utf reason;
276}
277```
278- `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user.
279
280### SyncMappings (server-to-client)
281```c
282struct SyncMappingsS2CPacket {
283 int num_roots;
284 MappingNode roots[num_roots];
285}
286struct MappingNode {
287 NoParentEntry obf_entry;
288 boolean is_named;
289 if<is_named> {
290 utf name;
291 boolean has_javadoc;
292 if<has_javadoc> {
293 utf javadoc;
294 }
295 }
296 unsigned short children_count;
297 MappingNode children[children_count];
298}
299typedef { Entry but without the has_parent or parent fields } NoParentEntry;
300```
301- `roots`: The root mapping nodes, containing all the entries without parents.
302- `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry.
303- `name`: The deobfuscated name of the entry, if it has a mapping.
304- `javadoc`: The documentation for the entry, if it is named and has documentation.
305- `children`: The children of this node
306
307### Rename (server-to-client)
308```c
309struct RenameS2CPacket {
310 unsigned short sync_id;
311 Entry obf_entry;
312 utf new_name;
313 boolean refresh_class_tree;
314}
315```
316- `sync_id`: the sync ID of the change for locking purposes.
317- `obf_entry`: the obfuscated name and descriptor of the entry to rename.
318- `new_name`: what to rename the entry to.
319- `refresh_class_tree`: whether the class tree on the sidebar of Enigma needs refreshing as a result of this change.
320
321### RemoveMapping (server-to-client)
322```c
323struct RemoveMappingS2CPacket {
324 unsigned short sync_id;
325 Entry obf_entry;
326}
327```
328- `sync_id`: the sync ID of the change for locking purposes.
329- `obf_entry`: the obfuscated name and descriptor of the entry to remove the mapping for.
330
331### ChangeDocs (server-to-client)
332```c
333struct ChangeDocsS2CPacket {
334 unsigned short sync_id;
335 Entry obf_entry;
336 utf new_docs;
337}
338```
339- `sync_id`: the sync ID of the change for locking purposes.
340- `obf_entry`: the obfuscated name and descriptor of the entry to change the documentation for.
341- `new_docs`: the new documentation for this entry, or an empty string to remove the documentation.
342
343### MarkDeobfuscated (server-to-client)
344```c
345struct MarkDeobfuscatedS2CPacket {
346 unsigned short sync_id;
347 Entry obf_entry;
348}
349```
350- `sync_id`: the sync ID of the change for locking purposes.
351- `obf_entry`: the obfuscated name and descriptor of the entry to mark as deobfuscated.
352
353### Message (server-to-client)
354```c
355struct MessageS2CPacket {
356 Message message;
357}
358```
359
360### UserList (server-to-client)
361```c
362struct UserListS2CPacket {
363 unsigned short len;
364 utf user[len];
365}
366```