Freeciv21
Develop your civilization from humble roots to a global empire
attribute.cpp
Go to the documentation of this file.
1 /* ,%%%%%%%, ***************************************
2  ,%%/\%%%%/\%, Copyright (c) 1996-2021 Freeciv21 and Freeciv
3  ,%%%\c "" J/%%, contributors. This file is part of Freeciv21.
4  %%%%/ d b \%%% Freeciv21 is free software: you can redistribute it
5  %%%% _ |%%% and/or modify it under the terms of the GNU General
6  `%%%%(=_Y_=)%%' Public License as published by the Free Software
7  `%%%%`\7/%%%' Foundation, either version 3 of the License,
8  `%%%%%%%' or (at your option) any later version. You should
9  have received a copy of the GNU General Public License along
10  with Freeciv21. If not, see https://www.gnu.org/licenses/.
11  */
12 
13 #include <QHash>
14 
15 // utility
16 #include "dataio.h"
17 #include "fcintl.h"
18 #include "genhash.h" // genhash_val_t
19 #include "log.h"
20 
21 // common
22 #include "packets.h"
23 
24 // client
25 #include "client_main.h"
26 
27 #include "attribute.h"
28 
29 #define log_attribute log_debug
30 
35 };
36 
37 class attr_key {
38 public:
39  attr_key() = default;
40  attr_key(int, int, int, int);
41  int key{0}, id{0}, x{0}, y{0};
42 };
43 
44 attr_key::attr_key(int k, int i, int x0, int y0)
45  : key(k), id(i), x(x0), y(y0)
46 {
47 }
48 
49 inline bool operator==(const attr_key &pkey1, const attr_key &pkey2)
50 {
51  return pkey1.key == pkey2.key && pkey1.id == pkey2.id && pkey1.x == pkey2.x
52  && pkey1.y == pkey2.y;
53 }
54 
55 inline uint qHash(const attr_key &pkey, uint seed)
56 {
57  return qHash(pkey.id, seed) ^ pkey.x ^ pkey.y ^ pkey.key;
58 }
59 
60 typedef QHash<attr_key, void *> attributeHash;
61 Q_GLOBAL_STATIC(attributeHash, attribute_hash)
62 
63 
66 void attribute_init() {}
67 
72 {
73  for (auto *at : qAsConst(*attribute_hash)) {
74  delete[] static_cast<char *>(at);
75  }
76  attribute_hash->clear();
77 }
78 
83  QByteArray &data)
84 {
85  /*
86  * Layout of version 2:
87  *
88  * struct {
89  * uint32 0; always != 0 in version 1
90  * uint8 2;
91  * uint32 entries;
92  * uint32 total_size_in_bytes;
93  * } preamble;
94  *
95  * struct {
96  * uint32 value_size;
97  * char key[], char value[];
98  * } body[entries];
99  */
100  const size_t entries = hash->size();
101  int total_length;
102  std::vector<int> value_lengths;
103  value_lengths.resize(entries);
104  struct raw_data_out dout;
105  int i;
106 
107  /*
108  * Step 1: loop through all keys and fill value_lengths and calculate
109  * the total_length.
110  */
111  // preamble
112  total_length = 4 + 1 + 4 + 4;
113  // body
114  total_length += entries * (4 + 4 + 4 + 2 + 2); // value_size + key
115  i = 0;
116 
117  for (auto *pvalue : qAsConst(*hash)) {
118  struct data_in din;
119 
120  dio_input_init(&din, pvalue, 4);
121  fc_assert_ret_val(dio_get_uint32_raw(&din, &value_lengths[i]),
122  A_SERIAL_FAIL);
123 
124  total_length += value_lengths[i];
125  i++;
126  }
127 
128  /*
129  * Step 2: allocate memory.
130  */
131  data.resize(total_length);
132  dio_output_init(&dout, data.data(), total_length);
133 
134  /*
135  * Step 3: fill out the preamble.
136  */
137  dio_put_uint32_raw(&dout, 0);
138  dio_put_uint8_raw(&dout, 2);
139  dio_put_uint32_raw(&dout, hash->size());
140  dio_put_uint32_raw(&dout, total_length);
141 
142  /*
143  * Step 4: fill out the body.
144  */
145  i = 0;
146 
147  attributeHash::const_iterator it = hash->constBegin();
148  while (it != hash->constEnd()) {
149  dio_put_uint32_raw(&dout, value_lengths[i]);
150 
151  dio_put_uint32_raw(&dout, it.key().key);
152  dio_put_uint32_raw(&dout, it.key().id);
153  dio_put_sint16_raw(&dout, it.key().x);
154  dio_put_sint16_raw(&dout, it.key().y);
155 
156  dio_put_memory_raw(&dout, ADD_TO_POINTER(it.value(), 4),
157  value_lengths[i]);
158  i++;
159  ++it;
160  }
161 
162  fc_assert(!dout.too_short);
163  fc_assert_msg(dio_output_used(&dout) == total_length,
164  "serialize_hash() total_length = %lu, actual = %lu",
165  (long unsigned) total_length,
166  (long unsigned) dio_output_used(&dout));
167 
168  /*
169  * Step 5: return.
170  */
171  log_attribute("attribute.cpp serialize_hash() "
172  "serialized %lu entries in %lu bytes",
173  (long unsigned) entries, (long unsigned) total_length);
174  return A_SERIAL_OK;
175 }
176 
184  const QByteArray &data)
185 {
186  int entries, i, dummy;
187  struct data_in din;
188 
189  hash->clear();
190 
191  dio_input_init(&din, data.constData(), data.size());
192 
194  if (dummy != 0) {
195  qDebug("attribute.cpp unserialize_hash() preamble, uint32 %lu != 0",
196  static_cast<long unsigned>(dummy));
197  return A_SERIAL_OLD;
198  }
200  if (dummy != 2) {
201  qDebug("attribute.cpp unserialize_hash() preamble, "
202  "uint8 %lu != 2 version",
203  static_cast<long unsigned>(dummy));
204  return A_SERIAL_OLD;
205  }
208  if (dummy != data.size()) {
209  qDebug("attribute.cpp unserialize_hash() preamble, "
210  "uint32 %lu != %lu data_length",
211  static_cast<long unsigned>(dummy),
212  static_cast<long unsigned>(data.size()));
213  return A_SERIAL_FAIL;
214  }
215 
216  log_attribute("attribute.cpp unserialize_hash() "
217  "uint32 %lu entries, %lu data_length",
218  (long unsigned) entries, (long unsigned) data.size());
219 
220  for (i = 0; i < entries; i++) {
221  attr_key key;
222  void *pvalue;
223  int value_length;
224  struct raw_data_out dout;
225 
226  if (!dio_get_uint32_raw(&din, &value_length)) {
227  qDebug("attribute.cpp unserialize_hash() "
228  "uint32 value_length dio_input_too_short");
229  return A_SERIAL_FAIL;
230  }
231  log_attribute("attribute.cpp unserialize_hash() "
232  "uint32 %lu value_length",
233  (long unsigned) value_length);
234 
235  // next 12 bytes
236  if (!dio_get_uint32_raw(&din, &key.key)
237  || !dio_get_uint32_raw(&din, &key.id)
238  || !dio_get_sint16_raw(&din, &key.x)
239  || !dio_get_sint16_raw(&din, &key.y)) {
240  qDebug("attribute.cpp unserialize_hash() "
241  "uint32 key dio_input_too_short");
242  return A_SERIAL_FAIL;
243  }
244  pvalue = new char[value_length + 4];
245 
246  dio_output_init(&dout, pvalue, value_length + 4);
247  dio_put_uint32_raw(&dout, value_length);
248  if (!dio_get_memory_raw(&din, ADD_TO_POINTER(pvalue, 4), value_length)) {
249  qDebug("attribute.cpp unserialize_hash() "
250  "memory dio_input_too_short");
251  return A_SERIAL_FAIL;
252  }
253 
254  if (hash->contains(key)) {
255  /* There are some untraceable attribute bugs caused by the CMA that
256  * can cause this to happen. I think the only safe thing to do is
257  * to delete all attributes. Another symptom of the bug is the
258  * value_length (above) is set to a random value, which can also
259  * cause a bug. */
260  ::operator delete[](pvalue);
261  hash->clear();
262  return A_SERIAL_FAIL;
263  }
264 
265  hash->insert(key, pvalue);
266  }
267 
268  if (dio_input_remaining(&din) > 0) {
269  /* This is not an error, as old clients sent overlong serialized
270  * attributes pre gna bug #21295, and these will be hanging around
271  * in savefiles forever. */
272  log_attribute("attribute.cpp unserialize_hash() "
273  "ignored %lu trailing octets",
274  (long unsigned) dio_input_remaining(&din));
275  }
276 
277  return A_SERIAL_OK;
278 }
279 
285 {
286  struct player *pplayer = client_player();
287 
288  if (!pplayer || client_is_observer() || !pplayer->is_alive) {
289  return;
290  }
291 
292  if (0 == attribute_hash->size()) {
293  return;
294  }
295 
296  pplayer->attribute_block.clear();
297  serialize_hash(attribute_hash, pplayer->attribute_block);
298  send_attribute_block(pplayer, &client.conn);
299 }
300 
306 {
307  struct player *pplayer = client_player();
308 
309  if (!pplayer) {
310  return;
311  }
312 
313  fc_assert_ret(attribute_hash != nullptr);
314 
315  switch (unserialize_hash(attribute_hash, pplayer->attribute_block)) {
316  case A_SERIAL_FAIL:
317  qCritical(_("There has been a CMA error. "
318  "Your citizen governor settings may be broken."));
319  break;
320  case A_SERIAL_OLD:
321  qInfo(_("Old attributes detected and removed."));
322  break;
323  default:
324  break;
325  };
326 }
327 
332 void attribute_set(int key, int id, int x, int y, size_t data_length,
333  const void *const data)
334 {
335  class attr_key akey(key, id, x, y);
336 
337  log_attribute("attribute_set(key = %d, id = %d, x = %d, y = %d, "
338  "data_length = %lu, data = %p)",
339  key, id, x, y, (long unsigned) data_length, data);
340 
341  fc_assert_ret(nullptr != attribute_hash);
342 
343  if (0 != data_length) {
344  void *pvalue = new char[data_length + 4];
345  struct raw_data_out dout;
346 
347  dio_output_init(&dout, pvalue, data_length + 4);
348  dio_put_uint32_raw(&dout, data_length);
349  dio_put_memory_raw(&dout, data, data_length);
350 
351  attribute_hash->insert(akey, pvalue);
352  } else {
353  attribute_hash->remove(akey);
354  }
355 
356  // Sync with the server
357  attribute_flush();
358 }
359 
367 size_t attribute_get(int key, int id, int x, int y, size_t max_data_length,
368  void *data)
369 {
370  attr_key akey(key, id, x, y);
371  int length;
372  struct data_in din;
373 
374  log_attribute("attribute_get(key = %d, id = %d, x = %d, y = %d, "
375  "max_data_length = %lu, data = %p)",
376  key, id, x, y, (long unsigned) max_data_length, data);
377 
378  fc_assert_ret_val(nullptr != attribute_hash, 0);
379 
380  if (!attribute_hash->contains(akey)) {
381  log_attribute(" not found");
382  return 0;
383  }
384 
385  auto *pvalue = attribute_hash->value(akey);
386 
387  dio_input_init(&din, pvalue, 0xffffffff);
388  fc_assert_ret_val(dio_get_uint32_raw(&din, &length), 0);
389 
390  if (length <= max_data_length) {
391  dio_get_memory_raw(&din, data, length);
392  }
393 
394  log_attribute(" found length = %d", length);
395  return length;
396 }
397 
401 void attr_city_set(enum attr_city what, int city_id, size_t data_length,
402  const void *const data)
403 {
404  attribute_set(what, city_id, -1, -1, data_length, data);
405 }
406 
410 size_t attr_city_get(enum attr_city what, int city_id,
411  size_t max_data_length, void *data)
412 {
413  return attribute_get(what, city_id, -1, -1, max_data_length, data);
414 }
void attribute_set(int key, int id, int x, int y, size_t data_length, const void *const data)
Low-level function to set an attribute.
Definition: attribute.cpp:332
uint qHash(const attr_key &pkey, uint seed)
Definition: attribute.cpp:55
static enum attribute_serial unserialize_hash(attributeHash *hash, const QByteArray &data)
This data was serialized (above), sent as an opaque data packet to the server, stored in a savegame,...
Definition: attribute.cpp:183
void attribute_init()
Initializes the attribute module.
Definition: attribute.cpp:66
QHash< attr_key, void * > attributeHash
Definition: attribute.cpp:60
size_t attribute_get(int key, int id, int x, int y, size_t max_data_length, void *data)
Low-level function to get an attribute.
Definition: attribute.cpp:367
size_t attr_city_get(enum attr_city what, int city_id, size_t max_data_length, void *data)
Get city related attribute.
Definition: attribute.cpp:410
static enum attribute_serial serialize_hash(attributeHash *hash, QByteArray &data)
Serialize an attribute hash for network/storage.
Definition: attribute.cpp:82
void attribute_restore()
Recreate the attribute set from the player's attribute_block.
Definition: attribute.cpp:305
void attribute_flush()
Send current state to the server.
Definition: attribute.cpp:284
void attribute_free()
Frees the attribute module.
Definition: attribute.cpp:71
attribute_serial
Definition: attribute.cpp:31
@ A_SERIAL_OLD
Definition: attribute.cpp:34
@ A_SERIAL_OK
Definition: attribute.cpp:33
@ A_SERIAL_FAIL
Definition: attribute.cpp:32
bool operator==(const attr_key &pkey1, const attr_key &pkey2)
Definition: attribute.cpp:49
void attr_city_set(enum attr_city what, int city_id, size_t data_length, const void *const data)
Set city related attribute.
Definition: attribute.cpp:401
#define log_attribute
Definition: attribute.cpp:29
attr_city
Definition: attribute.h:21
attr_key()=default
struct player * client_player()
Either controlling or observing.
struct civclient client
bool client_is_observer()
Returns whether client is observer.
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.
Definition: dataio_raw.cpp:144
size_t dio_input_remaining(struct data_in *din)
Return the number of unread bytes.
Definition: dataio_raw.cpp:185
void dio_put_uint8_raw(struct raw_data_out *dout, int value)
Insert value using 8 bits.
Definition: dataio_raw.cpp:229
void dio_put_sint16_raw(struct raw_data_out *dout, int value)
Insert value using 16 bits.
Definition: dataio_raw.cpp:326
bool dio_get_uint32_raw(struct data_in *din, int *dest)
Receive uint32 value to dest.
Definition: dataio_raw.cpp:534
bool dio_get_sint16_raw(struct data_in *din, int *dest)
Take value from 16 bits.
Definition: dataio_raw.cpp:671
void dio_put_uint32_raw(struct raw_data_out *dout, int value)
Insert value using 32 bits.
Definition: dataio_raw.cpp:267
bool dio_get_memory_raw(struct data_in *din, void *dest, size_t dest_size)
Take memory block directly.
Definition: dataio_raw.cpp:710
void dio_put_memory_raw(struct raw_data_out *dout, const void *value, size_t size)
Insert block directly from memory.
Definition: dataio_raw.cpp:406
bool dio_get_uint8_raw(struct data_in *din, int *dest)
Receive uint8 value to dest.
Definition: dataio_raw.cpp:492
size_t dio_output_used(struct raw_data_out *dout)
Return the maximum number of bytes used.
Definition: dataio_raw.cpp:157
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.
Definition: dataio_raw.cpp:169
#define _(String)
Definition: fcintl.h:50
#define fc_assert_msg(condition, message,...)
Definition: log.h:96
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
void send_attribute_block(const struct player *pplayer, struct connection *pconn)
Split the attribute block into chunks and send them over pconn.
Definition: packets.cpp:681
Q_GLOBAL_STATIC(QVector< QString >, future_name_translation)
#define ADD_TO_POINTER(p, n)
Definition: shared.h:80
struct connection conn
Definition: client_main.h:89
Definition: player.h:231
QByteArray attribute_block
Definition: player.h:288
bool is_alive
Definition: player.h:250