-
Notifications
You must be signed in to change notification settings - Fork 10
Protocol
The diep.io client and server communicate by means of HTML5 Websockets, a low-overhead protocol that allows for two-way information exchange and "push notifications".
By "wrapping" the Javascript WebSocket send
and onmessage
methods, the provided Chrome extension
can snoop on the connection (e.g. view and modify packets to/from the server) without worrying about encryption. A relevant code sample from the extension is as follows:
var wsInstances = new Set();
window.WebSocket.prototype.send = function(data) {
if(!wsInstances.has(this)){
console.log("New WebSocket Used:");
console.log(this);
wsInstances.add(this);
// Snoop on incoming websocket traffic.
var inst = this;
var proxiedRecv = inst.onmessage;
this.onmessage = function(event) {
event = handleRecvData.call(this, event, proxiedRecv);
return proxiedRecv.call(this, event);
};
console.log("Successfully hijacked onmessage handler.");
}
data = handleSendData.call(this, data);
return proxiedSend.call(this, data);
};
Note: Sizes are given in bytes and all multi-byte values are little-endian.
Name | Size | Description |
---|---|---|
uint8 | 1 | Unsigned 8-bit int |
uint16 | 2 | Unsigned 16-bit int |
uint32 | 4 | Unsigned 32-bit int |
int8 | 1 | Signed 8-bit int |
float32 | 4 | Signed 4-bit float |
float64 | 8 | Signed 8-bit float |
string | ≥1 | Null-terminated UTF-8 string |
To correctly decode the strings (which are encoded in UTF-8) represented as bytes, the following function can be used:
function decodeUTF8(bytes) {
// From: https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
var s = '';
var i = 0;
while (i < bytes.length) {
var c = bytes[i++];
if (c > 127) {
if (c > 191 && c < 224) {
if (i >= bytes.length) throw 'UTF-8 decode: incomplete 2-byte sequence';
c = (c & 31) << 6 | bytes[i] & 63;
} else if (c > 223 && c < 240) {
if (i + 1 >= bytes.length) throw 'UTF-8 decode: incomplete 3-byte sequence';
c = (c & 15) << 12 | (bytes[i] & 63) << 6 | bytes[++i] & 63;
} else if (c > 239 && c < 248) {
if (i+2 >= bytes.length) throw 'UTF-8 decode: incomplete 4-byte sequence';
c = (c & 7) << 18 | (bytes[i] & 63) << 12 | (bytes[++i] & 63) << 6 | bytes[++i] & 63;
} else throw 'UTF-8 decode: unknown multibyte start 0x' + c.toString(16) + ' at index ' + (i - 1);
++i;
}
if (c <= 0xffff) s += String.fromCharCode(c);
else if (c <= 0x10ffff) {
c -= 0x10000;
s += String.fromCharCode(c >> 10 | 0xd800)
s += String.fromCharCode(c & 0x3FF | 0xdc00)
} else throw 'UTF-8 decode: code point 0x' + c.toString(16) + ' exceeds UTF-16 reach';
}
return s;
}
The client always sends the server a Javascript Int8Array
(an array of 8-bit signed integers). These
packets' functionality vary by header (first number) and by length.
All the following packets are organized by category and header value.
This packet contains information about the player.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
+1...3 | int8[3] | Cursor X coordinate relative to map |
+4 | uint8 | ??? |
+5...7 | int8[3] | Cursor Y coordinate relative to map |
+8 | uint8 | ??? |
+9 | uint8 | Tank movement and firing |
An array of at least size 9 that has the last number 1
, for example [1, -116, -5, -32, 15, -116, -65, -63, 6, 1]
. If auto-fire is turned on, all following packets will have last number 1
(unless you are also moving).
Note that all cursor information is encoded as normal in the other fields of the packet.
An array of at least size 9 with a last number greater than 1
, for example [1, -116, -1, -128, 5, -116, -69, -95, 9, 4]
. Last byte corresponds to the keys that are currently pressed, and the corresponding bit is set by a logical OR operation. The five possible masks are as follows:
Key | Code (byte) | Code (binary) |
---|---|---|
Mouse left button | 0x01 |
0b00000001 |
Up Arrow | 0x02 |
0b00000010 |
Left Arrow | 0x04 |
0b00000100 |
Down Arrow | 0x08 |
0b00001000 |
Right Arrow | 0x10 |
0b00010000 |
Some examples of last numbers and their corresponding directions are as follows:
Last Number | Direction |
---|---|
2 | North |
4 | West |
6 | Northwest |
8 | South |
12 | Southwest |
16 | East |
18 | Northeast |
24 | Southeast |
An array of variable size, where the first number is 2, contains the name you choose encoded in UTF-8 followed by a \0
. For example, if I choose the name "bob", my browser would send the packet [2, 98, 111, 98, 0]
.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
+1 | string | Your chosen name |
Upgrade Packets (0x03
or 0x04
)
An array of size 2 with header value 3 is sent when you upgrade a parameter (for example, movement speed) and takes the form [3, X]
. For actual tank upgrades (e.g. to Twin, Tri-Angle, etc.), a packet with header 4 of the form [4, Y]
is sent.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
+1 | uint8 | X or Y value |
More information on parameter upgrade packets, including all X and Y-values as well as their corresponding parameters or tanks, is available on the wiki page entitled "Upgrade Packets".
An array of size 1 will simply be an array of size 1 containing only the number 5 (e.g. [5]
). These appear to be a heartbeat/keep-alive message and do not contain any other information.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
-
An array of size 6 seems to be sent whenever the mouse cursor starts moving over the spawn screen (e.g. when it says "this is the tale of...").
-
An array of size 7 also is only sent when viewing the spawn screen, but this one is sent when the cursor is moving (as opposed to starting to move).
-
An array of size 8 has an unknown purpose, but we have observed length-8 packets (just not sure what they do yet).
-
An array of size 9 or 10 is only sent when you are either in the game, or observing it, and appears to contain information about your cursor location and angle (e.g. cursor 120 degrees from the center at point AxB, etc.)
The server sends the client an ArrayBuffer
, with the difference being that these appear to have random length,
but do somehow communicate the locations/types of other tanks/shapes/etc. Note that we have not deciphered
much of the server-to-client protocol yet.
We have discovered that that the first 2 8-bit categories appear to be a header for the message:
-
The first 8-bit sequence contains either a 0 or a 2. If it contains a 0, it is a "regular" packet, and if it contains 2, it appears to be a special packet.
-
The second 8-bit sequence is used as a "counter" if the first number is 0 (you can observe this in the image above). If the first number is 2, it contains information about the current game, such as the leaderboard (read below).
Unfortunately, server-to-client communication remains largely undeciphered.
In the client JS code (filename: d.js
), the (prettified) section of code responsible for
handling server communications is:
b.onmessage = function(a) {
a = new Uint8Array(a.data);
var c = D(a.length);
ka(a, c);
b.events.push([1, c, a.length]);
va()
}
Interestingly enough, the b.events.push([1, c, a.length])
line always appears to push [1, INTEGER1
, INTEGER2
]. INTEGER1
is computed by the D(a.length)
method that does some complicated number crunching and returns an
integer.
The ka(a, c)
method appears to simply copy the values in array a
to array c
, and va()
appears to be a handler to redraw or update the interface.
This packet may contain information about the server.
Offset | Value Type | Description |
---|---|---|
+0 | uint8 | Packet Header |
+1 | uint8[2-3] | Server Uptime |
??? | ??? | ??? |
This packet has an unknown purpose.
This packet contains a message about the server (most likely server name). It cannot be detected by our script.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
+1 | string | Server info |
An array of size 1 will simply be an array of size 1 containing only the number 5 (e.g. [5]
). These appear to be a heartbeat/keep-alive message and do not contain any other information.
Offset | Type | Description |
---|---|---|
+0 | uint8 | Packet header |
This section has been moved to Server Packets