Packet Schema (.packet) Format: Difference between revisions
Created page with "= Packet Schema (.packet) Format = This page explains how to define Fantasy Tennis packets using the schema based <code>.packet</code> format consumed by <code>FTPacketGen</code>. The generator turns these schema files into Java classes (packets and nested structures), including parsing for client packets and auto writing for server packets. == Where .packet files live == <code>FTPacketGen</code> scans a directory tree for files ending in <code>.packet</code> (<code>ser..." |
No edit summary |
||
| Line 94: | Line 94: | ||
== Supported base types == | == Supported base types == | ||
These are recognized and mapped by the generator | These are recognized and mapped by the generator. Sizes refer to how values are written to / read from the packet payload. | ||
{| class="wikitable" | {| class="wikitable" | ||
! Schema type !! Java type | ! Schema type !! Java type !! Payload representation | ||
|- | |- | ||
| int / int32 / uint32 || int | | int / int32 / uint32 || int || 4 bytes (little-endian) | ||
|- | |- | ||
| long / int64 / uint64 || long | | long / int64 / uint64 || long || 8 bytes (little-endian) | ||
|- | |- | ||
| short / int16 / uint16 || short | | short / int16 / uint16 || short || 2 bytes (little-endian) | ||
|- | |- | ||
| char || char | | char || char || 2 bytes (UTF-16 / Java <code>char</code>) | ||
|- | |- | ||
| byte / int8 / uint8 || byte | | byte / int8 / uint8 || byte || 1 byte | ||
|- | |- | ||
| bool / boolean || boolean | | bool / boolean || boolean || 1 byte (0 = false, 1 = true) | ||
|- | |- | ||
| float || float | | float || float || 4 bytes (IEEE-754, little-endian) | ||
|- | |- | ||
| double || double | | double || double || 8 bytes (IEEE-754, little-endian) | ||
|- | |- | ||
| string || String | | string || String || Variable length, null-terminated (UTF-16LE by default; UTF-8 if specified) | ||
|- | |- | ||
| date || java.util.Date | | date || java.util.Date || 8 bytes (Windows FILETIME) | ||
|- | |- | ||
| bytes || byte[] | | bytes || byte[] || Raw byte sequence (length defined by schema) | ||
|} | |} | ||
Anything else is treated as a custom/nested message type (must be defined as <code>message <TypeName> { ... }</code> somewhere in the scanned schema set). | Anything else is treated as a custom/nested message type (must be defined as <code>message <TypeName> { ... }</code> somewhere in the scanned schema set). | ||
''Notes:'' | |||
* All numeric values are written using little-endian byte order. | |||
* <code>char</code> corresponds to Java <code>char</code> and is always 2 bytes wide. | |||
* String encoding can be overridden using <code>[encoding=utf8]</code> or <code>[len=...]</code>. | |||
== repeated fields == | == repeated fields == | ||
Revision as of 15:24, 18 December 2025
Packet Schema (.packet) Format
This page explains how to define Fantasy Tennis packets using the schema based .packet format consumed by FTPacketGen. The generator turns these schema files into Java classes (packets and nested structures), including parsing for client packets and auto writing for server packets.
Where .packet files live
FTPacketGen scans a directory tree for files ending in .packet (server-core/src/main/packets/). The folder structure becomes the Java package:
com.jftse.server.core.shared.packets.<relative folders...>
Example:
server-core/src/main/packets/
auth/
CMSG_Login.packet
Generates classes in:
com.jftse.server.core.shared.packets.auth
Message blocks
A schema file can contain one or multiple message blocks.
Packet message (has packet id)
Use this to define an actual network packet (generates a class that implements IPacket):
message CMSG_Login (0xFA1) {
string username = 1;
string password = 2 [encoding = utf8];
int32 version = 3;
byte unk0 = 4;
string hwid = 5 [encoding = utf8];
}
- The
(0x....)makes it a packet class and setsPACKET_ID. - If the message name starts with
CMSG(case-insensitive), it is treated as a client packet (read/parse from bytes). - Otherwise it is treated as a server packet (write/build to bytes).
Struct / nested message (no packet id)
Use this to define a reusable structure you can reference as a field type in other messages:
message Account {
int32 id = 1;
int32 id2 = 2;
byte tutorialCount = 3;
int32 lastPlayedPlayerId = 4;
boolean gameMaster = 5;
}
Then use it inside a packet:
message SMSG_PlayerList (0x1005) {
Account account = 1;
repeated Player players = 2;
}
Field syntax
Each field line follows this shape:
[repeated] <type> <name> = <number> [<options>];
Examples:
int32 gold = 1;
repeated int32 itemIds = 2;
string nickname = 3 [len=16];
Field numbering rules (IMPORTANT)
Field numbers are validated strictly:
- Must start at
1 - Must be sequential with no gaps (
1,2,3,...) - Must not contain duplicates
If you skip a number (or duplicate one), generation fails.
Reserved field names
Do not use these field names:
datametaData
They are reserved internally by the generated packet classes.
Supported base types
These are recognized and mapped by the generator. Sizes refer to how values are written to / read from the packet payload.
| Schema type | Java type | Payload representation |
|---|---|---|
| int / int32 / uint32 | int | 4 bytes (little-endian) |
| long / int64 / uint64 | long | 8 bytes (little-endian) |
| short / int16 / uint16 | short | 2 bytes (little-endian) |
| char | char | 2 bytes (UTF-16 / Java char)
|
| byte / int8 / uint8 | byte | 1 byte |
| bool / boolean | boolean | 1 byte (0 = false, 1 = true) |
| float | float | 4 bytes (IEEE-754, little-endian) |
| double | double | 8 bytes (IEEE-754, little-endian) |
| string | String | Variable length, null-terminated (UTF-16LE by default; UTF-8 if specified) |
| date | java.util.Date | 8 bytes (Windows FILETIME) |
| bytes | byte[] | Raw byte sequence (length defined by schema) |
Anything else is treated as a custom/nested message type (must be defined as message <TypeName> { ... } somewhere in the scanned schema set).
Notes:
- All numeric values are written using little-endian byte order.
charcorresponds to Javacharand is always 2 bytes wide.- String encoding can be overridden using
[encoding=utf8]or[len=...].
repeated fields
Use repeated for lists/arrays (except bytes, which is already a raw byte array).
repeated int32 values = 1;
repeated Player players = 2;
How repeated is encoded
By default, repeated fields are encoded as:
- a count prefix (1 byte by default), then
- that many elements
On the read side, the count prefix is always read as a single byte unless you force fixed length via [len=...].
On the write side, you can control the numeric type used for the count prefix via [type=...].
Options
Options go in square brackets:
string name = 1 [len=16];
repeated Player players = 2 [type=short];
Options are parsed as:
key = value
separated by commas.
len (strings, bytes, repeated)
len changes how a field is read/written.
string + len
Reads a fixed-length string (UTF-8) of exactly len bytes:
string nickname = 1 [len=16];
bytes + len
Reads exactly len bytes:
bytes payload = 1 [len=64];
If bytes has no len, it reads “the rest of the packet payload”.
repeated + len
Reads a fixed number of elements (no count prefix is consumed on read):
repeated int32 fixedSet = 1 [len=10];
type (size/count prefix control)
type controls the integer type used when writing sizes/counts (and can also force a cast for some primitives).
repeated + type
Controls the numeric type used to write the list size:
repeated Player players = 1 [type=short];
This writes (short) players.size() before the elements.
primitive field + type
If you set type=... on a supported primitive field, the generator will cast when writing. This is useful when the schema expresses a value as int32 but the protocol stores it smaller:
int32 flags = 1 [type=byte];
encoding=utf8 (strings)
For writing strings:
- Default writing uses UTF-16LE +
0x0000terminator. - With
[encoding=utf8]it writes UTF-8 +0x00terminator.
string password = 1 [encoding = utf8];
Reading strings:
- Variable-length
stringis auto-detected by the generated reader (UTF-16LE vs ASCII/UTF-8 style). - Fixed-length strings (
[len=...]) are read as UTF-8 bytes.
Client vs Server packet behavior
The generator treats packets differently depending on the message name.
Client packets (CMSG*)
If the message name starts with CMSG / cmsg:
- The generated class registers itself with
PacketRegistryin a static block. fromBytes(...)parses all defined fields in schema order.- A generic
read(Class<T>)API exists plus type-specific read helpers.
These are also included in the generated PacketAutoRegister list.
Server packets (everything else)
For non-CMSG packet messages:
- The builder’s
build()writes fields into the internal byte buffer automatically (in schema order). toBytes()returns the full header + payload.
Nested/composite messages
Any message without a packet id becomes a plain Java class with fields, getters/setters, and a builder.
When you reference that message name as a field type inside a packet, the generator writes/reads its subfields in order.
Full example: Player list packet
This shows nested messages and a repeated composite type.
message Account {
int32 id = 1;
int32 id2 = 2;
byte tutorialCount = 3;
int32 lastPlayedPlayerId = 4;
boolean gameMaster = 5;
}
message ClothEquipment {
int32 hair = 1;
int32 face = 2;
int32 dress = 3;
int32 pants = 4;
int32 socks = 5;
int32 shoes = 6;
int32 gloves = 7;
int32 racket = 8;
int32 glasses = 9;
int32 bag = 10;
int32 hat = 11;
int32 dye = 12;
}
message Player {
int32 id = 1;
string name = 2;
byte level = 3;
boolean created = 4;
boolean canDelete = 5;
int32 gold = 6;
byte playerType = 7;
byte str = 8;
byte sta = 9;
byte dex = 10;
byte wil = 11;
byte statPoints = 12;
boolean oldRenameAllowed = 13;
boolean renameAllowed = 14;
ClothEquipment clothEquipment = 15;
}
message SMSG_PlayerList (0x1005) {
Account account = 1;
repeated Player players = 2;
}
Practical tips / gotchas
- Keep schema field order exactly matching the on wire protocol order. The generator reads/writes strictly in schema order.
- Use
[type=short](or another type) onrepeatedif the protocol’s list length prefix is not 1 byte. - Use
bytesfor raw data blocks; don’t tryrepeated byteunless you specifically want a length-prefixed list of bytes. - Avoid
dataandmetaDataas field names. - Don’t skip field numbers, generation will fail.