/**************************************************************************** Send all waiting data. Return TRUE on success. ****************************************************************************/ static bool conn_compression_flush(struct connection *pconn) { int compression_level = get_compression_level(); uLongf compressed_size = 12 + 1.001 * pconn->compression.queue.size; int error; Bytef compressed[compressed_size]; error = compress2(compressed, &compressed_size, pconn->compression.queue.p, pconn->compression.queue.size, compression_level); fc_assert_ret_val(error == Z_OK, FALSE); if (compressed_size + 2 < pconn->compression.queue.size) { struct data_out dout; log_compress("COMPRESS: compressed %lu bytes to %ld (level %d)", (unsigned long) pconn->compression.queue.size, compressed_size, compression_level); stat_size_uncompressed += pconn->compression.queue.size; stat_size_compressed += compressed_size; if (compressed_size <= JUMBO_BORDER) { unsigned char header[2]; log_compress("COMPRESS: sending %ld as normal", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, 2 + compressed_size + COMPRESSION_BORDER); connection_send_data(pconn, header, sizeof(header)); connection_send_data(pconn, compressed, compressed_size); } else { unsigned char header[6]; log_compress("COMPRESS: sending %ld as jumbo", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, JUMBO_SIZE); dio_put_uint32(&dout, 6 + compressed_size); connection_send_data(pconn, header, sizeof(header)); connection_send_data(pconn, compressed, compressed_size); } } else { log_compress("COMPRESS: would enlarging %lu bytes to %ld; " "sending uncompressed", (unsigned long) pconn->compression.queue.size, compressed_size); connection_send_data(pconn, pconn->compression.queue.p, pconn->compression.queue.size); stat_size_no_compression += pconn->compression.queue.size; } return pconn->used; }
/**************************************************************************** This method isn't endian safe and there will also be problems if sizeof(int) at serialization time is different from sizeof(int) at deserialization time. ****************************************************************************/ static enum attribute_serial serialize_hash(const struct attribute_hash *hash, void **pdata, int *pdata_length) { /* * Layout of version 2: * * struct { * uint32 0; always != 0 in version 1 * uint8 2; * uint32 entries; * uint32 total_size_in_bytes; * } preamble; * * struct { * uint32 value_size; * char key[], char value[]; * } body[entries]; */ const size_t entries = attribute_hash_size(hash); int total_length, value_lengths[entries]; void *result; struct data_out dout; int i; /* * Step 1: loop through all keys and fill value_lengths and calculate * the total_length. */ /* preamble */ total_length = 4 * 4; /* body */ total_length += entries * (4 + 4 * 4); /* value_size + key */ i = 0; attribute_hash_values_iterate(hash, pvalue) { struct data_in din; dio_input_init(&din, pvalue, 4); dio_get_uint32(&din, &value_lengths[i]); total_length += value_lengths[i]; i++; } attribute_hash_values_iterate_end; /* * Step 2: allocate memory. */ result = fc_malloc(total_length); dio_output_init(&dout, result, total_length); /* * Step 3: fill out the preamble. */ dio_put_uint32(&dout, 0); dio_put_uint8(&dout, 2); dio_put_uint32(&dout, attribute_hash_size(hash)); dio_put_uint32(&dout, total_length); /* * Step 4: fill out the body. */ i = 0; attribute_hash_iterate(hash, pkey, pvalue) { dio_put_uint32(&dout, value_lengths[i]); dio_put_uint32(&dout, pkey->key); dio_put_uint32(&dout, pkey->id); dio_put_sint16(&dout, pkey->x); dio_put_sint16(&dout, pkey->y); dio_put_memory(&dout, ADD_TO_POINTER(pvalue, 4), value_lengths[i]); i++; }
/************************************************************************** It returns the request id of the outgoing packet (or 0 if pc->is_server). **************************************************************************/ int send_packet_data(struct connection *pc, unsigned char *data, int len) { /* default for the server */ int result = 0; freelog(BASIC_PACKET_LOG_LEVEL, "sending packet type=%s(%d) len=%d", get_packet_name(data[2]), data[2], len); if (!pc->is_server) { pc->client.last_request_id_used = get_next_request_id(pc->client.last_request_id_used); result = pc->client.last_request_id_used; freelog(BASIC_PACKET_LOG_LEVEL, "sending request %d", result); } if (pc->outgoing_packet_notify) { pc->outgoing_packet_notify(pc, data[2], len, result); } #ifdef USE_COMPRESSION if(TRUE) { static int stat_size_alone, stat_size_uncompressed, stat_size_compressed, stat_size_no_compression; static bool compression_level_initialized = FALSE; static int compression_level; int packet_type = data[2]; int size = len; if (!compression_level_initialized) { char *s = getenv("FREECIV_COMPRESSION_LEVEL"); if (!s || sscanf(s, "%d", &compression_level) != 1 || compression_level < -1 || compression_level > 9) { compression_level = -1; } compression_level_initialized = TRUE; } /* TODO: PACKET_FREEZE_HINT and PACKET_THAW_HINT are meaningful * only internally. They should not be sent to connection at all. * Freezing could also be handled via separate functions, and * not by special packets. * Only problem is backward compatibility, so this cannot be * changed in stable branch. */ if (packet_type == PACKET_PROCESSING_STARTED || packet_type == PACKET_FREEZE_HINT) { if (pc->compression.frozen_level == 0) { byte_vector_reserve(&pc->compression.queue, 0); } pc->compression.frozen_level++; } if (pc->compression.frozen_level > 0) { size_t old_size = pc->compression.queue.size; byte_vector_reserve(&pc->compression.queue, old_size + len); memcpy(pc->compression.queue.p + old_size, data, len); freelog(COMPRESS2_LOG_LEVEL, "COMPRESS: putting %s into the queue", get_packet_name(packet_type)); } else { stat_size_alone += size; freelog(COMPRESS_LOG_LEVEL, "COMPRESS: sending %s alone (%d bytes total)", get_packet_name(packet_type), stat_size_alone); send_connection_data(pc, data, len); } if (packet_type == PACKET_PROCESSING_FINISHED || packet_type == PACKET_THAW_HINT) { pc->compression.frozen_level--; if (pc->compression.frozen_level == 0) { uLongf compressed_size = 12 + pc->compression.queue.size * 1.001; int error; Bytef compressed[compressed_size]; error = compress2(compressed, &compressed_size, pc->compression.queue.p, pc->compression.queue.size, compression_level); assert(error == Z_OK); if (compressed_size + 2 < pc->compression.queue.size) { struct data_out dout; freelog(COMPRESS_LOG_LEVEL, "COMPRESS: compressed %lu bytes to %ld (level %d)", (unsigned long)pc->compression.queue.size, compressed_size, compression_level); stat_size_uncompressed += pc->compression.queue.size; stat_size_compressed += compressed_size; if (compressed_size <= JUMBO_BORDER) { unsigned char header[2]; freelog(COMPRESS_LOG_LEVEL, "COMPRESS: sending %ld as normal", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, 2 + compressed_size + COMPRESSION_BORDER); send_connection_data(pc, header, sizeof(header)); send_connection_data(pc, compressed, compressed_size); } else { unsigned char header[6]; freelog(COMPRESS_LOG_LEVEL, "COMPRESS: sending %ld as jumbo", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, JUMBO_SIZE); dio_put_uint32(&dout, 6 + compressed_size); send_connection_data(pc, header, sizeof(header)); send_connection_data(pc, compressed, compressed_size); } } else { freelog(COMPRESS_LOG_LEVEL, "COMPRESS: would enlarging %lu bytes to %ld; sending uncompressed", (unsigned long)pc->compression.queue.size, compressed_size); send_connection_data(pc, pc->compression.queue.p, pc->compression.queue.size); stat_size_no_compression += pc->compression.queue.size; } } } freelog(COMPRESS2_LOG_LEVEL, "COMPRESS: STATS: alone=%d compression-expand=%d compression (before/after) = %d/%d", stat_size_alone, stat_size_no_compression, stat_size_uncompressed, stat_size_compressed); } #else send_connection_data(pc, data, len); #endif #if PACKET_SIZE_STATISTICS { static struct { int counter; int size; } packets_stats[PACKET_LAST]; static int packet_counter = 0; static int last_start_turn_seen = -1; static bool start_turn_seen = FALSE; int packet_type = data[2]; int size = len; bool print = FALSE; bool clear = FALSE; if (!packet_counter) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } } packets_stats[packet_type].counter++; packets_stats[packet_type].size += size; packet_counter++; if (packet_type == PACKET_START_TURN && last_start_turn_seen != game.turn) { start_turn_seen=TRUE; last_start_turn_seen = game.turn; } if ((packet_type == PACKET_PROCESSING_FINISHED || packet_type == PACKET_THAW_HINT) && start_turn_seen) { start_turn_seen = FALSE; print = TRUE; clear = TRUE; } if(print) { int i, sum = 0; int ll = LOG_DEBUG; #if PACKET_SIZE_STATISTICS == 2 delta_stats_report(); #endif freelog(ll, "Transmitted packets:"); freelog(ll, "%8s %8s %8s %s", "Packets", "Bytes", "Byt/Pac", "Name"); for (i = 0; i < PACKET_LAST; i++) { if (packets_stats[i].counter == 0) { continue; } sum += packets_stats[i].size; freelog(ll, "%8d %8d %8d %s(%i)", packets_stats[i].counter, packets_stats[i].size, packets_stats[i].size / packets_stats[i].counter, get_packet_name(i),i); } freelog(LOG_TEST, "turn=%d; transmitted %d bytes in %d packets;average size " "per packet %d bytes", game.turn, sum, packet_counter, sum / packet_counter); freelog(LOG_TEST, "turn=%d; transmitted %d bytes", game.turn, pc->statistics.bytes_send); } if (clear) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } packet_counter = 0; pc->statistics.bytes_send = 0; delta_stats_reset(); } } #endif return result; }
/**************************************************************************** Send all waiting data. Return TRUE on success. ****************************************************************************/ static bool conn_compression_flush(struct connection *pconn) { int compression_level = get_compression_level(); uLongf compressed_size = 12 + 1.001 * pconn->compression.queue.size; int error; Bytef compressed[compressed_size]; bool jumbo; unsigned long compressed_packet_len; error = compress2(compressed, &compressed_size, pconn->compression.queue.p, pconn->compression.queue.size, compression_level); fc_assert_ret_val(error == Z_OK, FALSE); /* Compression signalling currently assumes a 2-byte packet length; if that * changes, the protocol should probably be changed */ fc_assert_ret_val(data_type_size(pconn->packet_header.length) == 2, FALSE); /* Include normal length field in decision */ jumbo = (compressed_size+2 >= JUMBO_BORDER); compressed_packet_len = compressed_size + (jumbo ? 6 : 2); if (compressed_packet_len < pconn->compression.queue.size) { struct data_out dout; log_compress("COMPRESS: compressed %lu bytes to %ld (level %d)", (unsigned long) pconn->compression.queue.size, compressed_size, compression_level); stat_size_uncompressed += pconn->compression.queue.size; stat_size_compressed += compressed_size; if (!jumbo) { unsigned char header[2]; FC_STATIC_ASSERT(COMPRESSION_BORDER > MAX_LEN_PACKET, uncompressed_compressed_packet_len_overlap); log_compress("COMPRESS: sending %ld as normal", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, 2 + compressed_size + COMPRESSION_BORDER); connection_send_data(pconn, header, sizeof(header)); connection_send_data(pconn, compressed, compressed_size); } else { unsigned char header[6]; FC_STATIC_ASSERT(JUMBO_SIZE >= JUMBO_BORDER+COMPRESSION_BORDER, compressed_normal_jumbo_packet_len_overlap); log_compress("COMPRESS: sending %ld as jumbo", compressed_size); dio_output_init(&dout, header, sizeof(header)); dio_put_uint16(&dout, JUMBO_SIZE); dio_put_uint32(&dout, 6 + compressed_size); connection_send_data(pconn, header, sizeof(header)); connection_send_data(pconn, compressed, compressed_size); } } else { log_compress("COMPRESS: would enlarge %lu bytes to %ld; " "sending uncompressed", (unsigned long) pconn->compression.queue.size, compressed_packet_len); connection_send_data(pconn, pconn->compression.queue.p, pconn->compression.queue.size); stat_size_no_compression += pconn->compression.queue.size; } return pconn->used; }