28 #include <QRegularExpression>
34 #define JUMBO_SIZE 0xffff
39 #define COMPRESSION_BORDER (16 * 1024 + 1)
40 #define PACKET_STRVEC_SEPARATOR '\3'
44 #define JUMBO_BORDER (64 * 1024 - COMPRESSION_BORDER - 1)
46 #define log_compress log_debug
47 #define log_compress2 log_debug
49 #define MAX_DECOMPRESSION 400
55 #define PACKET_SIZE_STATISTICS 0
72 static int level = -2;
75 const char *s = getenv(
"FREECIV_COMPRESSION_LEVEL");
93 QScopedArrayPointer<Bytef> compressed(
new Bytef[compressed_size]);
95 unsigned long compressed_packet_len;
97 error = compress2(compressed.data(), &compressed_size,
110 compressed_packet_len = compressed_size + (jumbo ? 6 : 2);
111 if (compressed_packet_len < pconn->compression.queue.size) {
114 log_compress(
"COMPRESS: compressed %lu bytes to %ld (level %d)",
116 compressed_size, compression_level);
121 unsigned char header[2];
123 uncompressed_compressed_packet_len_overlap);
125 log_compress(
"COMPRESS: sending %ld as normal", compressed_size);
132 unsigned char header[6];
134 compressed_normal_jumbo_packet_len_overlap);
136 log_compress(
"COMPRESS: sending %ld as jumbo", compressed_size);
144 log_compress(
"COMPRESS: would enlarge %lu bytes to %ld; "
145 "sending uncompressed",
147 compressed_packet_len);
165 "Too many calls to conn_compression_thaw on %s!",
177 enum packet_type packet_type)
182 log_packet(
"sending packet type=%s(%d) len=%d to %s",
187 pc->
client.last_request_id_used =
189 result = pc->
client.last_request_id_used;
204 #define MAX_LEN_COMPRESS_QUEUE (MAX_LEN_BUFFER / 2)
206 compress_queue_maxlen_too_big);
212 log_compress2(
"COMPRESS: huge queue, forcing to flush (%lu/%lu)",
228 log_compress(
"COMPRESS: sending %s alone (%d bytes total)",
232 log_compress2(
"COMPRESS: STATS: alone=%d compression-expand=%d "
233 "compression (before/after) = %d/%d",
236 log_compress2(
"COMPRESS: STATS: alone=%d compression-expand=%d "
237 "compression (before/after) = %d/%d",
241 #if PACKET_SIZE_STATISTICS
246 } packets_stats[PACKET_LAST];
247 static int packet_counter = 0;
248 static int last_start_turn_seen = -1;
249 static bool start_turn_seen = FALSE;
255 if (!packet_counter) {
258 for (i = 0; i < PACKET_LAST; i++) {
259 packets_stats[i].counter = 0;
260 packets_stats[i].size = 0;
264 packets_stats[packet_type].counter++;
265 packets_stats[packet_type].size +=
size;
268 if (packet_type == PACKET_BEGIN_TURN
269 && last_start_turn_seen !=
game.
info.turn) {
270 start_turn_seen = TRUE;
271 last_start_turn_seen =
game.
info.turn;
274 if ((packet_type == PACKET_PROCESSING_FINISHED
275 || packet_type == PACKET_THAW_CLIENT)
276 && start_turn_seen) {
277 start_turn_seen = FALSE;
285 #if PACKET_SIZE_STATISTICS == 2
286 delta_stats_report();
288 printf(
"Transmitted packets:\n");
289 printf(
"%8s %8s %8s %s",
"Packets",
"Bytes",
"Byt/Pac",
"Name\n");
291 for (i = 0; i < PACKET_LAST; i++) {
292 if (packets_stats[i].counter == 0) {
295 sum += packets_stats[i].size;
296 printf(
"%8d %8d %8d %s(%i)\n", packets_stats[i].counter,
297 packets_stats[i].
size,
298 packets_stats[i].
size / packets_stats[i].counter,
301 printf(
"turn=%d; transmitted %d bytes in %d packets;average size "
302 "per packet %d bytes\n",
303 game.
info.turn, sum, packet_counter, sum / packet_counter);
304 printf(
"turn=%d; transmitted %d bytes\n",
game.
info.turn,
310 for (i = 0; i < PACKET_LAST; i++) {
311 packets_stats[i].counter = 0;
312 packets_stats[i].size = 0;
330 enum packet_type *ptype)
333 int whole_packet_len;
335 enum packet_type type;
339 bool compressed_packet =
false;
342 void *(*receive_handler)(
struct connection *);
358 whole_packet_len = len_read;
364 compressed_packet =
true;
372 whole_packet_len = 6;
375 compressed_packet =
true;
378 log_compress(
"COMPRESS: got a normal packet of size %d",
382 if (
static_cast<unsigned>(whole_packet_len) > pc->
buffer->
ndata) {
386 if (whole_packet_len < header_size) {
387 qDebug(
"The packet size is reported to be less than header alone. "
388 "The connection will be closed now.");
394 if (compressed_packet) {
395 uLong compressed_size = whole_packet_len - header_size;
396 int decompress_factor = 80;
397 unsigned long int decompressed_size =
398 decompress_factor * compressed_size;
399 int error = Z_DATA_ERROR;
401 void *decompressed =
fc_malloc(decompressed_size);
405 uncompress(
static_cast<Bytef *
>(decompressed), &decompressed_size,
406 static_cast<const Bytef *
>(
410 if (error == Z_DATA_ERROR) {
411 decompress_factor += 50;
412 decompressed_size = decompress_factor * compressed_size;
413 decompressed =
fc_realloc(decompressed, decompressed_size);
418 qDebug(
"Uncompressing of the packet stream failed. "
419 "The connection will be closed now.");
425 }
while (error != Z_OK);
427 buffer->
ndata -= whole_packet_len;
432 memmove(buffer->
data, buffer->
data + whole_packet_len, buffer->
ndata);
434 if (buffer->
ndata + decompressed_size > buffer->
nsize) {
435 buffer->
nsize += decompressed_size;
436 buffer->
data =
static_cast<unsigned char *
>(
444 memmove(buffer->
data + decompressed_size, buffer->
data, buffer->
ndata);
449 memcpy(buffer->
data, decompressed, decompressed_size);
453 buffer->
ndata += decompressed_size;
455 log_compress(
"COMPRESS: decompressed %ld into %ld", compressed_size,
468 qDebug(
"The packet stream is corrupt. The connection "
469 "will be closed now.");
475 utype.type = packet_type(utype.itype);
477 if (utype.type >= PACKET_LAST
480 qDebug(
"Received unsupported packet type %d (%s). The connection "
481 "will be closed now.",
487 log_packet(
"got packet type=(%s)%d len=%d from %s",
488 packet_name(utype.type), utype.itype, whole_packet_len,
497 #if PACKET_SIZE_STATISTICS
502 } packets_stats[PACKET_LAST];
503 static int packet_counter = 0;
505 int packet_type = utype.itype;
506 int size = whole_packet_len;
508 if (!packet_counter) {
511 for (i = 0; i < PACKET_LAST; i++) {
512 packets_stats[i].counter = 0;
513 packets_stats[i].size = 0;
517 packets_stats[packet_type].counter++;
518 packets_stats[packet_type].size +=
size;
521 if (packet_counter % 100 == 0) {
524 printf(
"Received packets:\n");
525 for (i = 0; i < PACKET_LAST; i++) {
526 if (packets_stats[i].counter == 0)
528 sum += packets_stats[i].size;
529 printf(
" [%-25.25s %3d]: %6d packets; %8d bytes total; "
530 "%5d bytes/packet average\n",
532 packets_stats[i].counter, packets_stats[i].
size,
533 packets_stats[i].
size / packets_stats[i].counter);
535 printf(
"received %d bytes in %d packets;average size "
536 "per packet %d bytes\n",
537 sum, packet_counter, sum / packet_counter);
541 data = receive_handler(pc);
562 log_debug(
"remove_packet_from_buffer: remove %d; remaining %lu",
len,
596 struct connection *pconn,
const struct packet_server_join_reply *packet)
598 if (packet->you_can_join) {
607 struct connection *pconn,
const struct packet_server_join_reply *packet)
609 if (packet->you_can_join) {
628 log_packet(
"received long packet (type %d, len %d, rem %lu) from %s",
629 type,
len,
static_cast<unsigned long>(rem),
641 const struct packet_player_attribute_chunk *chunk)
643 log_packet(
"received attribute chunk %u/%u %u",
644 static_cast<unsigned int>(chunk->offset),
645 static_cast<unsigned int>(chunk->total_length),
646 static_cast<unsigned int>(chunk->chunk_length));
650 if (chunk->total_length < 0 || chunk->chunk_length < 0
653 > chunk->total_length
654 || chunk->chunk_length > chunk->total_length
655 || chunk->offset + chunk->chunk_length > chunk->total_length
656 || (chunk->offset != 0
657 && chunk->total_length
661 qCritical(
"Received wrong attribute chunk");
665 if (chunk->offset == 0) {
669 chunk->chunk_length);
671 if (chunk->offset + chunk->chunk_length == chunk->total_length) {
684 struct packet_player_attribute_chunk packet;
698 for (
int current_chunk = 0; current_chunk < chunks; current_chunk++) {
703 packet.chunk_length = size_of_current_chunk;
706 packet.chunk_length);
707 bytes_left -= packet.chunk_length;
712 memset(packet.data + packet.chunk_length, 0,
716 send_packet_player_attribute_chunk(pconn, &packet);
726 struct connection *pc,
struct packet_player_attribute_chunk *packet)
734 fc_assert(packet->chunk_length <= packet->total_length);
735 fc_assert(packet->offset >= 0 && packet->offset < packet->total_length);
737 log_packet(
"sending attribute chunk %d/%d %d", packet->offset,
738 packet->total_length, packet->chunk_length);
755 memset(&default_handlers, 0,
sizeof(default_handlers));
760 return &default_handlers;
772 fc_assert(strlen(capability) <
sizeof(functional_capability));
775 tokens = QString(capability).split(QRegularExpression(
"[ \t\n,]+"));
778 for (
const auto &str : qAsConst(tokens)) {
782 if (functional_capability[0] !=
'\0') {
785 sz_strlcat(functional_capability, qUtf8Printable(str));
789 if (!packet_handlers_hash->contains(functional_capability)) {
793 packet_handlers_hash->insert(functional_capability, phandlers);
795 phandlers = packet_handlers_hash->value(functional_capability);
809 if (qstrvec ==
nullptr) {
822 if (
'\0' != str[0]) {
844 while ((p = strchr(str, separator))) {
845 new_str =
new char[p - str + 1];
846 memcpy(new_str, str, p - str);
847 new_str[p - str] =
'\0';
848 psv->append(new_str);
bool has_capability(const char *cap, const char *capstr)
Wrapper for fc_has_capability() for nullptr terminated strings.
int get_next_request_id(int old_request_id)
Get next request id.
const char * conn_description(const struct connection *pconn, bool is_private)
° Return pointer to static string containing a description for this ° connection, based on pconn->nam...
void connection_do_buffer(struct connection *pc)
Turn on buffering, using a counter so that calls may be nested.
void connection_close(struct connection *pconn, const QString &reason)
Call the conn_close_callback.
bool connection_send_data(struct connection *pconn, const unsigned char *data, int len)
Write data to socket.
bool conn_compression_frozen(const struct connection *pconn)
Returns TRUE if the connection is frozen.
void connection_do_unbuffer(struct connection *pc)
Turn off buffering if internal counter of number of times buffering was turned on falls to zero,...
void dio_output_init(struct raw_data_out *dout, void *destination, size_t dest_size)
Initializes the output to the given output buffer and the given buffer size.
void dio_put_uint16_raw(struct raw_data_out *dout, int value)
Insert value using 16 bits.
size_t dio_input_remaining(struct data_in *din)
Return the number of unread bytes.
bool dio_get_uint32_raw(struct data_in *din, int *dest)
Receive uint32 value to dest.
void dio_input_rewind(struct data_in *din)
Rewinds the stream so that the get-functions start from the beginning.
void dio_put_uint32_raw(struct raw_data_out *dout, int value)
Insert value using 32 bits.
bool dio_get_uint16_raw(struct data_in *din, int *dest)
Receive uint16 value to dest.
bool dio_get_type_raw(struct data_in *din, enum data_type type, int *dest)
Receive value using 'size' bits to dest.
size_t data_type_size(enum data_type type)
Return the size of the data_type in bytes.
void dio_input_init(struct data_in *din, const void *src, size_t src_size)
Initializes the input to the given input buffer and the given number of valid input bytes.
bool is_server()
Is program type server?
#define fc_assert_ret(condition)
#define fc_assert(condition)
#define fc_assert_action_msg(condition, action, message,...)
#define fc_assert_ret_val(condition, val)
#define log_debug(message,...)
#define FC_STATIC_ASSERT(cond, tag)
void post_receive_packet_server_join_reply(struct connection *pconn, const struct packet_server_join_reply *packet)
Modify if needed the packet header field lengths.
#define COMPRESSION_BORDER
void packets_deinit()
Call when there is no longer a requirement for protocol processing.
static int get_compression_level()
Returns the compression level.
void packet_strvec_compute(char str[MAX_LEN_PACKET], QVector< QString > *qstrvec)
const struct packet_handlers * packet_handlers_get(const char *capability)
Returns the packet handlers variant for 'capability'.
#define MAX_DECOMPRESSION
bool packet_check(struct data_in *din, struct connection *pc)
Sanity check packet.
void packet_header_init(struct packet_header *packet_header)
Set the packet header field lengths used for the login protocol, before the capability of the connect...
void * get_packet_from_connection_raw(struct connection *pc, enum packet_type *ptype)
Read and return a packet from the connection 'pc'.
bool conn_compression_thaw(struct connection *pconn)
Thaw the connection.
const struct packet_handlers * packet_handlers_initial()
Returns the packet handlers variant with no special capability.
const char *const packet_functional_capability
void qstrvec_from_str(QVector< QString > *psv, char separator, const char *str)
Build the string vector from a string until 'str_size' bytes are read.
void pre_send_packet_player_attribute_chunk(struct connection *pc, struct packet_player_attribute_chunk *packet)
Test and log for sending player attribute_block.
static void packet_header_set(struct packet_header *packet_header)
Set the packet header field lengths used after the login protocol, after the capability of the connec...
static int stat_size_compressed
void remove_packet_from_buffer(struct socket_packet_buffer *buffer)
Remove the packet from the buffer.
void post_send_packet_server_join_reply(struct connection *pconn, const struct packet_server_join_reply *packet)
Modify if needed the packet header field lengths.
static void packet_handlers_free()
Destroy the packet handler hash table.
static int stat_size_uncompressed
int send_packet_data(struct connection *pc, unsigned char *data, int len, enum packet_type packet_type)
It returns the request id of the outgoing packet (or 0 if is_server()).
#define MAX_LEN_COMPRESS_QUEUE
QHash< QString, struct packet_handlers * > packetsHash
QVector< QString > * packet_strvec_extract(const char *str)
static int stat_size_alone
void generic_handle_player_attribute_chunk(struct player *pplayer, const struct packet_player_attribute_chunk *chunk)
Updates pplayer->attribute_block according to the given packet.
static int stat_size_no_compression
void send_attribute_block(const struct player *pplayer, struct connection *pconn)
Split the attribute block into chunks and send them over pconn.
static bool conn_compression_flush(struct connection *pconn)
Send all waiting data.
#define PACKET_STRVEC_SEPARATOR
#define ATTRIBUTE_CHUNK_SIZE
#define get_packet_from_connection(pc, ptype)
void packet_handlers_fill_capability(struct packet_handlers *phandlers, const char *capability)
const char * packet_name(enum packet_type type)
void packet_handlers_fill_initial(struct packet_handlers *phandlers)
#define MAX_ATTRIBUTE_BLOCK
Q_GLOBAL_STATIC(QVector< QString >, future_name_translation)
struct setting_list * level[OLEVELS_NUM]
bool str_to_int(const char *str, int *pint)
Convert 'str' to it's int reprentation if possible.
#define ADD_TO_POINTER(p, n)
struct packet_game_info info
const struct packet_handlers * handlers
struct connection::@58 compression
struct connection::@59 statistics
struct connection::@57 phs
struct packet_header packet_header
char username[MAX_LEN_NAME]
void(* incoming_packet_notify)(struct connection *pc, int packet_type, int size)
void(* outgoing_packet_notify)(struct connection *pc, int packet_type, int size, int request_id)
struct connection::@55::@60 client
struct socket_packet_buffer * buffer
void *(* receive[PACKET_LAST])(struct connection *pconn)
QByteArray attribute_block
QByteArray attribute_block_buffer
#define fc_realloc(ptr, sz)
#define sz_strlcat(dest, src)