webperimental: Mountain vision bonus.
[freeciv.git] / client / agents / cma_core.c
blob1b4bda0fe6a6e2f082612614297eb4f41144045e
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 #include <string.h>
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
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"
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"
41 /* client */
42 #include "attribute.h"
43 #include "client_main.h"
44 #include "climisc.h"
45 #include "packhand.h"
47 /* client/include */
48 #include "chatline_g.h"
49 #include "citydlg_g.h"
50 #include "cityrep_g.h"
51 #include "messagewin_g.h"
53 /* client/agents */
54 #include "agents.h"
56 #include "cma_core.h"
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.
67 /****************************************************************************
68 defines, structs, globals, forward declarations
69 *****************************************************************************/
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
76 #define SHOW_TIME_STATS FALSE
77 #define SHOW_APPLY_RESULT_ON_SERVER_ERRORS FALSE
78 #define ALWAYS_APPLY_AT_SERVER FALSE
80 #define SAVED_PARAMETER_SIZE 29
83 * Misc statistic to analyze performance.
85 static struct {
86 struct timer *wall_timer;
87 int apply_result_ignored, apply_result_applied, refresh_forced;
88 } stats;
91 /****************************************************************************
92 Returns TRUE iff the two results are equal. Both results have to be
93 results for the given city.
94 *****************************************************************************/
95 static bool fc_results_are_equal(const struct cm_result *result1,
96 const struct cm_result *result2)
98 #define T(x) if (result1->x != result2->x) { \
99 log_results_are_equal(#x); \
100 return FALSE; }
102 T(disorder);
103 T(happy);
105 specialist_type_iterate(sp) {
106 T(specialists[sp]);
107 } specialist_type_iterate_end;
109 output_type_iterate(ot) {
110 T(surplus[ot]);
111 } output_type_iterate_end;
113 fc_assert_ret_val(result1->city_radius_sq == result2->city_radius_sq,
114 FALSE);
115 city_map_iterate(result1->city_radius_sq, cindex, x, y) {
116 if (is_free_worked_index(cindex)) {
117 continue;
120 if (result1->worker_positions[cindex]
121 != result2->worker_positions[cindex]) {
122 log_results_are_equal("worker_positions");
123 return FALSE;
125 } city_map_iterate_end;
127 return TRUE;
129 #undef T
133 /****************************************************************************
134 Returns TRUE if the city is valid for CMA. Fills parameter if TRUE
135 is returned. Parameter can be NULL.
136 *****************************************************************************/
137 static struct city *check_city(int city_id, struct cm_parameter *parameter)
139 struct city *pcity = game_city_by_number(city_id);
140 struct cm_parameter dummy;
142 if (!parameter) {
143 parameter = &dummy;
146 if (!pcity
147 || !cma_get_parameter(ATTR_CITY_CMA_PARAMETER, city_id, parameter)) {
148 return NULL;
151 if (city_owner(pcity) != client.conn.playing) {
152 cma_release_city(pcity);
153 return NULL;
156 return pcity;
159 /****************************************************************************
160 Change the actual city setting to the given result. Returns TRUE iff
161 the actual data matches the calculated one.
162 *****************************************************************************/
163 static bool apply_result_on_server(struct city *pcity,
164 const struct cm_result *result)
166 int first_request_id = 0, last_request_id = 0, i;
167 int city_radius_sq = city_map_radius_sq_get(pcity);
168 struct cm_result *current_state = cm_result_new(pcity);;
169 bool success;
170 struct tile *pcenter = city_tile(pcity);
172 fc_assert_ret_val(result->found_a_valid, FALSE);
173 cm_result_from_main_map(current_state, pcity);
175 if (fc_results_are_equal(current_state, result)
176 && !ALWAYS_APPLY_AT_SERVER) {
177 stats.apply_result_ignored++;
178 return TRUE;
181 /* Do checks */
182 if (city_size_get(pcity) != cm_result_citizens(result)) {
183 log_error("apply_result_on_server(city %d=\"%s\") bad result!",
184 pcity->id, city_name_get(pcity));
185 cm_print_city(pcity);
186 cm_print_result(result);
187 return FALSE;
190 stats.apply_result_applied++;
192 log_apply_result("apply_result_on_server(city %d=\"%s\")",
193 pcity->id, city_name_get(pcity));
195 connection_do_buffer(&client.conn);
197 /* Remove all surplus workers */
198 city_tile_iterate_skip_free_worked(city_radius_sq, pcenter, ptile, idx,
199 x, y) {
200 if (tile_worked(ptile) == pcity
201 && !result->worker_positions[idx]) {
202 log_apply_result("Removing worker at {%d,%d}.", x, y);
204 last_request_id =
205 dsend_packet_city_make_specialist(&client.conn, pcity->id, x, y);
206 if (first_request_id == 0) {
207 first_request_id = last_request_id;
210 } city_tile_iterate_skip_free_worked_end;
212 /* Change the excess non-default specialists to default. */
213 specialist_type_iterate(sp) {
214 if (sp == DEFAULT_SPECIALIST) {
215 continue;
218 for (i = 0; i < pcity->specialists[sp] - result->specialists[sp]; i++) {
219 log_apply_result("Change specialist from %d to %d.",
220 sp, DEFAULT_SPECIALIST);
221 last_request_id = city_change_specialist(pcity,
222 sp, DEFAULT_SPECIALIST);
223 if (first_request_id == 0) {
224 first_request_id = last_request_id;
227 } specialist_type_iterate_end;
229 /* now all surplus people are DEFAULT_SPECIALIST */
231 /* Set workers */
232 /* FIXME: This code assumes that any toggled worker will turn into a
233 * DEFAULT_SPECIALIST! */
234 city_tile_iterate_skip_free_worked(city_radius_sq, pcenter, ptile, idx,
235 x, y) {
236 if (NULL == tile_worked(ptile)
237 && result->worker_positions[idx]) {
238 log_apply_result("Putting worker at {%d,%d}.", x, y);
239 fc_assert_action(city_can_work_tile(pcity, ptile), break);
241 last_request_id =
242 dsend_packet_city_make_worker(&client.conn, pcity->id, x, y);
243 if (first_request_id == 0) {
244 first_request_id = last_request_id;
247 } city_tile_iterate_skip_free_worked_end;
249 /* Set all specialists except DEFAULT_SPECIALIST (all the unchanged
250 * ones remain as DEFAULT_SPECIALIST). */
251 specialist_type_iterate(sp) {
252 if (sp == DEFAULT_SPECIALIST) {
253 continue;
256 for (i = 0; i < result->specialists[sp] - pcity->specialists[sp]; i++) {
257 log_apply_result("Changing specialist from %d to %d.",
258 DEFAULT_SPECIALIST, sp);
259 last_request_id = city_change_specialist(pcity,
260 DEFAULT_SPECIALIST, sp);
261 if (first_request_id == 0) {
262 first_request_id = last_request_id;
265 } specialist_type_iterate_end;
267 if (last_request_id == 0 || ALWAYS_APPLY_AT_SERVER) {
269 * If last_request is 0 no change request was send. But it also
270 * means that the results are different or the fc_results_are_equal()
271 * test at the start of the function would be true. So this
272 * means that the client has other results for the same
273 * allocation of citizen than the server. We just send a
274 * PACKET_CITY_REFRESH to bring them in sync.
276 first_request_id = last_request_id =
277 dsend_packet_city_refresh(&client.conn, pcity->id);
278 stats.refresh_forced++;
281 connection_do_unbuffer(&client.conn);
283 if (last_request_id != 0) {
284 int city_id = pcity->id;
286 wait_for_requests("CMA", first_request_id, last_request_id);
287 if (pcity != check_city(city_id, NULL)) {
288 log_verbose("apply_result_on_server(city %d) !check_city()!", city_id);
289 return FALSE;
293 /* Return. */
294 cm_result_from_main_map(current_state, pcity);
296 success = fc_results_are_equal(current_state, result);
297 if (!success) {
298 cm_clear_cache(pcity);
300 #if SHOW_APPLY_RESULT_ON_SERVER_ERRORS
301 log_error("apply_result_on_server(city %d=\"%s\") no match!",
302 pcity->id, city_name_get(pcity));
304 log_test("apply_result_on_server(city %d=\"%s\") have:",
305 pcity->id, city_name_get(pcity));
306 cm_print_city(pcity);
307 cm_print_result(current_state);
309 log_test("apply_result_on_server(city %d=\"%s\") want:",
310 pcity->id, city_name_get(pcity));
311 cm_print_result(result);
312 #endif /* SHOW_APPLY_RESULT_ON_SERVER_ERRORS */
315 cm_result_destroy(current_state);
317 log_apply_result("apply_result_on_server() return %d.", (int) success);
318 return success;
321 /****************************************************************************
322 Prints the data of the stats struct via log_test(...).
323 *****************************************************************************/
324 static void report_stats(void)
326 #if SHOW_TIME_STATS
327 int total, per_mill;
329 total = stats.apply_result_ignored + stats.apply_result_applied;
330 per_mill = (stats.apply_result_ignored * 1000) / (total ? total : 1);
332 log_test("CMA: apply_result: ignored=%2d.%d%% (%d) "
333 "applied=%2d.%d%% (%d) total=%d",
334 per_mill / 10, per_mill % 10, stats.apply_result_ignored,
335 (1000 - per_mill) / 10, (1000 - per_mill) % 10,
336 stats.apply_result_applied, total);
337 #endif /* SHOW_TIME_STATS */
340 /****************************************************************************
341 Remove governor setting from city.
342 *****************************************************************************/
343 static void release_city(int city_id)
345 attr_city_set(ATTR_CITY_CMA_PARAMETER, city_id, 0, NULL);
348 /****************************************************************************
349 algorithmic functions
350 *****************************************************************************/
352 /****************************************************************************
353 The given city has changed. handle_city ensures that either the city
354 follows the set CMA goal or that the CMA detaches itself from the
355 city.
356 *****************************************************************************/
357 static void handle_city(struct city *pcity)
359 struct cm_result *result = cm_result_new(pcity);
360 bool handled;
361 int i, city_id = pcity->id;
363 log_handle_city("handle_city(city %d=\"%s\") pos=(%d,%d) owner=%s",
364 pcity->id, city_name_get(pcity), TILE_XY(pcity->tile),
365 nation_rule_name(nation_of_city(pcity)));
367 log_handle_city2("START handle city %d=\"%s\"",
368 pcity->id, city_name_get(pcity));
370 handled = FALSE;
371 for (i = 0; i < 5; i++) {
372 struct cm_parameter parameter;
374 log_handle_city2(" try %d", i);
376 if (pcity != check_city(city_id, &parameter)) {
377 handled = TRUE;
378 break;
381 cm_query_result(pcity, &parameter, result);
382 if (!result->found_a_valid) {
383 log_handle_city2(" no valid found result");
385 cma_release_city(pcity);
387 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
388 _("The citizen governor can't fulfill the requirements "
389 "for %s. Passing back control."), city_link(pcity));
390 handled = TRUE;
391 break;
392 } else {
393 if (!apply_result_on_server(pcity, result)) {
394 log_handle_city2(" doesn't cleanly apply");
395 if (pcity == check_city(city_id, NULL) && i == 0) {
396 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
397 _("The citizen governor has gotten confused dealing "
398 "with %s. You may want to have a look."),
399 city_link(pcity));
401 } else {
402 log_handle_city2(" ok");
403 /* Everything ok */
404 handled = TRUE;
405 break;
410 cm_result_destroy(result);
412 if (!handled) {
413 fc_assert_ret(pcity == check_city(city_id, NULL));
414 log_handle_city2(" not handled");
416 create_event(city_tile(pcity), E_CITY_CMA_RELEASE, ftc_client,
417 _("The citizen governor has gotten confused dealing "
418 "with %s. You may want to have a look."),
419 city_link(pcity));
421 cma_release_city(pcity);
423 log_error("handle_city() CMA: %s has changed multiple times.",
424 city_name_get(pcity));
425 /* TRANS: No full stop after the URL, could cause confusion. */
426 log_error(_("Please report this message at %s"), BUG_URL);
429 log_handle_city2("END handle city=(%d)", city_id);
432 /****************************************************************************
433 Callback for the agent interface.
434 *****************************************************************************/
435 static void city_changed(int city_id)
437 struct city *pcity = game_city_by_number(city_id);
439 if (pcity) {
440 cm_clear_cache(pcity);
441 handle_city(pcity);
445 /****************************************************************************
446 Callback for the agent interface.
447 *****************************************************************************/
448 static void city_remove(int city_id)
450 release_city(city_id);
453 /****************************************************************************
454 Callback for the agent interface.
455 *****************************************************************************/
456 static void new_turn(void)
458 report_stats();
461 /*************************** public interface *******************************/
462 /****************************************************************************
463 Initialize city governor code
464 *****************************************************************************/
465 void cma_init(void)
467 struct agent self;
468 struct timer *timer = stats.wall_timer;
470 log_debug("sizeof(struct cm_result)=%d",
471 (unsigned int) sizeof(struct cm_result));
472 log_debug("sizeof(struct cm_parameter)=%d",
473 (unsigned int) sizeof(struct cm_parameter));
475 /* reset cache counters */
476 memset(&stats, 0, sizeof(stats));
478 /* We used to just use timer_new here, but apparently cma_init can be
479 * called multiple times per client invocation so that lead to memory
480 * leaks. */
481 stats.wall_timer = timer_renew(timer, TIMER_USER, TIMER_ACTIVE);
483 memset(&self, 0, sizeof(self));
484 strcpy(self.name, "CMA");
485 self.level = 1;
486 self.city_callbacks[CB_CHANGE] = city_changed;
487 self.city_callbacks[CB_NEW] = city_changed;
488 self.city_callbacks[CB_REMOVE] = city_remove;
489 self.turn_start_notify = new_turn;
490 register_agent(&self);
493 /****************************************************************************
494 Apply result on server if it's valid
495 *****************************************************************************/
496 bool cma_apply_result(struct city *pcity, const struct cm_result *result)
498 fc_assert(!cma_is_city_under_agent(pcity, NULL));
499 if (result->found_a_valid) {
500 return apply_result_on_server(pcity, result);
501 } else
502 return TRUE; /* ???????? */
505 /****************************************************************************
506 Put city under governor control
507 *****************************************************************************/
508 void cma_put_city_under_agent(struct city *pcity,
509 const struct cm_parameter *const parameter)
511 log_debug("cma_put_city_under_agent(city %d=\"%s\")",
512 pcity->id, city_name_get(pcity));
514 fc_assert_ret(city_owner(pcity) == client.conn.playing);
516 cma_set_parameter(ATTR_CITY_CMA_PARAMETER, pcity->id, parameter);
518 cause_a_city_changed_for_agent("CMA", pcity);
520 log_debug("cma_put_city_under_agent: return");
523 /****************************************************************************
524 Release city from governor control.
525 *****************************************************************************/
526 void cma_release_city(struct city *pcity)
528 release_city(pcity->id);
529 refresh_city_dialog(pcity);
530 city_report_dialog_update_city(pcity);
533 /****************************************************************************
534 Check whether city is under governor control, and fill parameter if it is.
535 *****************************************************************************/
536 bool cma_is_city_under_agent(const struct city *pcity,
537 struct cm_parameter *parameter)
539 struct cm_parameter my_parameter;
541 if (!cma_get_parameter(ATTR_CITY_CMA_PARAMETER, pcity->id, &my_parameter)) {
542 return FALSE;
545 if (parameter) {
546 memcpy(parameter, &my_parameter, sizeof(struct cm_parameter));
548 return TRUE;
551 /**************************************************************************
552 Get the parameter.
554 Don't bother to cm_init_parameter, since we set all the fields anyway.
555 But leave the comment here so we can find this place when searching
556 for all the creators of a parameter.
557 **************************************************************************/
558 bool cma_get_parameter(enum attr_city attr, int city_id,
559 struct cm_parameter *parameter)
561 size_t len;
562 char buffer[SAVED_PARAMETER_SIZE];
563 struct data_in din;
564 int version, dummy;
566 /* Changing this function is likely to break compatability with old
567 * savegames that store these values. */
569 len = attr_city_get(attr, city_id, sizeof(buffer), buffer);
570 if (len == 0) {
571 return FALSE;
573 fc_assert_ret_val(len == SAVED_PARAMETER_SIZE, FALSE);
575 dio_input_init(&din, buffer, len);
577 dio_get_uint8_raw(&din, &version);
578 fc_assert_ret_val(version == 2, FALSE);
580 /* Initialize the parameter (includes some AI-only fields that aren't
581 * touched below). */
582 cm_init_parameter(parameter);
584 output_type_iterate(i) {
585 dio_get_sint16_raw(&din, &parameter->minimal_surplus[i]);
586 dio_get_sint16_raw(&din, &parameter->factor[i]);
587 } output_type_iterate_end;
589 dio_get_sint16_raw(&din, &parameter->happy_factor);
590 dio_get_uint8_raw(&din, &dummy); /* Dummy value; used to be factor_target. */
591 dio_get_bool8_raw(&din, &parameter->require_happy);
593 return TRUE;
596 /**************************************************************************
597 Set attribute block for city from parameter.
598 **************************************************************************/
599 void cma_set_parameter(enum attr_city attr, int city_id,
600 const struct cm_parameter *parameter)
602 char buffer[SAVED_PARAMETER_SIZE];
603 struct raw_data_out dout;
605 /* Changing this function is likely to break compatability with old
606 * savegames that store these values. */
608 dio_output_init(&dout, buffer, sizeof(buffer));
610 dio_put_uint8_raw(&dout, 2);
612 output_type_iterate(i) {
613 dio_put_sint16_raw(&dout, parameter->minimal_surplus[i]);
614 dio_put_sint16_raw(&dout, parameter->factor[i]);
615 } output_type_iterate_end;
617 dio_put_sint16_raw(&dout, parameter->happy_factor);
618 dio_put_uint8_raw(&dout, 0); /* Dummy value; used to be factor_target. */
619 dio_put_bool8_raw(&dout, parameter->require_happy);
621 fc_assert(dio_output_used(&dout) == SAVED_PARAMETER_SIZE);
623 attr_city_set(attr, city_id, SAVED_PARAMETER_SIZE, buffer);