If delta is enabled for this packet the packet-payload (after the bytes
used by the packet-header) is followed by the delta-header. (See HACKING
to learn how to understand the packet-header.) The delta-header
is a bitvector which represents all non-key fields of the packet. If
the field has changed the corresponding bit is set and the field
value is so included in delta-body. The values of the unchanged fields
will be filled in from an old version at the receiving side. The old
version filled in from is the previous packet of the same kind that has
the same value in each key field. (If the packet's kind don't have any
key fields the previous packet of the same kind is used) If no old
version exists the unchanged fields will be assumed to be zero.
For bool field another optimization called bool-header-folding is
applied. Instead of sending an indicator in the bitvector if the given
bool values has changed (and so using 1 byte for the real value) the
actual value of the bool is transfered in the bitvector bit of this
Another optimization called array-diff is used to reduce the amount of
elements transfered if an array is changed. This is independent of the
delta-header bit i.e. it will only be used if the array has changed
its value and the bit indicates this. Instead of transferring the
whole array only a list of (index, new value of this index) pairs are
transferred. The index is 8bit and the end of this pair list is
denoted by an index of 255.
For fields of struct type (or arrays of struct) the following function
is used to compare entries, where foo stands for the name of the struct:
bool are_foo_equal(const struct foo *a, const struct foo *b);
The declaration of this function must be made available to the generated
code by having it #include the correct header. The includes are hard-
coded in generate_packets.py.
To further reduce the network traffic the (delta) packets are
compressed using the DEFLATE compression algorithm.
To get better compression results multiple packets are
grouped together and compressed into a chunk. This chunk is then
transfered as a normal packet. A chunk packet starts with the 2 byte
length field which every packet has. A chunk packet has no type. A
chunk packet is identified by having a too large length field. If the
length of the packet is over COMPRESSION_BORDER it is a chunk
packet. It will be uncompressed at the receiving side and re-feed into
the receiving queue.
If the length of the chunk packet can't be expressed in the available
space of the 16bit length field (>48kb) the chunk is sent as a jumbo
packet. The difference between a normal chunk packet and a jumbo chunk
packet is that the jumbo packet has JUMBO_SIZE in the size field and
has an additional 4 byte len field after the 2 byte len field. The
second len field contains the size of the whole packet (2 byte
first length field + 4 byte second length field + compressed data).
The size field of a normal chunk packet is its size + COMPRESSION_BORDER.
Packets are grouped for the compression based on the
PACKET_FREEZE_HINT/PACKET_THAW_HINT packet pairs. If the first
(freeze) packet is encountered the packets till the second (thaw)
packet are put into a queue. This queue is then compressed and sent as
a chunk packet. If the compression would expand in size the queued
packets are sent uncompressed as "normal" packets.
The compression level can be controlled by the
FREECIV_COMPRESSION_LEVEL environment variable.
There are four file/filesets involved in the delta protocol:
1) the definition file (common/packets.def).
2) the generator (common/generate_packets.py).
3) the generated files (*/*_gen.[ch] or as a list
client/civclient_gen.c, client/packhand_gen.h, common/packets_gen.c,
common/packets_gen.h, server/hand_gen.h and server/srv_main_gen.c).
4) the overview (README.delta, this file)
The definition file lists all valid packet types with their
fields. The generator takes this as input and creates the generated
For adding and/or removing packets and/or fields you only have to
touch the definition file. If you however plan to change the generated
code (adding more statistics for example) you have to change the
Changing the definition file
Adding a packet:
1) choose an unused packet number. The generator will make sure that
you don't use the same number two times.
2) choose a packet name. It should follow the naming style of the
other packets: PACKET_<group>_<remaining>. <group> may be SERVER,
CITY, UNIT, PLAYER, DIPLOMACY and so on.
3) decide if this packet goes from server to client or client to server
4) choose the field names and types
5) choose packet and field flags
6) write the entry into the corresponding section of common/packets.def
If you add a field which is a struct (say "foobar") you have to write
the following functions: dio_get_foobar, dio_put_foobar and
Removing a packet:
1) add a mandatory capability
2) remove the entry from common/packets.def
Adding a field:
1) add a mandatory capability
2) add a normal field line:
1) add a non-mandatory capability (say "new_version")
2) add a normal field line containing this capability in an add-cap
COORD x; add-cap(new_version)
Removing a field:
1) add a mandatory capability
2) remove the corresponding field line
1) add a non-mandatory capability (say "cleanup")
2) add to the corresponding field line a remove-cap flag
After changing the definition file the generator has to be run. The
common/Makefile will take care of this. You don't need to run
Capabilities and variants
The generator has to generate code which supports different
capabilities at runtime according to the specification given in the
definitions with add-cap and remove-cap. The generator will find the
set of used capabilities for a given packet. Lets say there are two
fields with "add-cap(cap1)" and one field with an "remove-cap(cap2)"
flag. So the set of capabilities are cap1, cap2. At runtime the
generated code may run under 4 different capabilities:
- neither cap1 nor cap2 are set
- cap1 is set but cap2 isn't
- cap1 is not set but cap2 is
- cap1 and cap2 are set
Each of these combinations is called a variant. If n is the number of
capabilities used by the packet the number of variants is 2^n.
For each of these variant a seperate send and receive function will be
generated. The variant for a packet and a connection are calculated
once and then saved in the connection struct.