OgarProject / Ogar

An open source Agar.io server implementation, written with Node.js.
Other
716 stars 823 forks source link

New Agar.io protocol #569

Open lukerayman opened 8 years ago

lukerayman commented 8 years ago

I work with Wireshark to figure out the new protocol for Agar.io servers. I know how to connect, read leaderboard, cells data. Player actions are not changed, you send the same data to join, spectate, move, split and eject as before. Here is what I already have.

Client -> Server

Initialize connection

The fist two messages initializes connection (the web socket is already open, you open it as before, no changes here).

Size Type Value Comment
1 byte 255 Opcode
4 uint32 6 Protocol version. Before the great protocol change it was 5.

The first message is enough to start getting world update and leaderboard messages. But if you want to play, you probably have to send the following message.

Size Type Value Comment
1 byte 254 Opcode
4 uint32 2106059819 This tricky. This value changes daily or even more often. If you send it wrong, your connection will be closed.

You are connected now. You can join, spectate and play. However, I discovered that your actions may be ignored until you send one more mysterious message.

Size Type Value Comment
1 byte 113 Opcode
20 byte[] 34:ac:3b:fd:3f:90:d5:88:fe:69:3c:9e:01:79:e6:e2:6f:f4:3c:c8 Some key that is different every time you connect to some server.

This key may be SHA-1 of a server token that comes in response after sending POST message to http://m.agar.io/findServer.

Spectate

Just send one byte 0x01.

Move

This message is not changed after the great protocol change.

Size Type Value Comment
1 byte 16 Opcode
4 int32 * Mouse X
4 int32 * Mouse Y
4 uint32 * Probably your cell ID

Other

Probably all the messages are the same as before. Only strings have a new format. It's probably UTF-8 but I'm not sure.

Server -> Client

Clear

This is the first message from the server. It's just a one byte 18 (0x12). It can be sent later again when the server is reset, so the client should remove all the cells from its memory and start a new fresh gameplay.

Leaderboard update

This message contains IDs and names of players in top 10.

Size Type Value Comment
1 byte 49 Opcode
4 uint32 0-10 Number of players on the leaderboard. Mostly 10.

Then you will get a list of players in following structures. Strings are sent in a new format.

Size Type Value Comment
4 uint32 0 Should be player ID, but the value is always 0. I guess it's not 0 only for us.
* string "Doge" Name of the player.

