TCP Protocol

The TCP protocol of Jazz Jackrabbit 2
Related: The UDP Protocol

Introduction

Let’s take a look at how the TCP packets are structured. They have a fairly easy to remember structure that consists of a header and some optional external data. I will be referring to the first two bytes of any packet as the header because they are common to all TCP packets. In a brief description we can describe the packet structure as such:
struct TCP_Packet {
	byte packetLength;	// Length/size of the packet
	byte packetID;		// Identified type of packet
	misc extraData;		// Can be of variable size
};
The second byte of the packet tells us what type of packet it is and what kind of data follows the header, if any. The packetID byte allows up to 256 different values, but for TCP packets only a very narrow range is actually used, 0×0D to 0×1B to be precise, leaving the rest unused.

ID 0×0D – Disconnect client (Server to Client)

struct diconnectClient {
	byte 0x08;		// packetLength, always 8 here
	byte 0x0D;		// packetID
	byte disconnectMessage;	// See table below
	byte socketID;		// The ID of the disconnected socket
	char serverVersion[4];
}
Example: HEX: 08 0D 0C 01 32 31 20 20 ASCII: ....21 This banned socket ID 1 (player 2) from an 1.21 (or actually 1.23) server. If it’s your socket ID then the server closes your socket (if you got kicked/banned etc..) so you wont be able to send or receive more packets.
disconnectMessage Description
0×01 Server full
0×02 Version different
0×03 ?
0×04 Error during handshaking
0×05 Feature not supported in shareware
0×06 Error downloading level
0×07 Connection lost
0×08 Winsock error
0×09 Connection timed out
0×0A Server stopped
0×0B Kicked off
0×0C Banned
else Unknown error

ID 0×0E – Joining details (Client to Server)

struct joinDetails {
	byte packetLength;
	byte 0x0E;
	byte numberOfPlayers;		// How many players are joining?
	struct playerArray[numberOfPlayers] {
		byte playerID;
		byte teamAndChar;	// See below
		byte furColor[4];
		char playerName;	// playerName is null terminated
	}
}
teamAndChar: team = byte & 0x10; //byte can be 0 (blue) or 1 (red). char = byte & 0x03; //byte can be 0, 1, 2 or 3. (Jazz,Spaz,Bird,Lori/Frog) Example of full packet: HEX: 23 0E 02 01 11 40 40 40 40 47 72 79 74 6F 6C 6C 65 43 43 20 00 02 11 28 28 28 28 70 6C 61 79 65 72 32 00 ASCII: ....@@@@GrytolleCC ...((((player2. 2 players joining the server using socketID 1. Players are GrytolleCC and player2. If you are making a GIP-script you may not want the name to be shown (JJ2+ only). In that case, use this as name: §| If you do, keep in mind that you won’t be able to do things that require staying in server, such as downloading level/tileset files, chatting, read chat etc… You are limited to the playerlist and the plus details packet.

ID 0×0F – Joining request (Client to Server)

struct joinRequest {
	byte 0x09;		// packetLength, always 9 here
	byte 0x0F;
	unsigned short UDPbind;	// Alternatively "byte UDPbind[2]", see below
	char clientVersion[4];	// Common values: "21  " for 1.23, "24  " for 1.24
	byte numberOfPlayersFromClient;
}
If you don’t know how to use short (unsigned) integers, use this approach and the alternative UDPbind struct: byte UDPbind[2]; UDPbind[0] = floor(bindport % 256); UDPbind[1] = floor(bindport / 256); Example packet: HEX: 09 0F 10 0E 32 31 20 20 02 ASCII: ....21 . 2 players joining spitscreen with bindport 3600 from an 1.21 client. The bindport is NOT 10052, it is the port you get from listening on an UDP socket. Set bindport to zero if you don’t want to use UDP (like if you’re making a serverlist/GIP script).

ID 0×10 – Server details (Server to Client)

