Stop sharing requirement_unit_state_ereq().
[freeciv.git] / client / attribute.c
blob666508cf0a56b68da7f84fdb9d64cc88bf1194cd
1 /**********************************************************************
2 Freeciv - Copyright (C) 2001 - R. Falke
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 /* utility */
19 #include "dataio.h"
20 #include "fcintl.h"
21 #include "genhash.h" /* genhash_val_t */
22 #include "log.h"
23 #include "mem.h"
25 /* common */
26 #include "packets.h"
28 /* client */
29 #include "client_main.h"
31 #include "attribute.h"
33 #define log_attribute log_debug
35 enum attribute_serial {
36 A_SERIAL_FAIL,
37 A_SERIAL_OK,
38 A_SERIAL_OLD,
41 struct attr_key {
42 int key, id, x, y;
45 static genhash_val_t attr_key_val(const struct attr_key *pkey);
46 static bool attr_key_comp(const struct attr_key *pkey1,
47 const struct attr_key *pkey2);
48 static struct attr_key *attr_key_dup(const struct attr_key *pkey);
49 static void attr_key_destroy(struct attr_key *pkey);
51 /* 'struct attribute_hash' and related functions. */
52 #define SPECHASH_TAG attribute
53 #define SPECHASH_IKEY_TYPE struct attr_key *
54 #define SPECHASH_IDATA_TYPE void *
55 #define SPECHASH_IKEY_VAL attr_key_val
56 #define SPECHASH_IKEY_COMP attr_key_comp
57 #define SPECHASH_IKEY_COPY attr_key_dup
58 #define SPECHASH_IKEY_FREE attr_key_destroy
59 #define SPECHASH_IDATA_FREE free
60 #include "spechash.h"
61 #define attribute_hash_values_iterate(hash, pvalue) \
62 TYPED_HASH_DATA_ITERATE(void *, hash, pvalue)
63 #define attribute_hash_values_iterate_end HASH_DATA_ITERATE_END
64 #define attribute_hash_iterate(hash, pkey, pvalue) \
65 TYPED_HASH_ITERATE(const struct attr_key *, void *, hash, pkey, pvalue)
66 #define attribute_hash_iterate_end HASH_ITERATE_END
69 static struct attribute_hash *attribute_hash = NULL;
71 /****************************************************************************
72 Hash function for attribute_hash.
73 ****************************************************************************/
74 static genhash_val_t attr_key_val(const struct attr_key *pkey)
76 return (genhash_val_t) pkey->id ^ pkey->x ^ pkey->y ^ pkey->key;
79 /****************************************************************************
80 Compare-function for the keys in the hash table.
81 ****************************************************************************/
82 static bool attr_key_comp(const struct attr_key *pkey1,
83 const struct attr_key *pkey2)
85 return pkey1->key == pkey2->key
86 && pkey1->id == pkey2->id
87 && pkey1->x == pkey2->x
88 && pkey1->y == pkey2->y;
91 /****************************************************************************
92 Duplicate an attribute key.
93 ****************************************************************************/
94 static struct attr_key *attr_key_dup(const struct attr_key *pkey)
96 struct attr_key *pnew_key = fc_malloc(sizeof(*pnew_key));
98 *pnew_key = *pkey;
99 return pnew_key;
102 /****************************************************************************
103 Free an attribute key.
104 ****************************************************************************/
105 static void attr_key_destroy(struct attr_key *pkey)
107 fc_assert_ret(NULL != pkey);
108 free(pkey);
111 /****************************************************************************
112 Initializes the attribute module.
113 ****************************************************************************/
114 void attribute_init(void)
116 fc_assert(NULL == attribute_hash);
117 attribute_hash = attribute_hash_new();
120 /****************************************************************************
121 Frees the attribute module.
122 ****************************************************************************/
123 void attribute_free(void)
125 fc_assert_ret(NULL != attribute_hash);
126 attribute_hash_destroy(attribute_hash);
127 attribute_hash = NULL;
130 /****************************************************************************
131 Serialize an attribute hash for network/storage.
132 ****************************************************************************/
133 static enum attribute_serial
134 serialize_hash(const struct attribute_hash *hash,
135 void **pdata, int *pdata_length)
138 * Layout of version 2:
140 * struct {
141 * uint32 0; always != 0 in version 1
142 * uint8 2;
143 * uint32 entries;
144 * uint32 total_size_in_bytes;
145 * } preamble;
147 * struct {
148 * uint32 value_size;
149 * char key[], char value[];
150 * } body[entries];
152 const size_t entries = attribute_hash_size(hash);
153 int total_length, value_lengths[entries];
154 void *result;
155 struct raw_data_out dout;
156 int i;
159 * Step 1: loop through all keys and fill value_lengths and calculate
160 * the total_length.
162 /* preamble */
163 total_length = 4 + 1 + 4 + 4;
164 /* body */
165 total_length += entries * (4 + 4 + 4 + 2 + 2); /* value_size + key */
166 i = 0;
167 attribute_hash_values_iterate(hash, pvalue) {
168 struct data_in din;
170 dio_input_init(&din, pvalue, 4);
171 dio_get_uint32_raw(&din, &value_lengths[i]);
173 total_length += value_lengths[i];
174 i++;
175 } attribute_hash_values_iterate_end;
178 * Step 2: allocate memory.
180 result = fc_malloc(total_length);
181 dio_output_init(&dout, result, total_length);
184 * Step 3: fill out the preamble.
186 dio_put_uint32_raw(&dout, 0);
187 dio_put_uint8_raw(&dout, 2);
188 dio_put_uint32_raw(&dout, attribute_hash_size(hash));
189 dio_put_uint32_raw(&dout, total_length);
192 * Step 4: fill out the body.
194 i = 0;
195 attribute_hash_iterate(hash, pkey, pvalue) {
196 dio_put_uint32_raw(&dout, value_lengths[i]);
198 dio_put_uint32_raw(&dout, pkey->key);
199 dio_put_uint32_raw(&dout, pkey->id);
200 dio_put_sint16_raw(&dout, pkey->x);
201 dio_put_sint16_raw(&dout, pkey->y);
203 dio_put_memory_raw(&dout, ADD_TO_POINTER(pvalue, 4), value_lengths[i]);
204 i++;
205 } attribute_hash_iterate_end;
207 fc_assert(!dout.too_short);
208 fc_assert_msg(dio_output_used(&dout) == total_length,
209 "serialize_hash() total_length = %lu, actual = %lu",
210 (long unsigned)total_length,
211 (long unsigned)dio_output_used(&dout));
214 * Step 5: return.
216 *pdata = result;
217 *pdata_length = total_length;
218 log_attribute("attribute.c serialize_hash() "
219 "serialized %lu entries in %lu bytes",
220 (long unsigned) entries, (long unsigned) total_length);
221 return A_SERIAL_OK;
224 /****************************************************************************
225 This data was serialized (above), sent as an opaque data packet to the
226 server, stored in a savegame, retrieved from the savegame, sent as an
227 opaque data packet back to the client, and now is ready to be restored.
228 Check everything!
229 ****************************************************************************/
230 static enum attribute_serial unserialize_hash(struct attribute_hash *hash,
231 const void *data,
232 size_t data_length)
234 int entries, i, dummy;
235 struct data_in din;
237 attribute_hash_clear(hash);
239 dio_input_init(&din, data, data_length);
241 dio_get_uint32_raw(&din, &dummy);
242 if (dummy != 0) {
243 log_verbose("attribute.c unserialize_hash() preamble, uint32 %lu != 0",
244 (long unsigned) dummy);
245 return A_SERIAL_OLD;
247 dio_get_uint8_raw(&din, &dummy);
248 if (dummy != 2) {
249 log_verbose("attribute.c unserialize_hash() preamble, "
250 "uint8 %lu != 2 version", (long unsigned) dummy);
251 return A_SERIAL_OLD;
253 dio_get_uint32_raw(&din, &entries);
254 dio_get_uint32_raw(&din, &dummy);
255 if (dummy != data_length) {
256 log_verbose("attribute.c unserialize_hash() preamble, "
257 "uint32 %lu != %lu data_length",
258 (long unsigned) dummy, (long unsigned) data_length);
259 return A_SERIAL_FAIL;
262 log_attribute("attribute.c unserialize_hash() "
263 "uint32 %lu entries, %lu data_length",
264 (long unsigned) entries, (long unsigned) data_length);
266 for (i = 0; i < entries; i++) {
267 struct attr_key key;
268 void *pvalue;
269 int value_length;
270 struct raw_data_out dout;
272 if (!dio_get_uint32_raw(&din, &value_length)) {
273 log_verbose("attribute.c unserialize_hash() "
274 "uint32 value_length dio_input_too_short");
275 return A_SERIAL_FAIL;
277 log_attribute("attribute.c unserialize_hash() "
278 "uint32 %lu value_length", (long unsigned) value_length);
280 /* next 12 bytes */
281 if (!dio_get_uint32_raw(&din, &key.key)
282 || !dio_get_uint32_raw(&din, &key.id)
283 || !dio_get_sint16_raw(&din, &key.x)
284 || !dio_get_sint16_raw(&din, &key.y)) {
285 log_verbose("attribute.c unserialize_hash() "
286 "uint32 key dio_input_too_short");
287 return A_SERIAL_FAIL;
289 pvalue = fc_malloc(value_length + 4);
291 dio_output_init(&dout, pvalue, value_length + 4);
292 dio_put_uint32_raw(&dout, value_length);
293 if (!dio_get_memory_raw(&din, ADD_TO_POINTER(pvalue, 4), value_length)) {
294 log_verbose("attribute.c unserialize_hash() "
295 "memory dio_input_too_short");
296 return A_SERIAL_FAIL;
299 if (!attribute_hash_insert(hash, &key, pvalue)) {
300 /* There are some untraceable attribute bugs caused by the CMA that
301 * can cause this to happen. I think the only safe thing to do is
302 * to delete all attributes. Another symptom of the bug is the
303 * value_length (above) is set to a random value, which can also
304 * cause a bug. */
305 free(pvalue);
306 attribute_hash_clear(hash);
307 return A_SERIAL_FAIL;
311 if (dio_input_remaining(&din) > 0) {
312 /* This is not an error, as old clients sent overlong serialized
313 * attributes pre gna bug #21295, and these will be hanging around
314 * in savefiles forever. */
315 log_attribute("attribute.c unserialize_hash() "
316 "ignored %lu trailing octets",
317 (long unsigned) dio_input_remaining(&din));
320 return A_SERIAL_OK;
323 /****************************************************************************
324 Send current state to the server. Note that the current
325 implementation will send all attributes to the server.
326 ****************************************************************************/
327 void attribute_flush(void)
329 struct player *pplayer = client_player();
331 if (!pplayer || client_is_observer() || !pplayer->is_alive) {
332 return;
335 fc_assert_ret(NULL != attribute_hash);
337 if (0 == attribute_hash_size(attribute_hash))
338 return;
340 if (pplayer->attribute_block.data) {
341 free(pplayer->attribute_block.data);
342 pplayer->attribute_block.data = NULL;
345 serialize_hash(attribute_hash, &pplayer->attribute_block.data,
346 &pplayer->attribute_block.length);
347 send_attribute_block(pplayer, &client.conn);
350 /****************************************************************************
351 Recreate the attribute set from the player's
352 attribute_block. Shouldn't be used by normal code.
353 ****************************************************************************/
354 void attribute_restore(void)
356 struct player *pplayer = client_player();
358 if (!pplayer) {
359 return;
362 fc_assert_ret(attribute_hash != NULL);
364 switch (unserialize_hash(attribute_hash,
365 pplayer->attribute_block.data,
366 pplayer->attribute_block.length)) {
367 case A_SERIAL_FAIL:
368 log_error(_("There has been a CMA error. "
369 "Your citizen governor settings may be broken."));
370 break;
371 case A_SERIAL_OLD:
372 log_normal(_("Old attributes detected and removed."));
373 break;
374 default:
375 break;
379 /****************************************************************************
380 Low-level function to set an attribute. If data_length is zero the
381 attribute is removed.
382 ****************************************************************************/
383 void attribute_set(int key, int id, int x, int y, size_t data_length,
384 const void *const data)
386 struct attr_key akey = { .key = key, .id = id, .x = x, .y = y };
388 log_attribute("attribute_set(key = %d, id = %d, x = %d, y = %d, "
389 "data_length = %lu, data = %p)", key, id, x, y,
390 (long unsigned) data_length, data);
392 fc_assert_ret(NULL != attribute_hash);
394 if (0 != data_length) {
395 void *pvalue = fc_malloc(data_length + 4);
396 struct raw_data_out dout;
398 dio_output_init(&dout, pvalue, data_length + 4);
399 dio_put_uint32_raw(&dout, data_length);
400 dio_put_memory_raw(&dout, data, data_length);
402 attribute_hash_replace(attribute_hash, &akey, pvalue);
403 } else {
404 attribute_hash_remove(attribute_hash, &akey);
408 /****************************************************************************
409 Low-level function to get an attribute. If data hasn't enough space
410 to hold the attribute data isn't set to the attribute. Returns the
411 actual size of the attribute. Can be zero if the attribute is
412 unset. To get the size of an attribute use
413 size = attribute_get(key, id, x, y, 0, NULL)
414 *****************************************************************************/
415 size_t attribute_get(int key, int id, int x, int y, size_t max_data_length,
416 void *data)
418 struct attr_key akey = { .key = key, .id = id, .x = x, .y = y };
419 void *pvalue;
420 int length;
421 struct data_in din;
423 log_attribute("attribute_get(key = %d, id = %d, x = %d, y = %d, "
424 "max_data_length = %lu, data = %p)", key, id, x, y,
425 (long unsigned) max_data_length, data);
427 fc_assert_ret_val(NULL != attribute_hash, 0);
429 if (!attribute_hash_lookup(attribute_hash, &akey, &pvalue)) {
430 log_attribute(" not found");
431 return 0;
434 dio_input_init(&din, pvalue, 0xffffffff);
435 dio_get_uint32_raw(&din, &length);
437 if (length <= max_data_length) {
438 dio_get_memory_raw(&din, data, length);
441 log_attribute(" found length = %d", length);
442 return length;
445 /****************************************************************************
446 Set unit related attribute
447 *****************************************************************************/
448 void attr_unit_set(enum attr_unit what, int unit_id, size_t data_length,
449 const void *const data)
451 attribute_set(what, unit_id, -1, -2, data_length, data);
454 /****************************************************************************
455 Get unit related attribute
456 *****************************************************************************/
457 size_t attr_unit_get(enum attr_unit what, int unit_id, size_t max_data_length,
458 void *data)
460 return attribute_get(what, unit_id, -1, -2, max_data_length, data);
463 /****************************************************************************
464 Set unit related integer attribute
465 *****************************************************************************/
466 void attr_unit_set_int(enum attr_unit what, int unit_id, int data)
468 attr_unit_set(what, unit_id, sizeof(int), &data);
471 /****************************************************************************
472 Get unit related integer attribute
473 *****************************************************************************/
474 size_t attr_unit_get_int(enum attr_unit what, int unit_id, int *data)
476 return attr_unit_get(what, unit_id, sizeof(int), data);
479 /****************************************************************************
480 Set city related attribute
481 *****************************************************************************/
482 void attr_city_set(enum attr_city what, int city_id, size_t data_length,
483 const void *const data)
485 attribute_set(what, city_id, -1, -1, data_length, data);
488 /****************************************************************************
489 Get city related attribute
490 *****************************************************************************/
491 size_t attr_city_get(enum attr_city what, int city_id, size_t max_data_length,
492 void *data)
494 return attribute_get(what, city_id, -1, -1, max_data_length, data);
497 /****************************************************************************
498 Set city related integer attribute
499 *****************************************************************************/
500 void attr_city_set_int(enum attr_city what, int city_id, int data)
502 attr_city_set(what, city_id, sizeof(int), &data);
505 /****************************************************************************
506 Get city related integer attribute
507 *****************************************************************************/
508 size_t attr_city_get_int(enum attr_city what, int city_id, int *data)
510 return attr_city_get(what, city_id, sizeof(int), data);
513 /****************************************************************************
514 Set player related attribute
515 *****************************************************************************/
516 void attr_player_set(enum attr_player what, int player_id, size_t data_length,
517 const void *const data)
519 attribute_set(what, player_id, -1, -1, data_length, data);
522 /****************************************************************************
523 Get player related attribute
524 *****************************************************************************/
525 size_t attr_player_get(enum attr_player what, int player_id,
526 size_t max_data_length, void *data)
528 return attribute_get(what, player_id, -1, -1, max_data_length, data);
531 /****************************************************************************
532 Set tile related attribute
533 *****************************************************************************/
534 void attr_tile_set(enum attr_tile what, int x, int y, size_t data_length,
535 const void *const data)
537 attribute_set(what, -1, x, y, data_length, data);
540 /****************************************************************************
541 Get tile related attribute
542 *****************************************************************************/
543 size_t attr_tile_get(enum attr_tile what, int x, int y, size_t max_data_length,
544 void *data)
546 return attribute_get(what, -1, x, y, max_data_length, data);