Game Protocol recap
From RuneWiki
I'm going to recap everything we know about the game server's protocol so beginners and veterans can refine their understanding a bit. We're going to be using some terms and ideas that will conflict with the last 20 years of RSPS, keep an open mind!
We'll cover the basics first and touch on more in-depth details after.
Basics
Data Sizes
A byte is the smallest unit of data you can store in memory. You can edit the bits that make up a byte, but you're always working with bytes.
Signed numbers means they can have a minus sign. They can represent values from -<data range> to <data range>.
Unsigned numbers means they cannot have a minus sign. They can represent values from 0 to <data range>.
Java int types are signed and may represent values between -2,147,483,648 to 2,147,483,647 (-2.147b to 2.147b).
Java byte types are signed and may represent values between -128 and 127.
Numbers can be converted between signed/unsigned at any time if you store the result in the right data types.
TCP
TCP is a byte stream protocol, you can expect to send a byte on one end and the other end will receive that byte in the exact same order (simplification).
Opcodes
Opcodes means "Operation Codes" which is another way to say, if this byte is received then do this. The first byte(s) of a packet are the opcode. Note that I say byte(s), that distinction is for RS after 2010, which can write 2 bytes if the opcode is greater than 127.
Isaac
Isaac is the stream cipher that Jagex uses to encrypt the opcode. Every byte that it processes changes the state permanently and both client/server must be doing the same operations in the same order at all times.
When the client connects, it initializes Isaac with the exact same starting state as the server.
Packet Lengths
Packet lengths for these opcodes can be fixed or variable, lengths are important for two reasons:
TCP has TCP fragmentation, where packets are split between multiple transmissions
Isaac must be deciphering/decrypting the same opcodes, in the same order, on both sides.
A fixed packet length is calculated by adding up all of the bytes that are sent and storing that result ahead of time.
A variable packet length is calculated when packets are written, the byte(s) following the opcode in this case are saying how many bytes the rest of the packet is.
Packet Writing/Reading Methods
The client has a class officially called Packet. Yes, even when it's used to read the cache it's still reading as Packet. People often call this Buffer, InputStream/OutputStream, or ByteStream.
Later revisions there's another class called PacketBit, some people have called this Packet. It contains the methods to read/write bits.
When the client is being obfuscated for release, the network protocol gets randomized in a couple ways...
Opcodes are scrambled - changed randomly each revision and for each platform
Reading/writing methods in the packet handlers may be replaced with their alt variants
Packet handlers are shuffled around to be in random locations, you cannot simply diff them in the same order after decompiling
Read along here: Packet.java
Writing
Jagex Name | RSPS Name | Description |
---|---|---|
p1 | writeByte | |
p2 | writeWord, writeShort | |
p3 | writeMedium | |
p4 | writeInt | |
p5 | ||
p6 | ||
p8 | writeLong | |
pjstr | writeString | |
pjstr2 | ||
pdata | writeBytes | |
psmart | ||
psmarts | ||
pSmart2or4 | ||
pBit |
Writing Alt (Obfuscation)
Jagex Name | RSPS Name | Description |
---|---|---|
p1_alt1 | ||
p1_alt2 | ||
p1_alt3 |
Reading
Jagex Name | RSPS Name | Description |
---|---|---|
g1 | ||
g2 | ||
g3 | ||
g4 | ||
g5 | ||
g6 | ||
g8 | ||
gjstr | ||
gjstr2 | ||
gdata | ||
gsmart | ||
gsmarts | ||
gBit |
Reading Alt (Obfuscation)
Jagex Name | RSPS Name | Description |
---|---|---|
g1_alt1 |
Server Logic
The server has some unique behaviors when processing packets. RSProt has some info covering packet limits that the server receives but I'll go over that here as well.
Client packets are separated into two types - client events (tracking/handled internally by the client) and user events (actionable inputs).
When the server reaches the limit of 10 user events or 50 client events, packet decoding stops for that player this tick and continues again next tick. These limits may vary depending on your revision if you care about that level of detail, OSRS made a change at one point to increase it to 10 user events from 5.
Server packets are also separated into two types - high priority, and low priority. That's two packet queues you write into, this helps improve game responsiveness because the client will only process 5-100 packets every 20ms. The important packets process first (map/info packets/variables) and the lower-priority content packets appear after.
Priority | Name |
---|---|
High | VARP_SMALL / VARP_LARGE |
High | PLAYER_INFO |
Low | IF_SETTEXT |
Deobfuscating Clients
I'm going to take you through what it looks like to know how to read a packet.
if(pktType == 176) {
daysSinceRecovChange = inStream.method427();
unreadMessages = inStream.method435();
membersInt = inStream.readUnsignedByte();
anInt1193 = inStream.method440();
daysSinceLastLogin = inStream.readUnsignedWord();
// ...
pktType = -1;
return true;
}
if (this.packetType == 176) {
this.daysSinceRecoveriesChanged = this.in.g1_alt2();
this.unreadMessages = this.in.g2_alt2();
this.warnMembersInNonMembers = this.in.g1();
this.lastAddress = this.in.g4_alt3();
this.daysSinceLastLogin = this.in.g2();
// ...
this.packetType = -1;
return true;
}
With the right Jagex naming, you can calculate the packet length by just adding 1 (g1) + 2 (g2) + 1 (g1) + 4 (g4) + 2 (g2) and know which methods to use on the "other side" immediately.
Sources
Lost City - 2004Scape (2024)
Blurite - RSProt (2024)
NXT beta client messages (2016)
NXT beta server messages (2016)
NXT beta C++ function names (2016)
Password Applet decompiled (2009)