struct serverDetails {
	byte packetLength;
	byte 0x10;
	byte socketID;		// Your socket ID, take care of it
	byte numberOfPlayersFromClient; // Same as sent in joinRequest? Confirmation?
	byte levelFileNameLength;
	char levelFileName[levelFileNameLength];
	int levelCRC;		// Remember, an integer is 4 bytes
	int tilesetCRC;
	byte gameMode;		// See below for a list of gamemodes
	byte maxScore;
	// The following is for JJ2+ only
	int someChecksum[4];	// Seems level-unique
	byte unknownData[4];	// Random each time?
	byte plusVersion[5];	// Server’s JJ2+ version?
	int musicCRC;		// I think this is the music file’s checksum. It is unique for levels with same music.
}
JJ2+ only data:
DA A9 05 7B F9 80 11 30 00 00 03 00 01 08 C0 3D E5
CF C5 57 DD 46 F2 DA F2 00 00 03 00 01 65 2C 2D 86
67 E5 9E 0B 91 5B 63 DB 00 00 03 00 01 A3 63 83 8F
67 E5 9E 0B F4 07 80 F3 00 00 03 00 01 A3 63 83 8F
67 E5 9E 0B F7 0A 67 4D 00 00 03 00 01 A3 63 83 8F
67 E5 9E 0B 4D D8 6B 3A 00 00 03 00 01 A3 63 83 8F
8F E7 D8 53 70 A3 CB FD 00 00 03 00 01 D0 56 89 87
84 BE 29 23 CB 39 F1 56 00 00 03 00 01 96 D0 BD E9
13 E5 AA 74 30 18 3B 1D 00 00 03 00 01 64 D0 FF 6F
E6 35 E9 D0 4D BA A4 AC 00 00 03 00 01 64 D0 FF 6F
BE FB AA 83 23 44 3C 9C 00 00 03 00 01 [musicCRC ]
BE FB AA 83 9D EB 76 5F 00 00 03 00 01 [musicCRC ]
BE FB AA 83 AC 3F AF D0
00 55 ac 87 70 a0 2f c1
00 55 ac 87 d1 35 27 3d
00 55 ac 87 ec 80 e6 9e
I see patterns… clearly four parts
gameMode Description
0×00 Singleplayer
0×01 Cooperative
0×02 Battle
0×03 Race
0×04 Treasure Hunt
0×05 Capture The Flag
else Unknown gamemode
Some related addresses:
  1.23 address
Level CRC32 checksum 0x5D01FC
Tileset CRC32 checksum 0x5D0200
Gamemode 0x5A4B68
Maxscore 0x5D01D0

ID 0×11 – Join notification (Server to Client)

struct joinNotification {
	byte packetLength;
	byte 0x11;
	byte socketID;		// The joiner(s) have this socket ID
	byte numberOfPlayers;
	struct playerArray[numberOfPlayers] {
		byte playerID;
		byte teamAndChar;	// See Joining details
		byte furColor[4];
		char playerName;	// playerName is null terminated
	}
}
This packet is sent to all clients except the one who join , the joiner already have all this information.

ID 0×12 – Server player list (Server to Client)

struct playerList {
	byte packetLength;
	byte 0x12;
	byte numberOfPlayers;
	struct playerArray[numberOfPlayers?] {
		byte socketID;
		byte playerID;
		byte teamAndChar;	// See Joining details
		byte furColor[4];
		char playerName;	// playerName is null terminated
	}
}
Note that numberOfPlayers is sometimes not the total amounts of players. Because of some packet size limit, more packets of this ID could get recieved containing more players.. Note: This packet looks different to JJ2+ clients

ID 0×13 – Game initiation (Server to Client)

struct gameInit {
	byte 0x02;
	byte 0x13;
}
This packet tells the client that the level should show instead of the loading screen.

ID 0×14 – File transmission (Server to Client)

First packet:
packetLength 0x14 packetCount[2] totalAmountofPackets[2] fileNameLength fileName
Later packets:
packetLength 0x14 packetCount[2] fileContent
h4(#h-9-1). TODO: Check if correct

ID 0×15 – Download request (Client to Server)

packetLength 0x15 fileNameLength filename
Example:
HEX: 11 15 0E 6A 6A 32 77 63 33 63 74 66 31 2E 6A 32 6C
ASCII: ...jj2wc3ctf1.j2l
h4(#h-10-1). TODO: Check if correct

ID 0×16 – Level cycle (Server to Client)

struct levelCycle {
	byte packetLength;
	byte 0x16;
	int levelCRC;	// integers are 4 bytes
	int tilesetCRC;
	byte fileNameLength;
	char fileName[fileNameLength];
}

ID 0×17 – End of level (Server to Client)

packetLength 0x17 unknownData
Example packet:
HEX: 17 17 20 FE 62 00 00 10 11 12 13 00 00 00 00 03 00 00 00 00 00 00 00
h4(#h-12-1). TODO: Have a look at the unknown data

I have seen for CTF games there are the final flag scores for the teams, etc..

ID 0×18 – Events update (Server to Client)

struct eventsUpdate {
	byte packetLength;
	byte 0x18;
	byte? checksum;
	unsigned short counter;
	[up to 32 bytes of event states]
}
The number of packets sent depends on the number of generators and triggers in the level, of which the state of each one is represented by a single bit. h4(#h-13-1). TODO: Have a look at the packet overall

ID 0×19 – Server stopped (Server to Client)

0x02 0x19
Tells the clients that the server stopped, then server closes all socket connections.

ID 0×1A – Update request (Client to Server)

0×02 0×1A
Requests an update about spawn and trigger state.
h4(#h-15-1). TODO

How is the data formatted? What packet ID?

ID 0×1B – Chat message (Server to Client/Client to Server)

struct chatMessage {
	byte packetLength;
	byte 0x1B;
	byte fromSocketIDandTeam;	// See below
	byte 0x20;			// A simple space character
	char chatMessage;
}
fromSocketIDandTeam: socketID = fromSocketIDandTeam & 15; team = floor(fromSocketIDandTeam/16);