This is how you read strings now: each character is 1 byte long, no 2 as it was before (if it's UTF-8 then special characters can have more bytes). You read the string until you get byte 0. It means the end of the string and you can start reading ID of the next player.

World update

I still work on this but I already know how structures for new cells or updating existing ones look like. The message starts from opcode 255. Then you get the size of the rest of the message.

Size Type Value Comment
1 byte 255 Opcode
4 uint32 * The size of the rest of the message (in bytes). I counted it and the actual massage will have 2 more bytes than this value.
* * * Here's the main content. It starts from something and then you have a list of cells that are new, are just move.

Data structure for a new cell in our viewport looks like this:

Size Type Value Comment
4 uint32 * Cell ID
4 int32 * X position
4 int32 * Y position
2 uint16 0 - 65535 Size
1 byte 0 Flags. I discovered some examples: 0x03 (0011) - virus, 0x0E (1110) - normal cell that uses skin, 0x0A (1010) - normal cell that has no skin.
3 byte[] 0xFF6407 Cell color in RGB format. Colors from agar.io servers always have two bytes with values: 0xFF and 0x07 in random order. The third byte is random. Viruses always have color 0x33FF33.
* string "%snake" The name of the skin. This field is optional. You have to check flags to know whether to read this or skip.
* string "Doge" The name of the cell. For an unnamed cell you will just get byte 0. Then you can start reading ID of the next cell.

Strings are sent in the new format of course. If some call has skin and name, then you will find byte 0 in between these fields as a normal end of the skin string.

This is my guess about flags: 0x01 (0001) - virus 0x02 (0010) - i don't know, but all cells have this one 0x04 (0100) - cell with skin 0x08 (1000) - cell controlled by a user?

Structure for cell update is much simpler. Its size is always 15.

Size Type Value Comment
4 uint32 * Cell ID
4 int32 * X position
4 int32 * Y position
2 uint16 0 - 65535 Size
1 byte * Flags

Camera update

In spectator mode. This packet has not been changed after the great protocol change.

Size Type Value Comment
1 byte 17 Opcode
4 float * Camera X position
4 float * Camera Y position
4 float * Zoom

My cell

You get this message when you join the game.

Size Type Value Comment
1 byte 32 Opcode
4 uint32 * Your cell ID

That's all I know so far. The hardest thing for the client is to obtain this secret number for the second initial message. In the previous version of the protocol you could easily find it in the "main_out.js" file. Right now I have no idea where to get it. Another mysterious part is the message with opcode 113. I guess you can't start playing without sending this message. It worked yesterday, but today it doesn't: your messages to spectate or join the game are ignored.

BiliBiliLzy commented 8 years ago

@Barbosik ._. so how can i do

Barbosik commented 8 years ago

EnTerr: did you tested first message? from this post: https://github.com/OgarProject/Ogar/issues/569#issuecomment-219899289

It seems that it didn't passed.

Also there is something strange.

For example, message FF DC 00 00 00 F1 0A. Your log:

[92]=skip 9, copy 6 
60:43:FA:0E: 73:09:00:00: 94: 
[3C 00]=back 60, copy the 6 
(F2:FF:FF: 4C:00: 00:) 
[A1]=skip 10, copy 5 

My log:

[ 92 ] skip 9, copy 6
60 43 FA 0E 73 09 00 00 94  // skip 9
[ 3C 00 ] back 60, copy 6
F0 FF FF 38 00 00  // copy 6             <= here is difference
[ A1 ] skip 10, copy 5
EnTerr commented 8 years ago

@Barbosik yes, i parsed your 1st msg - didn't you see it above, the https://github.com/OgarProject/Ogar/issues/569#issuecomment-219938109 ? It was the hardest because of the size and the self-references.

Re "strange", (F2:FF:FF: 4C:00: 00:) was wrong - it should be (F0:FF:FF: 38:00: 00) indeed - as you say. My work was all done "by hand", so typos may exist. But the big picture is right.

Barbosik commented 8 years ago

EnTerr: something strange in your log with compression type 0x1x.

Message DC 00 00 00 F1 0A:

[12]=skip 1, copy 6   // why skip for 0x1x?
77:
[1E 00]=back 30, copy the 6  // offset is present

Message 96 01 00 00 F2 82:

[10]=copy 16 more           // why just copy for 0x1x?
(87:D0:B5:D0:BC:D0:A1:D0:BC:D0:BE:D0:B3:D1:83:00:)

// here is missing compression type AF
// here is missing part for cell id = 0C 11 1A 76
// here is missing compression type 13
// here is missing compression type DF

[A1]=skip 10, copy 5
26:12:1A:76:    F6:F4:FF:FF:    7C:F2:                                  
[D2 00]=go back 210, copy the 5

Why in the first message it is used to read bytes and to copy with offset? But in second message it is used to copy only and with no offset?

BiliBiliLzy commented 8 years ago

@Barbosik but im not coder ._. im just is a china player

firelightning13 commented 8 years ago

@BiliBiliLzy be patient

BiliBiliLzy commented 8 years ago

@firelightning13 ._. ok thank you

Barbosik commented 8 years ago

Pattern copy may copy self bytes, it means that copy block should not be used. With this fix repeat works ok.

The question about compression type 0x00...0x1F is still open.

EnTerr commented 8 years ago

@Barbosik - the cut-off in meaning does not have to be right between 0x0f and 0x10. It might be that 0x10 is in the "copy more X" group and 0x12 is in the "skip X and copy Y+4 [with offset Z]".

However you decode it, i advise to always check the result length against the "uncompressed length" uint32 field right after 0xFF prefix. I haven't seen a single packet so far breaking the rule so seems like a great sanity check against mis-interpretation

EnTerr commented 8 years ago

@Barbosik - won't the "new" Agario client work with "old" private servers without 0xff compression envelope?

I mean instead of trying to wrap in pseudo-compression with "skip" messages - just sending the 0x10 cell update but with the new (6) field format (x, y, size, flags [, rgb] [, name] ...) vs the old (5) packing (x, y, size, rgb, flags ...)

I suspect 0xFF is very much optional thing that the server may apply per-packet and if client receives 0xFF, it uncompressed that - but then re-processes the outcome like a regular uncompressed packet.

Barbosik commented 8 years ago

EnTerr: wow, it really accepts messages without compression :)

Do you working for moneyclip? ;)

EnTerr commented 8 years ago

No, it's just logical/simpler that way - can be implemented in the form of a "filter" in the server stack - when server has to send a packet, it tries to compress it and if the result is smaller than the original, sends it as 0xFF way - otherwise send the original.

