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.
7
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 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <string.h>
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22
23 /* utility */
24 #include "fcintl.h"
25 #include "log.h"
26 #include "mem.h"
27 #include "shared.h" /* for MIN() */
28 #include "specialist.h"
29 #include "support.h"
30 #include "timing.h"
31
32 /* common */
33 #include "city.h"
34 #include "dataio.h"
35 #include "events.h"
36 #include "game.h"
37 #include "government.h"
38 #include "packets.h"
39 #include "specialist.h"
40
41 /* client */
42 #include "attribute.h"
43 #include "client_main.h"
44 #include "climisc.h"
45 #include "packhand.h"
46
47 /* client/include */
48 #include "chatline_g.h"
49 #include "citydlg_g.h"
50 #include "cityrep_g.h"
51 #include "messagewin_g.h"
52
53 /* client/agents */
54 #include "agents.h"
55
56 #include "cma_core.h"
57
58
59 /*
60 * The CMA is an agent. The CMA will subscribe itself to all city
61 * events. So if a city changes the callback function city_changed is
62 * called. handle_city will be called from city_changed to update the
63 * given city. handle_city will call cma_query_result and
64 * apply_result_on_server to update the server city state.
65 */
66
67 /****************************************************************************
68 defines, structs, globals, forward declarations
69 *****************************************************************************/
70
71 #define log_apply_result log_debug
72 #define log_handle_city log_debug
73 #define log_handle_city2 log_debug
74 #define log_results_are_equal log_debug
75
76 #define SHOW_TIME_STATS FALSE
77 #define SHOW_APPLY_RESULT_ON_SERVER_ERRORS FALSE
78 #define ALWAYS_APPLY_AT_SERVER FALSE
79
80 #define SAVED_PARAMETER_SIZE 29
81
82 #define CMA_ATTR_VERSION 2
83
84 /*
85 * Misc statistic to analyze performance.
86 */
87 static struct {
88 struct timer *wall_timer;
89 int apply_result_ignored, apply_result_applied, refresh_forced;
90 } stats;
91
92
93 /****************************************************************************
94 Returns TRUE iff the two results are equal. Both results have to be
95 results for the given city.
96 *****************************************************************************/
fc_results_are_equal(const struct cm_result * result1,const struct cm_result * result2)97 static bool fc_results_are_equal(const struct cm_result *result1,
98 const struct cm_result *result2)
99 {
100 #define T(x) if (result1->x != result2->x) { \
101 log_results_are_equal(#x); \
102 return FALSE; }
103
104 T(disorder);
105 T(happy);
106
107 specialist_type_iterate(sp) {
108 T(specialists[sp]);
109 } specialist_type_iterate_end;
110
111 output_type_iterate(ot) {
112 T(surplus[ot]);
113 } output_type_iterate_end;
114
115 fc_assert_ret_val(result1->city_radius_sq == result2->city_radius_sq,
116 FALSE);
117 city_map_iterate(result1->city_radius_sq, cindex, x, y) {
118 if (is_free_worked_index(cindex)) {
119 continue;
120 }
121
122 if (result1->worker_positions[cindex]
123 != result2->worker_positions[cindex]) {
124 log_results_are_equal("worker_positions");
125 return FALSE;
126 }
127 } city_map_iterate_end;
128
129 return TRUE;
130
131 #undef T
132 }
133
134
135 /****************************************************************************
136 Returns TRUE if the city is valid for CMA. Fills parameter if TRUE
137 is returned. Parameter can be NULL.
138 *****************************************************************************/
check_city(int city_id,struct cm_parameter * parameter)139 static struct city *check_city(int city_id, struct cm_parameter *parameter)
140 {
141 struct city *pcity = game_city_by_number(city_id);
142 struct cm_parameter dummy;
143
144 if (!parameter) {
145 parameter = &dummy;
146 }
147
148 if (!pcity
149 || !cma_get_parameter(ATTR_CITY_CMA_PARAMETER, city_id, parameter)) {
150 return NULL;
151 }
152
153 if (city_owner(pcity) != client.conn.playing) {
154 cma_release_city(pcity);
155 return NULL;
156 }
157
158 return pcity;
159 }
160
161 /****************************************************************************
162 Change the actual city setting to the given result. Returns TRUE iff
163 the actual data matches the calculated one.
164 *****************************************************************************/
apply_result_on_server(struct city * pcity,const struct cm_result * result)165 static bool apply_result_on_server(struct city *pcity,
166 const struct cm_result *result)
167 {
168 int first_request_id = 0, last_request_id = 0, i;
169 int city_radius_sq = city_map_radius_sq_get(pcity);
170 struct cm_result *current_state = cm_result_new(pcity);;
171 bool success;
172 struct tile *pcenter = city_tile(pcity);
173
174 fc_assert_ret_val(result->found_a_valid, FALSE);
175 cm_result_from_main_map(current_state, pcity);
176
177 if (fc_results_are_equal(current_state, result)
178 && !ALWAYS_APPLY_AT_SERVER) {
179 stats.apply_result_ignored++;
180 return TRUE;
181 }
182
183 /* Do checks */
184 if (city_size_get(pcity) != cm_result_citizens(result)) {
185 log_error("apply_result_on_server(city %d=\"%s\") bad result!",
186 pcity->id, city_name_get(pcity));
187 cm_print_city(pcity);
188 cm_print_result(result);
189 return FALSE;
190 }
191
192 stats.apply_result_applied++;
193
194 log_apply_result("apply_result_on_server(city %d=\"%s\")",
195 pcity->id, city_name_get(pcity));
196
197 connection_do_buffer(&client.conn);
198
199 /* Remove all surplus workers */
200 city_tile_iterate_skip_free_worked(city_radius_sq, pcenter, ptile, idx,
201 x, y) {
202 if (tile_worked(ptile) == pcity
203 && !result->worker_positions[idx]) {
204 log_apply_result("Removing worker at {%d,%d}.", x, y);
205
206 last_request_id =
207 dsend_packet_city_make_specialist(&client.conn, pcity->id, x, y);
208 if (first_request_id == 0) {
209 first_request_id = last_request_id;
210 }
211 }
212 } city_tile_iterate_skip_free_worked_end;
213
214 /* Change the excess non-default specialists to default. */
215 specialist_type_iterate(sp) {
216 if (sp == DEFAULT_SPECIALIST) {
217 continue;
218 }
219
220 for (i = 0; i < pcity->specialists[sp] - result->specialists[sp]; i++) {
221 log_apply_result("Change specialist from %d to %d.",
222 sp, DEFAULT_SPECIALIST);
223 last_request_id = city_change_specialist(pcity,
224 sp, DEFAULT_SPECIALIST);
225 if (first_request_id == 0) {
226 first_request_id = last_request_id;
227 }
228 }
229 } specialist_type_iterate_end;
230
231 /* now all surplus people are DEFAULT_SPECIALIST */
232
233 /* Set workers */
234 /* FIXME: This code assumes that any toggled worker will turn into a
235 * DEFAULT_SPECIALIST! */
236 city_tile_iterate_skip_free_worked(city_radius_sq, pcenter, ptile, idx,
237 x, y) {
238 if (NULL == tile_worked(ptile)
239 && result->worker_positions[idx]) {
240 log_apply_result("Putting worker at {%d,%d}.", x, y);
241 fc_assert_action(city_can_work_tile(pcity, ptile), break);
242
243 last_request_id =
244 dsend_packet_city_make_worker(&client.conn, pcity->id, x, y);
245 if (first_request_id == 0) {
246 first_request_id = last_request_id;
247 }
248 }
249 } city_tile_iterate_skip_free_worked_end;
250
251 /* Set all specialists except DEFAULT_SPECIALIST (all the unchanged
252 * ones remain as DEFAULT_SPECIALIST). */
253 specialist_type_iterate(sp) {
254 if (sp == DEFAULT_SPECIALIST) {
255 continue;
256 }
257
258 for (i = 0; i < result->specialists[sp] - pcity->specialists[sp]; i++) {
259 log_apply_result("Changing specialist from %d to %d.",
260 DEFAULT_SPECIALIST, sp);
261 last_request_id = city_change_specialist(pcity,
262 DEFAULT_SPECIALIST, sp);
263 if (first_request_id == 0) {
264 first_request_id = last_request_id;
265 }
266 }
267 } specialist_type_iterate_end;
268
269 if (last_request_id == 0 || ALWAYS_APPLY_AT_SERVER) {
270 /*
271 * If last_request is 0 no change request was send. But it also
272 * means that the results are different or the fc_results_are_equal()
273 * test at the start of the function would be true. So this
274 * means that the client has other results for the same
275 * allocation of citizen than the server. We just send a
276 * PACKET_CITY_REFRESH to bring them in sync.
277 */
278 first_request_id = last_request_id =
279 dsend_packet_city_refresh(&client.conn, pcity->id);
280 stats.refresh_forced++;
281 }
282
283 connection_do_unbuffer(&client.conn);
284
285 if (last_request_id != 0) {
286 int city_id = pcity->id;
287
288 wait_for_requests("CMA", first_request_id, last_request_id);
289 if (pcity != check_city(city_id, NULL)) {
290 log_verbose("apply_result_on_server(city %d) !check_city()!", city_id);
291 return FALSE;
292 }
293 }
294
295 /* Return. */
296 cm_result_from_main_map(current_state, pcity);
297
298 success = fc_results_are_equal(current_state, result);
299 if (!success) {
300 cm_clear_cache(pcity);
301
302 #if SHOW_APPLY_RESULT_ON_SERVER_ERRORS
303 log_error("apply_result_on_server(city %d=\"%s\") no match!",
304 pcity->id, city_name_get(pcity));
305
306 log_test("apply_result_on_server(city %d=\"%s\") have:",
307 pcity->id, city_name_get(pcity));
308 cm_print_city(pcity);
309 cm_print_result(current_state);
310
311 log_test("apply_result_on_server(city %d=\"%s\") want:",
312 pcity->id, city_name_get(pcity));
313 cm_print_result(result);
314 #endif /* SHOW_APPLY_RESULT_ON_SERVER_ERRORS */
315 }
316
317 cm_result_destroy(current_state);
318
319 log_apply_result("apply_result_on_server() return %d.", (int) success);
320 return success;
321 }
322
323 /****************************************************************************
324 Prints the data of the stats struct via log_test(...).
325 *****************************************************************************/
report_stats(void)326 static void report_stats(void)
327 {
328 #if SHOW_TIME_STATS
329 int total, per_mill;
330
331 total = stats.apply_result_ignored + stats.apply_result_applied;
332 per_mill = (stats.apply_result_ignored * 1000) / (total ? total : 1);
333
334 log_test("CMA: apply_result: ignored=%2d.%d%% (%d) "
335 "applied=%2d.%d%% (%d) total=%d",
336 per_mill / 10, per_mill % 10, stats.apply_result_ignored,
337 (1000 - per_mill) / 10, (1000 - per_mill) % 10,
338 stats.apply_result_applied, total);
339 #endif /* SHOW_TIME_STATS */
340 }
341
342 /****************************************************************************
343 Remove governor setting from city.
344 *****************************************************************************/
release_city(int city_id)345 static void release_city(int city_id)
346 {
347 attr_city_set(ATTR_CITY_CMA_PARAMETER, city_id, 0, NULL);
348 }
349
350 /****************************************************************************
351 algorithmic functions
352 *****************************************************************************/
353
354 /****************************************************************************
355 The given city has changed. handle_city ensures that either the city
356 follows the set CMA goal or that the CMA detaches itself from the
357 city.
358 *****************************************************************************/
handle_city(struct city * pcity)359 static void handle_city(struct city *pcity)
360 {
361 struct cm_result *result = cm_result_new(pcity);
362 bool handled;
363 int i, city_id = pcity->id;
364
365 log_handle_city("handle_city(city %d=\"%s\") pos=(%d,%d) owner=%s",
366 pcity->id, city_name_get(pcity), TILE_XY(pcity->tile),
367 nation_rule_name(nation_of_city(pcity)));
368
369 log_handle_city2("START handle city %d=\"%s\"",
370 pcity->id, city_name_get(pcity));
371
372 handled = FALSE;
373 for (i = 0; i < 5; i++) {
374 struct cm_parameter parameter;
375
376 log_handle_city2(" try %d", i);
377
378 if (pcity != check_city(city_id, ¶meter)) {
379 handled = TRUE;
380 break;
381 }
382
383 cm_query_result(pcity, ¶meter, result, FALSE);
384 if (!result->found_a_valid) {
385 log_handle_city2(" no valid found result");
386
387 cma_release_city(pcity);
388
389 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
390 _("The citizen governor can't fulfill the requirements "
391 "for %s. Passing back control."), city_link(pcity));
392 handled = TRUE;
393 break;
394 } else {
395 if (!apply_result_on_server(pcity, result)) {
396 log_handle_city2(" doesn't cleanly apply");
397 if (pcity == check_city(city_id, NULL) && i == 0) {
398 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
399 _("The citizen governor has gotten confused dealing "
400 "with %s. You may want to have a look."),
401 city_link(pcity));
402 }
403 } else {
404 log_handle_city2(" ok");
405 /* Everything ok */
406 handled = TRUE;
407 break;
408 }
409 }
410 }
411
412 cm_result_destroy(result);
413
414 if (!handled) {
415 fc_assert_ret(pcity == check_city(city_id, NULL));
416 log_handle_city2(" not handled");
417
418 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
419 _("The citizen governor has gotten confused dealing "
420 "with %s. You may want to have a look."),
421 city_link(pcity));
422
423 cma_release_city(pcity);
424
425 log_error("handle_city() CMA: %s has changed multiple times.",
426 city_name_get(pcity));
427 /* TRANS: No full stop after the URL, could cause confusion. */
428 log_error(_("Please report this message at %s"), BUG_URL);
429 }
430
431 log_handle_city2("END handle city=(%d)", city_id);
432 }
433
434 /****************************************************************************
435 Callback for the agent interface.
436 *****************************************************************************/
city_changed(int city_id)437 static void city_changed(int city_id)
438 {
439 struct city *pcity = game_city_by_number(city_id);
440
441 if (pcity) {
442 cm_clear_cache(pcity);
443 handle_city(pcity);
444 }
445 }
446
447 /****************************************************************************
448 Callback for the agent interface.
449 *****************************************************************************/
city_remove(int city_id)450 static void city_remove(int city_id)
451 {
452 release_city(city_id);
453 }
454
455 /****************************************************************************
456 Callback for the agent interface.
457 *****************************************************************************/
new_turn(void)458 static void new_turn(void)
459 {
460 report_stats();
461 }
462
463 /*************************** public interface *******************************/
464 /****************************************************************************
465 Initialize city governor code
466 *****************************************************************************/
cma_init(void)467 void cma_init(void)
468 {
469 struct agent self;
470 struct timer *timer = stats.wall_timer;
471
472 log_debug("sizeof(struct cm_result)=%d",
473 (unsigned int) sizeof(struct cm_result));
474 log_debug("sizeof(struct cm_parameter)=%d",
475 (unsigned int) sizeof(struct cm_parameter));
476
477 /* reset cache counters */
478 memset(&stats, 0, sizeof(stats));
479
480 /* We used to just use timer_new here, but apparently cma_init can be
481 * called multiple times per client invocation so that lead to memory
482 * leaks. */
483 stats.wall_timer = timer_renew(timer, TIMER_USER, TIMER_ACTIVE);
484
485 memset(&self, 0, sizeof(self));
486 strcpy(self.name, "CMA");
487 self.level = 1;
488 self.city_callbacks[CB_CHANGE] = city_changed;
489 self.city_callbacks[CB_NEW] = city_changed;
490 self.city_callbacks[CB_REMOVE] = city_remove;
491 self.turn_start_notify = new_turn;
492 register_agent(&self);
493 }
494
495 /****************************************************************************
496 Apply result on server if it's valid
497 *****************************************************************************/
cma_apply_result(struct city * pcity,const struct cm_result * result)498 bool cma_apply_result(struct city *pcity, const struct cm_result *result)
499 {
500 fc_assert(!cma_is_city_under_agent(pcity, NULL));
501 if (result->found_a_valid) {
502 return apply_result_on_server(pcity, result);
503 } else
504 return TRUE; /* ???????? */
505 }
506
507 /****************************************************************************
508 Put city under governor control
509 *****************************************************************************/
cma_put_city_under_agent(struct city * pcity,const struct cm_parameter * const parameter)510 void cma_put_city_under_agent(struct city *pcity,
511 const struct cm_parameter *const parameter)
512 {
513 log_debug("cma_put_city_under_agent(city %d=\"%s\")",
514 pcity->id, city_name_get(pcity));
515
516 fc_assert_ret(city_owner(pcity) == client.conn.playing);
517
518 cma_set_parameter(ATTR_CITY_CMA_PARAMETER, pcity->id, parameter);
519
520 cause_a_city_changed_for_agent("CMA", pcity);
521
522 log_debug("cma_put_city_under_agent: return");
523 }
524
525 /****************************************************************************
526 Release city from governor control.
527 *****************************************************************************/
cma_release_city(struct city * pcity)528 void cma_release_city(struct city *pcity)
529 {
530 release_city(pcity->id);
531 refresh_city_dialog(pcity);
532 city_report_dialog_update_city(pcity);
533 }
534
535 /****************************************************************************
536 Check whether city is under governor control, and fill parameter if it is.
537 *****************************************************************************/
cma_is_city_under_agent(const struct city * pcity,struct cm_parameter * parameter)538 bool cma_is_city_under_agent(const struct city *pcity,
539 struct cm_parameter *parameter)
540 {
541 struct cm_parameter my_parameter;
542
543 if (!cma_get_parameter(ATTR_CITY_CMA_PARAMETER, pcity->id, &my_parameter)) {
544 return FALSE;
545 }
546
547 if (parameter) {
548 memcpy(parameter, &my_parameter, sizeof(struct cm_parameter));
549 }
550 return TRUE;
551 }
552
553 /**************************************************************************
554 Get the parameter.
555
556 Don't bother to cm_init_parameter, since we set all the fields anyway.
557 But leave the comment here so we can find this place when searching
558 for all the creators of a parameter.
559 **************************************************************************/
cma_get_parameter(enum attr_city attr,int city_id,struct cm_parameter * parameter)560 bool cma_get_parameter(enum attr_city attr, int city_id,
561 struct cm_parameter *parameter)
562 {
563 size_t len;
564 char buffer[SAVED_PARAMETER_SIZE];
565 struct data_in din;
566 int version, dummy;
567
568 /* Changing this function is likely to break compatibility with old
569 * savegames that store these values. */
570
571 len = attr_city_get(attr, city_id, sizeof(buffer), buffer);
572 if (len == 0) {
573 return FALSE;
574 }
575 fc_assert_ret_val(len == SAVED_PARAMETER_SIZE, FALSE);
576
577 dio_input_init(&din, buffer, len);
578
579 dio_get_uint8(&din, &version);
580 if (version != CMA_ATTR_VERSION) {
581 log_error("CMA data has a wrong version %d (expected %d)",
582 version, CMA_ATTR_VERSION);
583 return FALSE;
584 }
585
586 /* Initialize the parameter (includes some AI-only fields that aren't
587 * touched below). */
588 cm_init_parameter(parameter);
589
590 output_type_iterate(i) {
591 dio_get_sint16(&din, ¶meter->minimal_surplus[i]);
592 dio_get_sint16(&din, ¶meter->factor[i]);
593 } output_type_iterate_end;
594
595 dio_get_sint16(&din, ¶meter->happy_factor);
596 dio_get_uint8(&din, &dummy); /* Dummy value; used to be factor_target. */
597 dio_get_bool8(&din, ¶meter->require_happy);
598
599 return TRUE;
600 }
601
602 /**************************************************************************
603 Set attribute block for city from parameter.
604 **************************************************************************/
cma_set_parameter(enum attr_city attr,int city_id,const struct cm_parameter * parameter)605 void cma_set_parameter(enum attr_city attr, int city_id,
606 const struct cm_parameter *parameter)
607 {
608 char buffer[SAVED_PARAMETER_SIZE];
609 struct data_out dout;
610
611 /* Changing this function is likely to break compatibility with old
612 * savegames that store these values. */
613
614 dio_output_init(&dout, buffer, sizeof(buffer));
615
616 dio_put_uint8(&dout, CMA_ATTR_VERSION);
617
618 output_type_iterate(i) {
619 dio_put_sint16(&dout, parameter->minimal_surplus[i]);
620 dio_put_sint16(&dout, parameter->factor[i]);
621 } output_type_iterate_end;
622
623 dio_put_sint16(&dout, parameter->happy_factor);
624 dio_put_uint8(&dout, 0); /* Dummy value; used to be factor_target. */
625 dio_put_bool8(&dout, parameter->require_happy);
626
627 fc_assert(dio_output_used(&dout) == SAVED_PARAMETER_SIZE);
628
629 attr_city_set(attr, city_id, SAVED_PARAMETER_SIZE, buffer);
630 }
631