Freeciv21
Develop your civilization from humble roots to a global empire
update_queue.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2023 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 // Qt
15 #include <QUrl>
16 
17 // utility
18 #include "log.h"
19 #include "support.h" // bool
20 
21 // common
22 #include "city.h"
23 #include "player.h"
24 
25 /* client/include */
26 #include "citydlg_g.h"
27 #include "cityrep_g.h"
28 #include "dialogs_g.h"
29 #include "gui_main_g.h"
30 #include "menu_g.h"
31 #include "pages_g.h"
32 #include "ratesdlg_g.h"
33 #include "repodlgs_g.h"
34 
35 // client
36 #include "canvas.h"
37 #include "client_main.h"
38 #include "connectdlg_common.h"
39 #include "qtg_cxxside.h"
40 #include "tileset/tilespec.h"
41 #include "update_queue.h"
43 #include "views/view_units.h"
44 
46 
47 // returns instance of queue
49 {
50  if (!m_instance)
52  return m_instance;
53 }
54 
56 {
57  delete m_instance;
58  m_instance = nullptr;
59 }
60 
61 // Extract the update_queue_data from the waiting queue data.
62 struct update_queue_data *
64 {
65  struct update_queue_data *uq_data = wq_data->uq_data;
66  wq_data->uq_data = nullptr;
67  return uq_data;
68 }
69 
70 // Create a new waiting queue data.
71 struct waiting_queue_data *
73  uq_free_fn_t free_data_func)
74 {
75  struct waiting_queue_data *wq_data = new waiting_queue_data;
76  wq_data->callback = callback;
77  wq_data->uq_data = data_new(data, free_data_func);
78  return wq_data;
79 }
80 
81 // Moves the instances waiting to the request_id to the callback queue.
82 void update_queue::wq_run_requests(waitingQueue &hash, int request_id)
83 {
84  waitq_list *list;
85  if (!hash.contains(request_id)) {
86  return;
87  }
88  list = hash.value(request_id);
89  for (auto wq_data : qAsConst(*list)) {
90  push(wq_data->callback, wq_data_extract(wq_data));
91  }
92  hash.remove(request_id);
93 }
94 
95 // Free a waiting queue data.
97 {
98  fc_assert_ret(nullptr != wq_data);
99  if (nullptr != wq_data->uq_data) {
100  // May be nullptr, see waiting_queue_data_extract().
101  data_destroy(wq_data->uq_data);
102  }
103  delete wq_data;
104 }
105 
106 // Connects the callback to a network event.
107 void update_queue::wq_add_request(waitingQueue &hash, int request_id,
108  uq_callback_t callback, void *data,
109  uq_free_fn_t free_data_func)
110 {
111  waitq_list *wqlist;
112  if (!hash.contains(request_id)) {
113  waitq_list *wqlist = new waitq_list;
114  hash.insert(request_id, wqlist);
115  }
116  wqlist = hash.value(request_id);
117  wqlist->append(wq_data_new(callback, data, free_data_func));
118 }
119 
120 // clears content
122 {
123  while (!queue.isEmpty()) {
124  updatePair pair = queue.dequeue();
125  data_destroy(pair.second);
126  }
127 
128  for (auto a : qAsConst(wq_processing_finished)) {
129  for (auto data : qAsConst(*a)) {
130  wq_data_destroy(data);
131  }
132  }
133  wq_processing_finished.clear();
134 }
135 
137 
138 // Create a new update queue data.
141 {
142  struct update_queue_data *uq_data = new update_queue_data();
143 
144  uq_data->data = data;
145  uq_data->free_data_func = free_data_func;
146  return uq_data;
147 }
148 
149 // Free a update queue data.
151 {
152  fc_assert_ret(nullptr != uq_data);
153  if (nullptr != uq_data->free_data_func) {
154  uq_data->free_data_func(uq_data->data);
155  }
156  delete uq_data;
157 }
158 
159 // Moves the instances waiting to the request_id to the callback queue.
161 {
163 }
164 
165 // Unqueue all updates.
167 {
168  updatePair pair;
169 
170  has_idle_cb = false;
171 
172  // Invoke callbacks.
173  while (!queue.isEmpty()) {
174  pair = queue.dequeue();
175  auto callback = pair.first;
176  auto uq_data = pair.second;
177  callback(uq_data->data);
178  data_destroy(uq_data);
179  }
180 }
181 
182 // Add a callback to the update queue. NB: you can only set a callback
183 // once. Setting a callback twice will put new callback at end.
185  struct update_queue_data *uq_data)
186 {
187  struct update_queue_data *uqr_data = nullptr;
188  for (auto p : qAsConst(queue)) {
189  if (p.first == callback)
190  uqr_data = p.second;
191  }
192  auto pr = qMakePair(callback, uqr_data);
193  queue.removeAll(pr);
194  if (uqr_data) {
195  data_destroy(uqr_data);
196  }
197  queue.enqueue(qMakePair(callback, uq_data));
198 
199  if (!has_idle_cb) {
200  has_idle_cb = true;
201  update_unqueue();
202  }
203 }
204 
205 // Add a callback to the update queue. NB: you can only set a callback
206 // once. Setting a callback twice will overwrite the previous.
207 void update_queue::add(uq_callback_t callback, void *data)
208 {
209  push(callback, data_new(data, nullptr));
210 }
211 
212 // Returns whether this callback is listed in the update queue.
214 {
215  for (auto pair : qAsConst(queue)) {
216  if (pair.first == callback)
217  return true;
218  }
219  return false;
220 }
221 
223  const void **data,
225 {
226  struct update_queue_data *uq_data = nullptr;
227  for (auto p : qAsConst(queue)) {
228  if (p.first == callback)
229  uq_data = p.second;
230  }
231  if (uq_data) {
232  if (nullptr != data) {
233  *data = uq_data->data;
234  }
235  if (nullptr != free_data_func) {
236  *free_data_func = uq_data->free_data_func;
237  }
238  return true;
239  }
240  return false;
241 }
242 
243 // Connects the callback to the end of the processing (in server side) of
244 // the request.
246  uq_callback_t callback,
247  void *data)
248 {
249  wq_add_request(wq_processing_finished, request_id, callback, data,
250  nullptr);
251 }
252 
258  uq_callback_t callback,
259  void *data)
260 {
261  if (wq_processing_finished.contains(request_id)) {
262  for (const auto &d : *wq_processing_finished[request_id]) {
263  if (d->callback == callback && d->uq_data->data == data) {
264  // Already present
265  return;
266  }
267  }
268  }
269  wq_add_request(wq_processing_finished, request_id, callback, data,
270  nullptr);
271 }
272 
273 // Connects the callback to the end of the processing (in server side) of
274 // the request.
276  int request_id, uq_callback_t callback, void *data,
278 {
279  wq_add_request(wq_processing_finished, request_id, callback, data,
281 }
282 
283 // Set the client page.
284 static void set_client_page_callback(void *data)
285 {
286  enum client_pages page = static_cast<client_pages>(FC_PTR_TO_INT(data));
287 
288  real_set_client_page(page);
289 }
290 
291 // Set the client page.
292 void set_client_page(enum client_pages page)
293 {
294  log_debug("Requested page: %s.", client_pages_name(page));
295 
297 }
298 
299 // Start server and then, set the client page.
300 void client_start_server_and_set_page(enum client_pages page)
301 {
302  log_debug("Requested server start + page: %s.", client_pages_name(page));
303 
304  if (client_start_server(client_url().userName())) {
306  client.conn.client.last_request_id_used, set_client_page_callback,
307  FC_INT_TO_PTR(page));
308  }
309 }
310 
311 // Returns the next client page.
312 enum client_pages get_client_page(void)
313 {
314  const void *data;
315 
316  if (update_queue::uq()->has_callback_full(set_client_page_callback, &data,
317  nullptr)) {
318  return static_cast<client_pages>(FC_PTR_TO_INT(data));
319  } else {
320  return get_current_client_page();
321  }
322 }
323 
324 // Returns whether there's page switching already in progress.
326 {
328 }
329 
330 // Request the menus to be initialized and updated.
331 void menus_init(void)
332 {
334  [](void *) {
335  real_menus_init();
337  },
338  nullptr);
339 }
340 
341 // Update the menus.
342 static void menus_update_callback(void *) { real_menus_update(); }
343 
344 // Request the menus to be updated.
345 void menus_update(void)
346 {
347  if (!update_queue::uq()->has_callback(menus_update_callback)) {
349  }
350 }
351 
352 // Update multipliers/policy dialog.
354 {
356 }
357 
358 // Update cities gui.
359 static void cities_update_callback(void *data)
360 {
361 #ifdef FREECIV_DEBUG
362 #define NEED_UPDATE(city_update, action) \
363  if (city_update & need_update) { \
364  action; \
365  need_update = static_cast<city_updates>(need_update & ~city_update); \
366  }
367 #else // FREECIV_DEBUG
368 #define NEED_UPDATE(city_update, action) \
369  if (city_update & need_update) { \
370  action; \
371  }
372 #endif // FREECIV_DEBUG
373 
374  cities_iterate(pcity)
375  {
376  enum city_updates need_update = pcity->client.need_updates;
377 
378  if (CU_NO_UPDATE == need_update) {
379  continue;
380  }
381 
382  // Clear all updates.
383  pcity->client.need_updates = CU_NO_UPDATE;
384 
388 
389 #ifdef FREECIV_DEBUG
390  if (CU_NO_UPDATE != need_update) {
391  qCritical("Some city updates not handled "
392  "for city %s (id %d): %d left.",
393  city_name_get(pcity), pcity->id, need_update);
394  }
395 #endif // FREECIV_DEBUG
396  }
398 #undef NEED_UPDATE
399 }
400 
401 // Request the city dialog to be popped up for the city.
402 void popup_city_dialog(struct city *pcity)
403 {
404  pcity->client.need_updates =
405  static_cast<city_updates>(static_cast<int>(pcity->client.need_updates)
406  | static_cast<int>(CU_POPUP_DIALOG));
408 }
409 
410 // Request the city dialog to be updated for the city.
411 void refresh_city_dialog(struct city *pcity)
412 {
413  pcity->client.need_updates =
414  static_cast<city_updates>(static_cast<int>(pcity->client.need_updates)
415  | static_cast<int>(CU_UPDATE_DIALOG));
417 }
418 
419 // Request the city to be updated in the city report.
421 {
422  pcity->client.need_updates =
423  static_cast<city_updates>(static_cast<int>(pcity->client.need_updates)
424  | static_cast<int>(CU_UPDATE_REPORT));
426 }
427 
428 // Update the connection list in the start page.
430 {
432 }
433 
434 // Update the nation report.
436 {
438 }
439 
440 // Update the city report.
442 {
444 }
445 
446 // Update the science report.
448 {
450 }
451 
452 // Update the economy report.
454 {
456 }
457 
458 // Update the units report.
460 {
462 }
463 
464 // Update the units report.
466 {
468 }
const char * city_name_get(const struct city *pcity)
Return the name of the city.
Definition: city.cpp:1077
#define cities_iterate_end
Definition: city.h:493
#define cities_iterate(pcity)
Definition: city.h:486
city_updates
Definition: city.h:270
@ CU_POPUP_DIALOG
Definition: city.h:274
@ CU_UPDATE_REPORT
Definition: city.h:272
@ CU_UPDATE_DIALOG
Definition: city.h:273
@ CU_NO_UPDATE
Definition: city.h:271
void real_city_dialog_popup(struct city *pcity)
Pop up (or bring to the front) a dialog for the given city.
Definition: citydlg.cpp:2247
void real_city_dialog_refresh(struct city *pcity)
Refresh (update) all data for the given city's dialog.
Definition: citydlg.cpp:2274
void real_city_report_dialog_update(void *unused)
Update (refresh) the entire city report dialog.
void real_city_report_update_city(struct city *pcity)
Update the information for a single city in the city report.
bool has_callback_full(uq_callback_t cb, const void **data, uq_free_fn_t *free_fn)
waitingQueue wq_processing_finished
Definition: update_queue.h:42
static update_queue * uq()
void connect_processing_finished_unique(int request_id, uq_callback_t cb, void *data)
Connects the callback to the end of the processing (in server side) of the request.
void wq_data_destroy(struct waiting_queue_data *wq_data)
void wq_run_requests(waitingQueue &hash, int request_id)
void update_unqueue()
bool has_idle_cb
Definition: update_queue.h:43
static void drop()
static update_queue * m_instance
Definition: update_queue.h:40
struct waiting_queue_data * wq_data_new(uq_callback_t callback, void *data, uq_free_fn_t free_fn)
struct update_queue_data * wq_data_extract(struct waiting_queue_data *wq_data)
bool has_callback(uq_callback_t callback)
void connect_processing_finished_full(int request_id, uq_callback_t cb, void *data, uq_free_fn_t free_func)
void data_destroy(struct update_queue_data *dt)
void connect_processing_finished(int request_id, uq_callback_t cb, void *data)
update_queue()=default
void push(uq_callback_t cb, struct update_queue_data *dt)
void wq_add_request(waitingQueue &hash, int request_id, uq_callback_t cb, void *data, uq_free_fn_t free_fn)
QQueue< updatePair > queue
Definition: update_queue.h:41
struct update_queue_data * data_new(void *data, uq_free_fn_t free_fn)
void add(uq_callback_t callback, void *data)
void processing_finished(int request_id)
QUrl & client_url()
Returns the URL that this client connects to.
struct civclient client
bool client_start_server(const QString &user_name)
Forks a server if it can.
void unit_select_dialog_update_real(void *unused)
Update the dialog window to select units on a particular tile.
Definition: dialogs.cpp:1034
enum client_pages get_current_client_page()
Returns current client page.
Definition: fc_client.cpp:589
void real_set_client_page(enum client_pages page)
Sets the "page" that the client should show.
Definition: fc_client.cpp:570
void real_science_report_dialog_update(void *)
Update the science report.
void real_conn_list_dialog_update(void *unused)
Update the connected users list at pregame state.
Definition: gui_main.cpp:121
#define fc_assert_ret(condition)
Definition: log.h:112
#define log_debug(message,...)
Definition: log.h:65
void real_menus_init(void)
Initialize menus (sensitivity, name, etc.) based on the current state and current ruleset,...
Definition: menu.cpp:83
void real_menus_update(void)
Update all of the menus (sensitivity, name, etc.) based on the current state.
Definition: menu.cpp:101
void real_multipliers_dialog_update(void *unused)
Update multipliers (policies) dialog.
Definition: ratesdlg.cpp:253
void real_economy_report_dialog_update(void *unused)
Update the economy report.
#define FC_PTR_TO_INT(p)
Definition: shared.h:89
#define FC_INT_TO_PTR(i)
Definition: shared.h:88
Definition: city.h:291
struct city::@15::@18 client
struct connection conn
Definition: client_main.h:89
struct connection::@55::@60 client
uq_free_fn_t free_data_func
Definition: update_queue.h:25
uq_callback_t callback
Definition: update_queue.h:31
struct update_queue_data * uq_data
Definition: update_queue.h:32
bool update_queue_is_switching_page(void)
void client_start_server_and_set_page(enum client_pages page)
void conn_list_dialog_update(void)
void economy_report_dialog_update(void)
void set_client_page(enum client_pages page)
void multipliers_dialog_update(void)
void city_report_dialog_update_city(struct city *pcity)
void science_report_dialog_update(void)
void popup_city_dialog(struct city *pcity)
static void set_client_page_callback(void *data)
static void cities_update_callback(void *data)
void refresh_city_dialog(struct city *pcity)
void units_report_dialog_update(void)
void players_dialog_update(void)
#define NEED_UPDATE(city_update, action)
void menus_update(void)
void city_report_dialog_update(void)
void menus_init(void)
enum client_pages get_client_page(void)
void unit_select_dialog_update(void)
static void menus_update_callback(void *)
void(* uq_free_fn_t)(void *data)
Definition: update_queue.h:20
void(* uq_callback_t)(void *data)
Definition: update_queue.h:19
QList< struct waiting_queue_data * > waitq_list
Definition: update_queue.h:35
QPair< uq_callback_t, struct update_queue_data * > updatePair
Definition: update_queue.h:36
QHash< int, waitq_list * > waitingQueue
Definition: update_queue.h:37
void real_players_dialog_update(void *unused)
Update all information in the player list dialog.
void units_view_dialog_update(void *unused)
Update the units view.
Definition: view_units.cpp:758