diff -ruN -X freeciv/diff_ignore freeciv.patched/client/Makefile.am freeciv.mod/client/Makefile.am --- freeciv.patched/client/Makefile.am Sat Oct 28 00:19:58 2000 +++ freeciv.mod/client/Makefile.am Tue Jan 16 16:53:42 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 -ruN -X freeciv/diff_ignore freeciv.patched/client/attribute.c freeciv.mod/client/attribute.c --- freeciv.patched/client/attribute.c Thu Jan 1 01:00:00 1970 +++ freeciv.mod/client/attribute.c Wed Jan 17 14:12:55 2001 @@ -0,0 +1,315 @@ + +#include +#include + +#include "hash.h" +#include "mem.h" +#include "game.h" +#include "packets.h" +#include "clinet.h" +#include "log.h" + +#include "attribute.h" + +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 (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, + void *data) +{ + struct attr_key *pkey; + void *pvalue = NULL; + + 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)) { + if (data_length == 0) { + void *old_value = hash_delete_entry(attribute_hash, pkey); + free(old_value); + } else { + hash_replace(attribute_hash, pkey, pvalue); + } + free(pkey); + } else { + 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; + + 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) + return 0; + + length = ((int *) pvalue)[0]; + assert(max_data_length >= length); + memcpy(data, pvalue + sizeof(int), length); + + return length; +} + +void attr_unit_set(enum attr_unit what, int unit_id, int data_length, + void *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, + void *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_player_set(enum attr_player what, int player_id, int data_length, + void *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, + void *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 -ruN -X freeciv/diff_ignore freeciv.patched/client/attribute.h freeciv.mod/client/attribute.h --- freeciv.patched/client/attribute.h Thu Jan 1 01:00:00 1970 +++ freeciv.mod/client/attribute.h Tue Jan 16 21:28:02 2001 @@ -0,0 +1,107 @@ +/********************************************************************** + 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_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, + void *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, + void *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, + void *data); +int attr_city_get(enum attr_city what, int city_id, int max_data_length, + void *data); + +/* + * Special methods for players. + */ +void attr_player_set(enum attr_player what, int player_id, int data_length, + void *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, + void *data); +int attr_tile_get(enum attr_tile what, int x, int y, int max_data_length, + void *data); +#endif diff -ruN -X freeciv/diff_ignore freeciv.patched/client/civclient.c freeciv.mod/client/civclient.c --- freeciv.patched/client/civclient.c Wed Jan 17 13:16:49 2001 +++ freeciv.mod/client/civclient.c Wed Jan 17 13:10:36 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 */ @@ -377,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); } - /************************************************************************** ... **************************************************************************/ diff -ruN -X freeciv/diff_ignore freeciv.patched/client/civclient.h freeciv.mod/client/civclient.h --- freeciv.patched/client/civclient.h Wed Jan 17 13:16:49 2001 +++ freeciv.mod/client/civclient.h Tue Jan 16 21:19:51 2001 @@ -22,7 +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 send_attribute_block_request(void); +void send_turn_done(void); void user_ended_turn(void); diff -ruN -X freeciv/diff_ignore freeciv.patched/client/control.c freeciv.mod/client/control.c --- freeciv.patched/client/control.c Mon Jan 15 17:59:01 2001 +++ freeciv.mod/client/control.c Tue Jan 16 21:15:18 2001 @@ -1202,14 +1202,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 -ruN -X freeciv/diff_ignore freeciv.patched/client/packhand.c freeciv.mod/client/packhand.c --- freeciv.patched/client/packhand.c Wed Jan 17 13:16:49 2001 +++ freeciv.mod/client/packhand.c Wed Jan 17 13:12:47 2001 @@ -59,11 +59,48 @@ #include "spaceshipdlg_g.h" #include "tilespec.h" #include "wldlg_g.h" +#include "attribute.h" #include "packhand.h" static void handle_city_packet_common(struct city *pcity, int is_new, int popup); +#define TEST_ATTRIBUTES + +#ifdef TEST_ATTRIBUTES +static unsigned char foobar(int unit_id, int i) +{ + return (unit_id + i); +} + +#define _SIZE 10000 +static void attach_test_attribute(int unit_id) +{ + unsigned char buffer[_SIZE]; + int length, i; + + length = + attr_unit_get(ATTR_UNIT_DUMMY, unit_id, sizeof(buffer), buffer); + if (length > 0) { + + freelog(LOG_NORMAL, "unit %d has an attribute of size %d", unit_id, + length); + for (i = 0; i < length; i++) { + assert(buffer[i] == foobar(unit_id,i)); + } + freelog(LOG_NORMAL, " content is ok"); + } else { + length = (rand() % (_SIZE-1))+1; + for (i = 0; i < length; i++) + { + buffer[i] = foobar(unit_id,i); + } + freelog(LOG_NORMAL, "attaching attribute of size %d to unit %d", + length, unit_id); + attr_unit_set(ATTR_UNIT_DUMMY, unit_id, length, buffer); + } +} +#endif /************************************************************************** ... @@ -218,7 +255,6 @@ struct city *pcity; struct unit *punit; - send_attribute_block_request(); refresh_overview_canvas(); refresh_overview_viewrect(); enable_turn_done_button(); @@ -698,7 +734,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 @@ -740,6 +775,9 @@ repaint_unit = 0; repaint_city = 0; punit = player_find_unit_by_id(&game.players[packet->owner], packet->id); +#ifdef TEST_ATTRIBUTES + attach_test_attribute(packet->id); +#endif if(punit) { int dest_x,dest_y; @@ -2171,5 +2209,6 @@ if (chunk->offset + chunk->chunk_length == chunk->total_length) { /* We successful received the last chunk. The attribute block is now complete. */ + attribute_restore(); } } diff -ruN -X freeciv/diff_ignore freeciv.patched/common/hash.c freeciv.mod/common/hash.c --- freeciv.patched/common/hash.c Sun Aug 6 13:47:09 2000 +++ freeciv.mod/common/hash.c Wed Jan 17 12:00:01 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 -ruN -X freeciv/diff_ignore freeciv.patched/common/hash.h freeciv.mod/common/hash.h --- freeciv.patched/common/hash.h Sun Aug 6 13:47:09 2000 +++ freeciv.mod/common/hash.h Wed Jan 17 13:13: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 -ruN -X freeciv/diff_ignore freeciv.patched/common/packets.c freeciv.mod/common/packets.c --- freeciv.patched/common/packets.c Wed Jan 17 13:16:49 2001 +++ freeciv.mod/common/packets.c Wed Jan 17 13:14:48 2001 @@ -4219,6 +4219,9 @@ 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); @@ -4266,6 +4269,9 @@ 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; } @@ -4287,6 +4293,7 @@ memcpy(pplayer->attribute_block.data + chunk->offset, chunk->data, chunk->chunk_length); } + /************************************************************************** Split the attribute block into chunks and send them over pconn. **************************************************************************/ diff -ruN -X freeciv/diff_ignore freeciv.patched/server/srv_main.c freeciv.mod/server/srv_main.c --- freeciv.patched/server/srv_main.c Wed Jan 17 13:16:50 2001 +++ freeciv.mod/server/srv_main.c Wed Jan 17 12:13:47 2001 @@ -525,6 +525,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);