This is more good news for alternative server writers (because they can just don't deal with the extra complexity of compression - keep code short, server fast) and more bad news for alternative client & bot writers (because potentially any type of packet may arrived wrapped with 0xFF on top of it, it's up to the server).

Barbosik commented 8 years ago

Yes, fix for private server is more easy, because there is no need to use 0xFF messages. Just send usual messages, but with a small fix in the cell update structure. I described this structure for the new protocol here: https://github.com/OgarProject/Ogar/issues/569#issuecomment-219332968

You can just send uncompressed message in the following form and the new client will accept it:

10                                             // update cell message
01 00                                          // 2 eat records
F7 6B 49 77 AA 87 48 77                        // eat record #1
D0 D5 49 77 B8 03 00 00 4C FC FF FF 67 00 00   // cell record #1
F7 6B 49 77 D8 FC FF FF C6 FD FF FF 8B 02 00   // cell record #2
00 00 00 00                                    // cell records terminator
01 00                                          // 1 remove record
AA 87 48 77                                    // remove record #1
Barbosik commented 8 years ago

EnTerr: can you explain please, how you decompressed message X2? from this post: https://github.com/OgarProject/Ogar/issues/569#issuecomment-220168693

Here is log from my parser:

msg3: chunkSize=220
[ F1 0A ] skip 25, copy 5
10 01 00 03 FF FA 0E 6E 43 FA 0E 8B 7E F9 0E E7 0C 00 00 AB F0 FF FF 84 00  // skip 25
[ 17 00 ] back 23, copy 5
00 03 FF FA 0E  // copy 5
[ F1 6F ] skip 126, copy 5
46 0A 00 00 1B F0 FF FF 75 00 00 32 ED FA 0E 45 0A 00 00 E0 F2 FF FF E3 00 00 10 37 FA 0E AF 0A 00 00 CF ED FF FF 6E 00 00 EE 5B FA 0E 70 0C 00 00 B0 F2 FF FF 4C 00 00 6C 55 FA 0E 7D 09 00 00 69 EF FF FF 96 00 00 66 43 FA 0E 8A 09 00 00 A2 F0 FF FF 38 00 00 65 43 FA 0E 99 0A 00 00 75 F0 FF FF 38 00 00 64 43 FA 0E D9 0A 00 00 A0 F0 FF FF 39 00 00 62 43 FA 0E E1 0B 00 00 6D F1  // skip 126
[ 1E 00 ] back 30, copy 5
FF FF 38 00 00  // copy 5
[ 92 ] skip 9, copy 6
60 43 FA 0E 73 09 00 00 94  // skip 9
[ 3C 00 ] back 60, copy 6
F0 FF FF 38 00 00  // copy 6
[ A1 ] skip 10, copy 5
6F 43 FA 0E 3D 08 00 00 DB F2  // skip 10
[ 1E 00 ] back 30, copy 5
FF FF 38 00 00  // copy 5
[ 22 ] skip 2, copy 6
6B 43  // skip 2
[ 87 00 ] back 135, copy 6
FA 0E AF 0A 00 00  // copy 6
[ 12 ] skip 0, copy 18
CF ED FF FF 6E 00 00 EE 5B FA 0E 70 0C 00 00 B0 F2 FF  // copy 18
[ 77 ] skip 7, copy 11
1E 00 E0 00 00 00 00  // skip 7
[ 02 00 ] back 2, copy 11
00 00 00 00 00 00 00 00 00 00 00  // copy 11
[ 29 ] skip 2, copy 13
21 FA  // skip 2
[ 0E 6E ] back 28174, copy 13
[ 43 ] skip 4, copy 7
FA 0E 00 00  // skip 4
msg3: resultSize=241, invalid

something is going wrong here:

FA 0E AF 0A 00 00  // copy 6
[ 12 ] skip 0, copy 18
CF ED FF FF 6E 00 00 EE 5B FA 0E 70 0C 00 00 B0 F2 FF  // copy 18
[ 77 ] skip 7, copy 11
1E 00 E0 00 00 00 00  // skip 7

I tried to use skip 1 for 0x1x, but then I cannot parse this message X1.

According to your logs there is different behavior for 0x1x - you're using skip 1 for message X1 and don't using skip 1 for message X2. Why?

issy123 commented 8 years ago

Hi guys,

I just want to say that maybe we could compress/merge everything that is said here into a single repository for a nice overview. This would make it easier to read and less filtering to get what you need. Agar.io protocol repository is found here:

https://github.com/issy123/agario-protocol/

