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)
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 ***********************************************************************/
15 #include <fc_config.h>
27 #include "shared.h" /* for MIN() */
28 #include "specialist.h"
37 #include "government.h"
39 #include "specialist.h"
42 #include "attribute.h"
43 #include "client_main.h"
48 #include "chatline_g.h"
49 #include "citydlg_g.h"
50 #include "cityrep_g.h"
51 #include "messagewin_g.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.
86 struct timer
*wall_timer
;
87 int apply_result_ignored
, apply_result_applied
, refresh_forced
;
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); \
105 specialist_type_iterate(sp
) {
107 } specialist_type_iterate_end
;
109 output_type_iterate(ot
) {
111 } output_type_iterate_end
;
113 fc_assert_ret_val(result1
->city_radius_sq
== result2
->city_radius_sq
,
115 city_map_iterate(result1
->city_radius_sq
, cindex
, x
, y
) {
116 if (is_free_worked_index(cindex
)) {
120 if (result1
->worker_positions
[cindex
]
121 != result2
->worker_positions
[cindex
]) {
122 log_results_are_equal("worker_positions");
125 } city_map_iterate_end
;
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
;
147 || !cma_get_parameter(ATTR_CITY_CMA_PARAMETER
, city_id
, parameter
)) {
151 if (city_owner(pcity
) != client
.conn
.playing
) {
152 cma_release_city(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
);;
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
++;
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
);
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
,
200 if (tile_worked(ptile
) == pcity
201 && !result
->worker_positions
[idx
]) {
202 log_apply_result("Removing worker at {%d,%d}.", x
, y
);
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
) {
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 */
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
,
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);
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
) {
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
);
294 cm_result_from_main_map(current_state
, pcity
);
296 success
= fc_results_are_equal(current_state
, result
);
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
);
321 /****************************************************************************
322 Prints the data of the stats struct via log_test(...).
323 *****************************************************************************/
324 static void report_stats(void)
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
356 *****************************************************************************/
357 static void handle_city(struct city
*pcity
)
359 struct cm_result
*result
= cm_result_new(pcity
);
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
));
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
, ¶meter
)) {
381 cm_query_result(pcity
, ¶meter
, 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
));
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."),
402 log_handle_city2(" ok");
410 cm_result_destroy(result
);
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."),
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
);
440 cm_clear_cache(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)
461 /*************************** public interface *******************************/
462 /****************************************************************************
463 Initialize city governor code
464 *****************************************************************************/
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
481 stats
.wall_timer
= timer_renew(timer
, TIMER_USER
, TIMER_ACTIVE
);
483 memset(&self
, 0, sizeof(self
));
484 strcpy(self
.name
, "CMA");
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
);
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
)) {
546 memcpy(parameter
, &my_parameter
, sizeof(struct cm_parameter
));
551 /**************************************************************************
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
)
562 char buffer
[SAVED_PARAMETER_SIZE
];
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
);
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
582 cm_init_parameter(parameter
);
584 output_type_iterate(i
) {
585 dio_get_sint16_raw(&din
, ¶meter
->minimal_surplus
[i
]);
586 dio_get_sint16_raw(&din
, ¶meter
->factor
[i
]);
587 } output_type_iterate_end
;
589 dio_get_sint16_raw(&din
, ¶meter
->happy_factor
);
590 dio_get_uint8_raw(&din
, &dummy
); /* Dummy value; used to be factor_target. */
591 dio_get_bool8_raw(&din
, ¶meter
->require_happy
);
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
);