diff -X freeciv/diff_ignore -Nurd freeciv/client/Makefile.am freeciv.attr/client/Makefile.am --- freeciv/client/Makefile.am Sat Oct 28 00:19:58 2000 +++ freeciv.attr/client/Makefile.am Wed Feb 14 14:35:52 2001 @@ -39,6 +39,8 @@ ## Above, note -I../intl instead of -I$(top_srdir/intl) is deliberate. civclient_SOURCES = \ + attribute.h \ + attribute.c \ cityrepdata.c \ cityrepdata.h \ civclient.c \ diff -X freeciv/diff_ignore -Nurd freeciv/client/attribute.c freeciv.attr/client/attribute.c --- freeciv/client/attribute.c Thu Jan 1 01:00:00 1970 +++ freeciv.attr/client/attribute.c Wed Feb 14 14:35:52 2001 @@ -0,0 +1,336 @@ + +#include +#include + +#include "hash.h" +#include "mem.h" +#include "game.h" +#include "packets.h" +#include "clinet.h" +#include "log.h" + +#include "attribute.h" + +#define ATTRIBUTE_LOG_LEVEL LOG_DEBUG + +static struct hash_table *attribute_hash = NULL; + +struct attr_key { + int key, id, x, y; +}; + +static unsigned int attr_hash_val_fn(const void *key, + unsigned int num_buckets) +{ + const struct attr_key *pkey = (const struct attr_key *) key; + + return (pkey->id ^ pkey->x ^ pkey->y ^ pkey->key) % num_buckets; +} + +static int attr_hash_cmp_fn(const void *key1, const void *key2) +{ + return memcmp(key1, key2, sizeof(struct attr_key)); +} + +void attribute_init() +{ + assert(attribute_hash == NULL); + attribute_hash = hash_new(attr_hash_val_fn, attr_hash_cmp_fn); +} + +/* + * This method isn't endian safe and there will also be errors if + * sizeof(int) changes. + */ +static void serialize_hash(struct hash_table *hash, void **pdata, + int *pdata_length) +{ + /* + * Layout: + * + * struct { + * int entries; + * int total_size_in_bytes; + * } preamble; + * + * struct { + * int key_size, key_offset, value_size, value_offset; + * } header[entries]; + * (offsets are relative to the body start) + * + * struct { + * char key[], char value[]; + * } body[entries]; + */ + int preamble_length, header_length, body_length, total_length, i, + current_body_offset, entries = hash_num_entries(hash), key_size = + sizeof(struct attr_key); + void *result, *body; + int *value_lengths, *header, *preamble; + + value_lengths = fc_malloc(sizeof(int) * entries); + + /* + * Step 1: loop through all keys and fill value_lengths + */ + for (i = 0; i < entries; i++) { + const void *pvalue = hash_value_by_number(hash, i); + value_lengths[i] = ((int *) pvalue)[0] + sizeof(int); + } + + /* + * Step 2: calculate the *_length variables + */ + preamble_length = 2 * sizeof(int); + header_length = entries * sizeof(int) * 4; + body_length = entries * key_size; + + for (i = 0; i < entries; i++) { + body_length += value_lengths[i]; + } + + /* + * Step 3: allocate memory + */ + total_length = preamble_length + header_length + body_length; + result = fc_malloc(total_length); + + /* + * Step 4: fill out the preamble + */ + preamble = result; + preamble[0] = entries; + preamble[1] = total_length; + + /* + * Step 5: fill out the header + */ + header = result + preamble_length; + current_body_offset = 0; + + for (i = 0; i < entries; i++) { + header[0] = key_size; + header[1] = current_body_offset; + current_body_offset += key_size; + header[2] = value_lengths[i]; + header[3] = current_body_offset; + current_body_offset += value_lengths[i]; + freelog(LOG_DEBUG, "serial: [%d] key{size=%d, offset=%d} " + "value{size=%d, offset=%d}", i, header[0], header[1], + header[2], header[3]); + header += 4; + } + + /* + * Step 5: fill out the body. + */ + body = result + preamble_length + header_length; + for (i = 0; i < entries; i++) { + const void *pkey = hash_key_by_number(hash, i); + const void *pvalue = hash_value_by_number(hash, i); + + memcpy(body, pkey, key_size); + body += key_size; + memcpy(body, pvalue, value_lengths[i]); + body += value_lengths[i]; + } + + /* + * Step 6: cleanup + */ + *pdata = result; + *pdata_length = total_length; + free(value_lengths); + freelog(LOG_DEBUG, "serialized %d entries in %d bytes", entries, + total_length); +} + +static void unserialize_hash(struct hash_table *hash, void *data, + int data_length) +{ + int *preamble, *header; + int entries, i, preamble_length, header_length; + void *body; + + hash_delete_all_entries(hash); + + preamble = (int *) data; + entries = preamble[0]; + assert(preamble[1] == data_length); + + freelog(LOG_DEBUG, "try to unserialized %d entries from %d bytes", + entries, data_length); + preamble_length = 2 * sizeof(int); + header_length = entries * sizeof(int) * 4; + + header = data + preamble_length; + body = data + preamble_length + header_length; + + for (i = 0; i < entries; i++) { + void *pkey = fc_malloc(header[0]); + void *pvalue = fc_malloc(header[2]); + + freelog(LOG_DEBUG, "unserial: [%d] key{size=%d, offset=%d} " + "value{size=%d, offset=%d}", i, header[0], header[1], + header[2], header[3]); + + memcpy(pkey, body + header[1], header[0]); + memcpy(pvalue, body + header[3], header[2]); + + hash_insert(hash, pkey, pvalue); + + header += 4; + } +} + +void attribute_flush(void) +{ + struct player *pplayer = game.player_ptr; + + assert(attribute_hash); + + if (hash_num_entries(attribute_hash) == 0) + return; + + if (pplayer->attribute_block.data != NULL) + free(pplayer->attribute_block.data); + + serialize_hash(attribute_hash, &(pplayer->attribute_block.data), + &(pplayer->attribute_block.length)); + send_attribute_block(pplayer, &aconnection); +} + +void attribute_restore(void) +{ + struct player *pplayer = game.player_ptr; + assert(attribute_hash); + unserialize_hash(attribute_hash, pplayer->attribute_block.data, + pplayer->attribute_block.length); +} + +void attribute_set(int key, int id, int x, int y, int data_length, + const void *const data) +{ + struct attr_key *pkey; + void *pvalue = NULL; + + freelog(ATTRIBUTE_LOG_LEVEL, "attribute_set(key=%d, id=%d, x=%d, y=%d, " + "data_length=%d, data=%p)", key, id, x, y, data_length, data); + + assert(attribute_hash); + assert(data_length >= 0); + + pkey = fc_malloc(sizeof(struct attr_key)); + pkey->key = key; + pkey->id = id; + pkey->x = x; + pkey->y = y; + + if (data_length != 0) { + pvalue = fc_malloc(data_length + sizeof(int)); + ((int *) pvalue)[0] = data_length; + memcpy(pvalue + sizeof(int), data, data_length); + } + + if (hash_key_exists(attribute_hash, pkey)) { + void *old_value = hash_delete_entry(attribute_hash, pkey); + free(old_value); + } + + if (data_length != 0) { + hash_insert(attribute_hash, pkey, pvalue); + } +} + +int attribute_get(int key, int id, int x, int y, int max_data_length, + void *data) +{ + + struct attr_key pkey; + void *pvalue; + int length; + + freelog(ATTRIBUTE_LOG_LEVEL, "attribute_get(key=%d, id=%d, x=%d, y=%d, " + "max_data_length=%d, data=%p)", key, id, x, y, max_data_length, + data); + + assert(attribute_hash); + + pkey.key = key; + pkey.id = id; + pkey.x = x; + pkey.y = y; + + pvalue = hash_lookup_data(attribute_hash, &key); + + if (pvalue == NULL) { + freelog(ATTRIBUTE_LOG_LEVEL, " not found"); + return 0; + } + + length = ((int *) pvalue)[0]; + assert(max_data_length >= length); + memcpy(data, pvalue + sizeof(int), length); + + freelog(ATTRIBUTE_LOG_LEVEL, " found length=%d", length); + return length; +} + +void attr_unit_set(enum attr_unit what, int unit_id, int data_length, + const void *const data) +{ + attribute_set(what, unit_id, -1, -2, data_length, data); +} + +int attr_unit_get(enum attr_unit what, int unit_id, int max_data_length, + void *data) +{ + return attribute_get(what, unit_id, -1, -2, max_data_length, data); +} + +void attr_city_set(enum attr_city what, int city_id, int data_length, + const void *const data) +{ + attribute_set(what, city_id, -1, -1, data_length, data); +} + +int attr_city_get(enum attr_city what, int city_id, int max_data_length, + void *data) +{ + return attribute_get(what, city_id, -1, -1, max_data_length, data); +} + +void attr_city_set_int(enum attr_city what, int city_id, int data) +{ + attr_city_set(what, city_id, sizeof(int), &data); +} + +int attr_city_get_int(enum attr_city what, int city_id, int *data) +{ + return attr_city_get(what, city_id, sizeof(int), data); +} + +void attr_player_set(enum attr_player what, int player_id, int data_length, + const void *const data) +{ + attribute_set(what, player_id, -1, -1, data_length, data); +} + +int attr_player_get(enum attr_player what, int player_id, + int max_data_length, void *data) +{ + return attribute_get(what, player_id, -1, -1, max_data_length, data); +} + + +void attr_tile_set(enum attr_tile what, int x, int y, int data_length, + const void *const data) +{ + attribute_set(what, -1, x, y, data_length, data); +} + +int attr_tile_get(enum attr_tile what, int x, int y, int max_data_length, + void *data) +{ + return attribute_get(what, -1, x, y, max_data_length, data); +} diff -X freeciv/diff_ignore -Nurd freeciv/client/attribute.h freeciv.attr/client/attribute.h --- freeciv/client/attribute.h Thu Jan 1 01:00:00 1970 +++ freeciv.attr/client/attribute.h Wed Feb 14 14:35:52 2001 @@ -0,0 +1,108 @@ +/********************************************************************** + Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +***********************************************************************/ +#ifndef FC__CLIENT_ATTRIBUTE_H +#define FC__CLIENT_ATTRIBUTE_H + +/* + * If 4 byte wide signed int is used this gives 20 object types with + * 100 million keys each. + */ +enum attr_object_type_start_keys { + ATTR_UNIT_START = 0 * 100 * 1000 * 1000, + ATTR_CITY_START = 1 * 100 * 1000 * 1000, + ATTR_PLAYER_START = 2 * 100 * 1000 * 1000, + ATTR_TILE_START = 3 * 100 * 1000 * 1000 +}; + +enum attr_unit { + ATTR_UNIT_DUMMY = ATTR_UNIT_START, +/* + ATTR_UNIT_AUTO_EXPLORER = ATTR_UNIT_START, + ATTR_UNIT_AUTO_SETTLER +*/ +}; + +enum attr_city { + ATTR_CITY_CMA_DUMMY = ATTR_CITY_START +}; + +enum attr_player { + ATTR_PLAYER_DUMMY = ATTR_PLAYER_START +}; + +enum attr_tile { + ATTR_TILE_DUMMY = ATTR_TILE_START +}; + +/* + * Generic methods. + */ +void attribute_init(void); + +/* + * Send current state to server. + */ +void attribute_flush(void); + +/* + * Recreate the attribute set from the player's attribute_block. + */ +void attribute_restore(void); + +/* + * If data_length is zero the attribute is removed. + */ +void attribute_set(int key, int id, int x, int y, int data_length, + const void *const data); +/* + * If data hasn't enough space to hold the attribute attribute_get + * aborts. Returns the actual size of the attribute. Can be zero if + * the attribute is unset. + */ +int attribute_get(int key, int id, int x, int y, int max_data_length, + void *data); + +/* + * Special methods for units. + */ +void attr_unit_set(enum attr_unit what, int unit_id, int data_length, + const void *const data); +int attr_unit_get(enum attr_unit what, int unit_id, int max_data_length, + void *data); + +/* + * Special methods for cities. + */ +void attr_city_set(enum attr_city what, int city_id, int data_length, + const void *const data); +int attr_city_get(enum attr_city what, int city_id, int max_data_length, + void *data); +void attr_city_set_int(enum attr_city what, int city_id, int data); +int attr_city_get_int(enum attr_city what, int city_id, int *data); + +/* + * Special methods for players. + */ +void attr_player_set(enum attr_player what, int player_id, int data_length, + const void *const data); +int attr_player_get(enum attr_player what, int player_id, + int max_data_length, void *data); + +/* + * Special methods for tiles. + */ +void attr_tile_set(enum attr_tile what, int x, int y, int data_length, + const void *const data); +int attr_tile_get(enum attr_tile what, int x, int y, int max_data_length, + void *data); +#endif diff -X freeciv/diff_ignore -Nurd freeciv/client/civclient.c freeciv.attr/client/civclient.c --- freeciv/client/civclient.c Sat Jan 13 02:22:05 2001 +++ freeciv.attr/client/civclient.c Wed Feb 14 14:35:52 2001 @@ -57,6 +57,7 @@ #include "plrdlg_g.h" #include "repodlgs_g.h" #include "tilespec.h" +#include "attribute.h" #include "civclient.h" @@ -162,6 +163,7 @@ init_messages_where(); init_our_capability(); game_init(); + attribute_init(); /* This seed is not saved anywhere; randoms in the client should have cosmetic effects only (eg city name suggestions). --dwp */ @@ -360,7 +362,12 @@ case PACKET_CONN_INFO: handle_conn_info((struct packet_conn_info *)packet); break; - + + case PACKET_ATTRIBUTE_CHUNK: + handle_player_attribute_chunk((struct packet_attribute_chunk *) + packet); + break; + default: freelog(LOG_ERROR, "Received unknown packet (type %d) from server!", type); /* Old clients (<= some 1.11.5-devel, capstr +1.11) used to exit() @@ -372,19 +379,27 @@ free(packet); } - /************************************************************************** ... **************************************************************************/ - void user_ended_turn(void) { + send_turn_done(); +} + +/************************************************************************** +... +**************************************************************************/ +void send_turn_done(void) +{ struct packet_generic_message gen_packet; - gen_packet.message[0]='\0'; + + attribute_flush(); + + gen_packet.message[0] = '\0'; send_packet_generic_message(&aconnection, PACKET_TURN_DONE, &gen_packet); } - /************************************************************************** ... **************************************************************************/ @@ -526,3 +541,15 @@ void dealloc_id(int id); /* double kludge (suppress a possible warning) */ void dealloc_id(int id) { }/* kludge */ + +/************************************************************************** +.. +**************************************************************************/ +void send_attribute_block_request() +{ + struct packet_player_request packet; + + packet.attribute_block = 1; + send_packet_player_request(&aconnection, &packet, + PACKET_PLAYER_ATTRIBUTE_BLOCK); +} diff -X freeciv/diff_ignore -Nurd freeciv/client/civclient.h freeciv.attr/client/civclient.h --- freeciv/client/civclient.h Mon Jan 15 01:30:17 2001 +++ freeciv.attr/client/civclient.h Wed Feb 14 14:35:52 2001 @@ -22,6 +22,8 @@ void send_move_unit(struct unit *punit); void send_goto_unit(struct unit *punit, int dest_x, int dest_y); void send_report_request(enum report_type type); +void send_attribute_block_request(void); +void send_turn_done(void); void user_ended_turn(void); diff -X freeciv/diff_ignore -Nurd freeciv/client/control.c freeciv.attr/client/control.c --- freeciv/client/control.c Wed Feb 7 22:55:32 2001 +++ freeciv.attr/client/control.c Wed Feb 14 14:35:52 2001 @@ -1455,14 +1455,11 @@ **************************************************************************/ void key_end_turn(void) { - if(!game.player_ptr->turn_done) { - struct packet_generic_message gen_packet; - gen_packet.message[0]='\0'; - send_packet_generic_message(&aconnection, PACKET_TURN_DONE, &gen_packet); + if (!game.player_ptr->turn_done) { + send_turn_done(); set_turn_done_button_state(FALSE); } } - /************************************************************************** ... **************************************************************************/ diff -X freeciv/diff_ignore -Nurd freeciv/client/packhand.c freeciv.attr/client/packhand.c --- freeciv/client/packhand.c Thu Jan 25 20:39:31 2001 +++ freeciv.attr/client/packhand.c Wed Feb 14 14:35:52 2001 @@ -59,6 +59,7 @@ #include "spaceshipdlg_g.h" #include "tilespec.h" #include "wldlg_g.h" +#include "attribute.h" #include "packhand.h" @@ -217,6 +218,7 @@ if(get_client_state()==CLIENT_GAME_RUNNING_STATE) { struct city *pcity; struct unit *punit; + refresh_overview_canvas(); refresh_overview_viewrect(); enable_turn_done_button(); @@ -696,7 +698,6 @@ struct unit *punit; int repaint_unit; int repaint_city; /* regards unit's homecity */ - /* Special case for a diplomat/spy investigating a city: The investigator needs to know the supported and present units of a city, whether or not they are fogged. So, we @@ -2160,5 +2161,19 @@ pcity->improvements[i] = (packet->improvements[i]=='1') ? 1 : 0; popup_sabotage_dialog(pcity); + } +} + +/************************************************************************** +... +**************************************************************************/ +void handle_player_attribute_chunk(struct packet_attribute_chunk *chunk) +{ + generic_handle_attribute_chunk(game.player_ptr, chunk); + + if (chunk->offset + chunk->chunk_length == chunk->total_length) { + /* We successful received the last chunk. The attribute block is + now complete. */ + attribute_restore(); } } diff -X freeciv/diff_ignore -Nurd freeciv/client/packhand.h freeciv.attr/client/packhand.h --- freeciv/client/packhand.h Sun Aug 27 08:57:22 2000 +++ freeciv.attr/client/packhand.h Wed Feb 14 14:35:52 2001 @@ -58,5 +58,6 @@ void handle_ruleset_game(struct packet_ruleset_game *packet); void handle_diplomat_action(struct packet_diplomat_action *packet); void handle_sabotage_list(struct packet_sabotage_list *packet); +void handle_player_attribute_chunk(struct packet_attribute_chunk *packet); #endif /* FC__PACKHAND_H */ diff -X freeciv/diff_ignore -Nurd freeciv/common/capstr.c freeciv.attr/common/capstr.c --- freeciv/common/capstr.c Mon Jan 22 05:57:14 2001 +++ freeciv.attr/common/capstr.c Wed Feb 14 14:35:52 2001 @@ -74,7 +74,7 @@ " game_ruleset nuclear_fallout land_channel_requirement event_wonder_obsolete" \ " event00_fix conn_info gen_impr_oversights diplo_move_city packet_short_city" \ " indef_impr_types worklist_true_ids shared_vision activity_patrol" \ -" gen_granary_size" +" gen_granary_size attributes" /* "+1.11" is protocol for 1.11.0 stable release. @@ -133,6 +133,8 @@ "gen_granary_size" adds the option to change the way the size of the food storage box is calculated. + + "attributes" is the ability to request and transfer attribute blocks. */ void init_our_capability(void) diff -X freeciv/diff_ignore -Nurd freeciv/common/game.c freeciv.attr/common/game.c --- freeciv/common/game.c Thu Jan 4 01:49:04 2001 +++ freeciv.attr/common/game.c Wed Feb 14 14:35:52 2001 @@ -926,6 +926,11 @@ { struct player *pplayer=&game.players[plrno]; + if (pplayer->attribute_block.data != NULL) { + free(pplayer->attribute_block.data); + pplayer->attribute_block.data = NULL; + } + unit_list_iterate(pplayer->units, punit) game_remove_unit(punit->id); unit_list_iterate_end; diff -X freeciv/diff_ignore -Nurd freeciv/common/hash.c freeciv.attr/common/hash.c --- freeciv/common/hash.c Sun Aug 6 13:47:09 2000 +++ freeciv.attr/common/hash.c Wed Feb 14 14:35:52 2001 @@ -543,6 +543,15 @@ } /************************************************************************** + Delete all entries of the hash. +**************************************************************************/ +void hash_delete_all_entries(struct hash_table *h) +{ + while (hash_num_entries(h) > 0) + hash_delete_entry(h, hash_key_by_number(h, 0)); +} + +/************************************************************************** Lookup: return existence: **************************************************************************/ int hash_key_exists(const struct hash_table *h, const void *key) @@ -576,4 +585,37 @@ unsigned int hash_num_deleted(const struct hash_table *h) { return h->num_deleted; +} + +/************************************************************************** + Enumeration: returns the pointer to a key. The keys are returned in + random order. +**************************************************************************/ +const void *hash_key_by_number(const struct hash_table *h, + unsigned int entry_number) +{ + int bucket_nr, counter = 0; + assert(entry_number < h->num_entries); + + for (bucket_nr = 0; bucket_nr < h->num_buckets; bucket_nr++) { + struct hash_bucket *bucket = &h->buckets[bucket_nr]; + + if (bucket->used != BUCKET_USED) + continue; + + if (entry_number == counter) + return bucket->key; + counter++; + } + /* never reached */ + assert(0); +} + +/************************************************************************** + Enumeration: returns the pointer to a value. +**************************************************************************/ +const void *hash_value_by_number(const struct hash_table *h, + unsigned int entry_number) +{ + return hash_lookup_data(h, hash_key_by_number(h, entry_number)); } diff -X freeciv/diff_ignore -Nurd freeciv/common/hash.h freeciv.attr/common/hash.h --- freeciv/common/hash.h Sun Aug 6 13:47:09 2000 +++ freeciv.attr/common/hash.h Wed Feb 14 14:35:52 2001 @@ -52,6 +52,12 @@ void *hash_lookup_data(const struct hash_table *h, const void *key); void *hash_delete_entry(struct hash_table *h, const void *key); +void hash_delete_all_entries(struct hash_table *h); + +const void *hash_key_by_number(const struct hash_table *h, + unsigned int entry_number); +const void *hash_value_by_number(const struct hash_table *h, + unsigned int entry_number); unsigned int hash_num_entries(const struct hash_table *h); unsigned int hash_num_buckets(const struct hash_table *h); diff -X freeciv/diff_ignore -Nurd freeciv/common/packets.c freeciv.attr/common/packets.c --- freeciv/common/packets.c Wed Jan 24 18:20:36 2001 +++ freeciv.attr/common/packets.c Wed Feb 14 14:35:53 2001 @@ -284,6 +284,7 @@ case PACKET_PLAYER_RESEARCH: case PACKET_PLAYER_TECH_GOAL: case PACKET_PLAYER_WORKLIST: + case PACKET_PLAYER_ATTRIBUTE_BLOCK: return receive_packet_player_request(pc); case PACKET_UNIT_BUILD_CITY: @@ -363,6 +364,9 @@ case PACKET_PATROL_ROUTE: return receive_packet_goto_route(pc); + case PACKET_ATTRIBUTE_CHUNK: + return receive_packet_attribute_chunk(pc); + default: freelog(LOG_ERROR, "unknown packet type %d received from %s", type, conn_description(pc)); @@ -1518,12 +1522,16 @@ cptr=put_worklist(cptr, &packet->worklist, pc, req_type == PACKET_PLAYER_WORKLIST); cptr=put_uint8(cptr, packet->wl_idx); + + if (pc && has_capability("attributes", pc->capability)) { + cptr = put_uint8(cptr, packet->attribute_block); + } + put_uint16(buffer, cptr-buffer); return send_connection_data(pc, buffer, cptr-buffer); } - /************************************************************************* ... **************************************************************************/ @@ -1545,6 +1553,10 @@ may want to remove the 'pc' argument (?) */ iget_worklist(&iter, &preq->worklist, pc); iget_uint8(&iter, &preq->wl_idx); + if (pc && has_capability("attributes", pc->capability)) + iget_uint8(&iter, &preq->attribute_block); + else + preq->attribute_block = 0; pack_iter_end(&iter, pc); remove_packet_from_buffer(pc->buffer); @@ -4203,4 +4215,133 @@ freelog(LOG_ERROR, "invalid type in receive_packet_goto_route()"); return NULL; } +} + +/************************************************************************** +... +**************************************************************************/ +int send_packet_attribute_chunk(struct connection *pc, + struct packet_attribute_chunk *packet) +{ + unsigned char buffer[MAX_LEN_PACKET], *cptr; + + assert(packet->total_length > 0 + && packet->total_length < MAX_ATTRIBUTE_BLOCK); + /* 500 bytes header, just to be sure */ + assert(packet->chunk_length > 0 + && packet->chunk_length < MAX_LEN_PACKET - 500); + assert(packet->chunk_length <= packet->total_length); + assert(packet->offset >= 0 && packet->offset < packet->total_length); + + freelog(LOG_DEBUG, "sending attribute chunk %d/%d %d", packet->offset, + packet->total_length, packet->chunk_length); + + cptr = put_uint8(buffer + 2, PACKET_ATTRIBUTE_CHUNK); + + cptr = put_uint32(cptr, packet->offset); + cptr = put_uint32(cptr, packet->total_length); + cptr = put_uint32(cptr, packet->chunk_length); + + memcpy(cptr, packet->data, packet->chunk_length); + cptr+=packet->chunk_length; + + put_uint16(buffer, cptr - buffer); + send_connection_data(pc, buffer, cptr - buffer); + + return 0; +} +/************************************************************************** +.. +**************************************************************************/ +struct packet_attribute_chunk *receive_packet_attribute_chunk(struct + connection + *pc) +{ + struct pack_iter iter; + struct packet_attribute_chunk *packet = + fc_malloc(sizeof(struct packet_attribute_chunk)); + + pack_iter_init(&iter, pc); + + iget_uint32(&iter, &packet->offset); + iget_uint32(&iter, &packet->total_length); + iget_uint32(&iter, &packet->chunk_length); + + assert(packet->total_length > 0 + && packet->total_length < MAX_ATTRIBUTE_BLOCK); + /* 500 bytes header, just to be sure */ + assert(packet->chunk_length > 0 + && packet->chunk_length < MAX_LEN_PACKET - 500); + assert(packet->chunk_length <= packet->total_length); + assert(packet->offset >= 0 && packet->offset < packet->total_length); + + assert(pack_iter_remaining(&iter) != -1); + assert(pack_iter_remaining(&iter) == packet->chunk_length); + + memcpy(packet->data, iter.ptr, packet->chunk_length); + + pack_iter_end(&iter, pc); + remove_packet_from_buffer(pc->buffer); + + freelog(LOG_DEBUG, "received attribute chunk %d/%d %d", packet->offset, + packet->total_length, packet->chunk_length); + + return packet; +} + +/************************************************************************** + Updates pplayer->attribute_block according to the given packet. +**************************************************************************/ +void generic_handle_attribute_chunk(struct player *pplayer, + struct packet_attribute_chunk *chunk) +{ + /* first one in a row */ + if (chunk->offset == 0) { + if (pplayer->attribute_block.data != NULL) { + free(pplayer->attribute_block.data); + pplayer->attribute_block.data = NULL; + } + pplayer->attribute_block.data = fc_malloc(chunk->total_length); + pplayer->attribute_block.length = chunk->total_length; + } + memcpy(pplayer->attribute_block.data + chunk->offset, + chunk->data, chunk->chunk_length); +} + +/************************************************************************** + Split the attribute block into chunks and send them over pconn. +**************************************************************************/ +void send_attribute_block(const struct player *pplayer, + struct connection *pconn) +{ + struct packet_attribute_chunk packet; + int current_chunk, chunks, bytes_left; + + if (pplayer->attribute_block.data == NULL) + return; + + assert(pplayer->attribute_block.length > 0 && + pplayer->attribute_block.length < MAX_ATTRIBUTE_BLOCK); + + chunks = + (pplayer->attribute_block.length - 1) / ATTRIBUTE_CHUNK_SIZE + 1; + bytes_left = pplayer->attribute_block.length; + + connection_do_buffer(pconn); + + for (current_chunk = 0; current_chunk < chunks; current_chunk++) { + int size_of_current_chunk = MIN(bytes_left, ATTRIBUTE_CHUNK_SIZE); + + packet.offset = ATTRIBUTE_CHUNK_SIZE * current_chunk; + packet.total_length = pplayer->attribute_block.length; + packet.chunk_length = size_of_current_chunk; + + memcpy(packet.data, pplayer->attribute_block.data + packet.offset, + packet.chunk_length); + bytes_left -= packet.chunk_length; + + send_packet_attribute_chunk(pconn, &packet); + } + + connection_do_unbuffer(pconn); } diff -X freeciv/diff_ignore -Nurd freeciv/common/packets.h freeciv.attr/common/packets.h --- freeciv/common/packets.h Wed Jan 24 15:57:47 2001 +++ freeciv.attr/common/packets.h Wed Feb 14 14:35:53 2001 @@ -20,8 +20,10 @@ #include "spaceship.h" #include "worklist.h" -#define MAX_LEN_USERNAME 10 /* see below */ -#define MAX_LEN_MSG 1536 +#define MAX_LEN_USERNAME 10 /* see below */ +#define MAX_LEN_MSG 1536 +#define MAX_ATTRIBUTE_BLOCK (1024*64) /* largest attribute block */ +#define ATTRIBUTE_CHUNK_SIZE (1024*2) /* attribute chunk size to use */ /* Note that MAX_LEN_USERNAME cannot be expanded, because it is used for the name in the first packet sent by the client, @@ -115,6 +117,8 @@ PACKET_PLAYER_REMOVE_VISION, PACKET_GOTO_ROUTE, PACKET_PATROL_ROUTE, + PACKET_ATTRIBUTE_CHUNK, + PACKET_PLAYER_ATTRIBUTE_BLOCK, PACKET_LAST /* leave this last */ }; @@ -231,6 +235,7 @@ int tech; /* research */ struct worklist worklist; /* one worklist */ int wl_idx; /* which worklist */ + int attribute_block; /* send attribute block as chunks */ }; /********************************************************* @@ -850,6 +855,13 @@ int unit_id; }; +struct packet_attribute_chunk +{ + int offset, total_length, chunk_length; + /* to keep memory management simple don't allocate dynamic memory */ + unsigned char data[ATTRIBUTE_CHUNK_SIZE]; +}; + /* These two are non-static for meta.c; others are now static --dwp */ unsigned char *put_uint16(unsigned char *buffer, int val); unsigned char *put_string(unsigned char *buffer, const char *mystring); @@ -1061,6 +1073,16 @@ const struct packet_goto_route *packet, enum goto_route_type type); struct packet_goto_route *receive_packet_goto_route(struct connection *pc); + +int send_packet_attribute_chunk(struct connection *pc, + struct packet_attribute_chunk *packet); +struct packet_attribute_chunk *receive_packet_attribute_chunk(struct + connection + *pc); +void send_attribute_block(const struct player *pplayer, + struct connection *pconn); +void generic_handle_attribute_chunk(struct player *pplayer, + struct packet_attribute_chunk *chunk); #include "packets_lsend.h" /* lsend_packet_* functions */ diff -X freeciv/diff_ignore -Nurd freeciv/common/player.c freeciv.attr/common/player.c --- freeciv/common/player.c Sat Jan 13 18:38:03 2001 +++ freeciv.attr/common/player.c Wed Feb 14 14:35:53 2001 @@ -101,6 +101,8 @@ } plr->gives_shared_vision = 0; plr->really_gives_vision = 0; + plr->attribute_block.data = NULL; + plr->attribute_block.length = 0; } /*************************************************************** diff -X freeciv/diff_ignore -Nurd freeciv/common/player.h freeciv.attr/common/player.h --- freeciv/common/player.h Wed Sep 6 23:41:16 2000 +++ freeciv.attr/common/player.h Wed Feb 14 14:35:53 2001 @@ -45,7 +45,6 @@ /* anything else I have forgotten? Let me know. -- Syela */ }; - struct player_economic { int gold; int tax; @@ -158,6 +157,10 @@ struct player_tile *private_map; unsigned int gives_shared_vision; /* bitvector those that give you shared vision */ unsigned int really_gives_vision; /* takes into account that p3 may see what p1 has via p2 */ + struct { + int length; + void *data; + } attribute_block; }; void player_init(struct player *plr); diff -X freeciv/diff_ignore -Nurd freeciv/server/plrhand.c freeciv.attr/server/plrhand.c --- freeciv/server/plrhand.c Tue Feb 13 18:53:22 2001 +++ freeciv.attr/server/plrhand.c Wed Feb 14 14:38:27 2001 @@ -1740,3 +1740,21 @@ "rebel provinces."), pplayer->name, cplayer->name, city_list_size(&cplayer->cities)); } + +/************************************************************************** + The client has send as a chunk of the attribute block. +**************************************************************************/ +void handle_player_attribute_chunk(struct player *pplayer, + struct packet_attribute_chunk *chunk) +{ + generic_handle_attribute_chunk(pplayer, chunk); +} + +/************************************************************************** + The client request an attribute block. +**************************************************************************/ +void handle_player_attribute_block(struct player *pplayer, + struct packet_player_request *packet) +{ + send_attribute_block(pplayer, pplayer->current_conn); +} diff -X freeciv/diff_ignore -Nurd freeciv/server/plrhand.h freeciv.attr/server/plrhand.h --- freeciv/server/plrhand.h Tue Feb 13 18:53:22 2001 +++ freeciv.attr/server/plrhand.h Wed Feb 14 14:35:53 2001 @@ -14,6 +14,7 @@ #define FC__PLRHAND_H #include "attribute.h" +#include "packets.h" struct player; struct packet_player_request; @@ -66,6 +67,10 @@ struct packet_player_request *preq); void handle_player_worklist(struct player *pplayer, struct packet_player_request *preq); +void handle_player_attribute_chunk(struct player *pplayer, + struct packet_attribute_chunk *preq); +void handle_player_attribute_block(struct player *pplayer, + struct packet_player_request *packet); void found_new_tech(struct player *plr, int tech_found, char was_discovery, char saving_bulbs); void tech_researched(struct player* plr); diff -X freeciv/diff_ignore -Nurd freeciv/server/savegame.c freeciv.attr/server/savegame.c --- freeciv/server/savegame.c Tue Feb 13 18:53:22 2001 +++ freeciv.attr/server/savegame.c Wed Feb 14 14:35:53 2001 @@ -58,8 +58,53 @@ * include it when appropriate for maximum savegame compatibility.) */ #define SAVEFILE_OPTIONS "1.7 startoptions spacerace2 rulesets" \ -" diplchance_percent worklists2 map_editor" +" diplchance_percent worklists2 map_editor attributes" + +/*************************************************************** +Quote the memory block denoted by data and length so it consists only +of " a-f0-9:". The returned string has the freed by the caller using +free(). +***************************************************************/ +char *quote_block(void *data, int length) +{ + char *buffer = malloc(length * 3 + 10); + int offset, i; + + sprintf(buffer, "%d:", length); + offset = strlen(buffer); + for (i = 0; i < length; i++) { + sprintf(buffer + offset, "%02x ", ((unsigned char *) data)[i]); + offset += 3; + } + return buffer; +} + +/*************************************************************** +Unquote a string. The unquoted data is written into dest. If the +unqoted data will be largern than dest_length the function aborts. It +returns the actual length of the unquoted block. +***************************************************************/ +int unquote_block(char *quoted, void *dest, int dest_length) +{ + int i, length, parsed; + + parsed = sscanf(quoted, "%d", &length); + assert(parsed == 1); + + assert(length <= dest_length); + quoted = strchr(quoted, ':') + 1; + + for (i = 0; i < length; i++) { + int tmp; + parsed = sscanf(quoted, "%x", &tmp); + assert(parsed == 1); + assert((tmp & 0xff) == tmp); + ((unsigned char *) dest)[i] = tmp; + quoted += 3; + } + return length; +} /*************************************************************** load starting positions for the players from a savegame file @@ -984,6 +1029,44 @@ unit_list_insert(&map_get_tile(punit->x, punit->y)->units, punit); } + + if (section_file_lookup(file, "player%d.attribute_block_length", plrno)) { + int raw_length1, raw_length2, part_nr, parts; + char *quoted; + + raw_length1 = + secfile_lookup_int(file, "player%d.attribute_block_length", plrno); + if (plr->attribute_block.data != NULL) { + free(plr->attribute_block.data); + plr->attribute_block.data = NULL; + } + plr->attribute_block.data = fc_malloc(raw_length1); + plr->attribute_block.length = raw_length1; + + quoted = + fc_malloc(secfile_lookup_int + (file, "player%d.attribute_block_length_quoted", + plrno) + 1); + quoted[0] = 0; + + parts = + secfile_lookup_int(file, "player%d.attribute_block_parts", plrno); + + for (part_nr = 0; part_nr < parts; part_nr++) { + char *current = secfile_lookup_str(file, + "player%d.attribute_block_data.part%d", + plrno, part_nr); + if (!current) + break; + strcat(quoted, current); + } + raw_length2 = + unquote_block(quoted, + plr->attribute_block.data, + plr->attribute_block.length); + assert(raw_length1 == raw_length2); + free(quoted); + } } /********************************************************************** @@ -1551,6 +1634,38 @@ } } secfile_insert_int(file, i, "player%d.total_ncities", plrno); + } + +#define PART_SIZE (2*1024) + if (plr->attribute_block.data != NULL) { + char *quoted = quote_block(plr->attribute_block.data, + plr->attribute_block.length); + char part[PART_SIZE + 1]; + int current_part_nr, parts, bytes_left; + + secfile_insert_int(file, plr->attribute_block.length, + "player%d.attribute_block_length", plrno); + secfile_insert_int(file, strlen(quoted), + "player%d.attribute_block_length_quoted", plrno); + + parts = (strlen(quoted) - 1) / PART_SIZE + 1; + bytes_left = strlen(quoted); + + secfile_insert_int(file, parts, + "player%d.attribute_block_parts", plrno); + + for (current_part_nr = 0; current_part_nr < parts; current_part_nr++) { + int size_of_current_part = MIN(bytes_left, PART_SIZE); + + memcpy(part, quoted + PART_SIZE * current_part_nr, + size_of_current_part); + part[PART_SIZE] = 0; + secfile_insert_str(file, part, + "player%d.attribute_block_data.part%d", plrno, + current_part_nr); + + } + free(quoted); } } diff -X freeciv/diff_ignore -Nurd freeciv/server/srv_main.c freeciv.attr/server/srv_main.c --- freeciv/server/srv_main.c Tue Feb 13 18:53:23 2001 +++ freeciv.attr/server/srv_main.c Wed Feb 14 14:35:53 2001 @@ -522,6 +522,11 @@ **************************************************************************/ static void send_all_info(struct conn_list *dest) { + conn_list_iterate(*dest, pconn) { + send_attribute_block(pconn->player,pconn); + } + conn_list_iterate_end; + send_game_info(dest); send_map_info(dest); send_player_info_c(0, dest); @@ -1112,6 +1117,17 @@ case PACKET_PATROL_ROUTE: handle_patrol_route(pplayer, (struct packet_goto_route *)packet); break; + case PACKET_ATTRIBUTE_CHUNK: + handle_player_attribute_chunk(pplayer, + (struct packet_attribute_chunk *) + packet); + break; + case PACKET_PLAYER_ATTRIBUTE_BLOCK: + handle_player_attribute_block(pplayer, + (struct packet_player_request *) + packet); + break; + default: freelog(LOG_ERROR, "Received unknown packet %d from %s", type, conn_description(pconn));