I also added some reverse engineering techniques that would make it easier to identify the protocol. If there are more techniques please contribute

EnTerr commented 8 years ago

@issy123 - why would we be re-doing it from scratch? I have always used http://agar.gcommer.com/Protocol - and probably rely on that in the future too.

EnTerr commented 8 years ago

@Barbosik - i already decoded both your X1 and X2 above. Why are you asking me again? Did you not see what i said above in https://github.com/OgarProject/Ogar/issues/569#issuecomment-220188093 ?!

So far there is no contradiction. One is using the 0x10 code, the other - 0x12. I have no problem with 0x10 belonging to the 0x00-0x0F "copy more" group, while 0x12 belongs to the other "skip & backcopy" 0x13-0xEF... why would i?

I decoded 0x10 and 0x12 the way i had to do it to make it work, i.e. get the right structure and length. The two examples are telling me how to decode them, "it just fits" that way.

Barbosik commented 8 years ago

EnTerr: the problem is that message X2 uses compression 0x0F, 0x10 and 0x13 and it uses it in the same way. And message X1 uses compression 0x12 which is between 0x10 and 0x13, but it looks like it should be processed in different way than 0x10 and 0x13. So, I'm looking for logic :)

Luka967 commented 8 years ago

There is a big problem ahead. I attempted to spawn but I didn't get ANY packet 0. Instead I got a lot of rubbish packets. See this

EDIT: I also receieved packet 1 even if I didn't want to spectate

Barbosik commented 8 years ago

They just changed protocol to 7 :( And it seems that they encrypted all messages from client to server. It means that they decided to stop using private servers on their client.

Luka967 commented 8 years ago

Well f**k them.

BiliBiliLzy commented 8 years ago

@Barbosik so we cant build self server?

Barbosik commented 8 years ago

BiliBiliLzy: unfortunately yes, now we can't. But we can still use old protocol and alternative clients

Luka967 commented 8 years ago

@ItzLevvie Do you still have the v72 version of agar.io? If yes, we can create a new gh-pages repo and insert the contents there - all done, no protocol switching. We'll go over http://OgarProject.github.io and we'll start the client.

Barbosik commented 8 years ago

Luka967: it seems that they just removed encryption for private servers. At a glance now it works ok with private server. Try your private server now ;)

mb1969 commented 8 years ago

Hi guys,

sorry to be too late but that compression really looks like LZ4-compression.

I've made a quick LZ4 based decompressor hack and the results seem to match.

Perhaps you can confirm.

Let's go to the next puzzle...

issy123 commented 8 years ago

Hi guys,

Sam recently made the anouncement that private servers will now work with the old protocol. From now on you could use agar.io?ip=yourogarserverip:port

Decoding agar.io's protocol has no purpose for ogar.

However if you want your private server want to work 1=1 with agar.io you have to make an client and also make the server that uses the same protocol. Or maybe for other purposes like making an extension for agar.io

But for the majority decoding isn't necessary

Source: https://www.reddit.com/r/Agario/comments/4k876e/private_servers_work_again/

BaumanDev commented 8 years ago

Dude, I still get a white screen, I have an indexSize error too.

firelightning13 commented 8 years ago

So maybe use protocol 6?

BaumanDev commented 8 years ago

How do I update it? I changed nothing...

OutsourcedGuru commented 7 years ago

@lukerayman Hey, dude. You might want to read my six-part blog entries starting with Hacking Agar.io and ultimately ending in the one-trick-pony Node.js repository.

I was mostly interested in defeating the annoying ads after almost every game crash.

NuclearC commented 6 years ago

@OutsourcedGuru I have a lot of info about mobile Agar.io protocol you may have interest in. If you want, I can maybe help you with that

Barbosik commented 6 years ago

@NuclearC this is interesting, some time ago I recorded session for mobile client and it looks like proto messages which is used for shop and stats in the desktop version. I tried it a long time ago, so it may be changed now. Could you give me the way to find mobile servers, I want to try how it works :)

NuclearC commented 6 years ago

@Barbosik I dont really know how to find mobile server, but one thing I figured out. The mobile client connects to load balancer/proxy, which can forward the traffic to any server. And yes they use serialized proto messages (I have almost all of them)

Edit: you can find the actual server by decoding one of the important proto packets that server sends back

mbm3 commented 6 years ago

lol i remember this

btw @NuclearC , do u still have contact with Razor & Sonic

NuclearC commented 6 years ago

@MasonBurdette discord only, https://discord.gg/gbDGw9 join my server so I can give them to you