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 <stdarg.h>
19 #include <string.h>
20
21 /* utility */
22 #include "capability.h"
23 #include "log.h"
24 #include "mem.h"
25 #include "timing.h"
26
27 /* client */
28 #include "client_main.h"
29
30 /* include */
31 #include "mapctrl_g.h"
32
33 /* agents */
34 #include "cma_core.h"
35 #include "cma_fec.h"
36 #include "sha.h"
37
38 #include "agents.h"
39
40 #define log_request_ids(...) /* log_test(__VA_ARGS__) */
41 #define log_todo_lists(...) /* log_test(__VA_ARGS__) */
42 #define log_meta_callback(...) log_debug(__VA_ARGS__)
43 #define log_debug_freeze(...) /* log_test(__VA_ARGS__) */
44
45 #define MAX_AGENTS 10
46
47 struct my_agent;
48
49 struct call {
50 struct my_agent *agent;
51 enum oct { OCT_NEW_TURN, OCT_UNIT, OCT_CITY, OCT_TILE } type;
52 enum callback_type cb_type;
53 int arg;
54 };
55
56 #define SPECLIST_TAG call
57 #define SPECLIST_TYPE struct call
58 #include "speclist.h"
59
60 #define call_list_iterate(calllist, pcall) \
61 TYPED_LIST_ITERATE(struct call, calllist, pcall)
62 #define call_list_iterate_end LIST_ITERATE_END
63
64 #define call_list_both_iterate(calllist, plink, pcall) \
65 TYPED_LIST_BOTH_ITERATE(struct call_list_link, struct call, \
66 calllist, plink, pcall)
67 #define call_list_both_iterate_end LIST_BOTH_ITERATE_END
68
69 /*
70 * Main data structure. Contains all registered agents and all
71 * outstanding calls.
72 */
73 static struct {
74 int entries_used;
75 struct my_agent {
76 struct agent agent;
77 int first_outstanding_request_id, last_outstanding_request_id;
78 struct {
79 struct timer *network_wall_timer;
80 int wait_at_network, wait_at_network_requests;
81 } stats;
82 } entries[MAX_AGENTS];
83 struct call_list *calls;
84 } agents;
85
86 static bool initialized = FALSE;
87 static int frozen_level;
88 static bool currently_running = FALSE;
89
90 /****************************************************************************
91 Return TRUE iff the two agent calls are equal.
92 ****************************************************************************/
calls_are_equal(const struct call * pcall1,const struct call * pcall2)93 static bool calls_are_equal(const struct call *pcall1,
94 const struct call *pcall2)
95 {
96 if (pcall1->agent != pcall2->agent) {
97 return FALSE;
98 }
99
100 if (pcall1->type != pcall2->type && pcall1->cb_type != pcall2->cb_type) {
101 return FALSE;
102 }
103
104 switch (pcall1->type) {
105 case OCT_UNIT:
106 case OCT_CITY:
107 case OCT_TILE:
108 return (pcall1->arg == pcall2->arg);
109 case OCT_NEW_TURN:
110 return TRUE;
111 }
112
113 log_error("Unsupported call type %d.", pcall1->type);
114 return FALSE;
115 }
116
117 /***********************************************************************
118 If the call described by the given arguments isn't contained in
119 agents.calls list, add the call to this list.
120 Maintains the list in a sorted order.
121 ***********************************************************************/
enqueue_call(enum oct type,enum callback_type cb_type,struct my_agent * agent,...)122 static void enqueue_call(enum oct type,
123 enum callback_type cb_type,
124 struct my_agent *agent, ...)
125 {
126 va_list ap;
127 struct call *pcall2;
128 int arg = 0;
129 const struct tile *ptile;
130 bool added = FALSE;
131
132 va_start(ap, agent);
133
134 if (client_is_observer()) {
135 return;
136 }
137
138 switch (type) {
139 case OCT_UNIT:
140 case OCT_CITY:
141 arg = va_arg(ap, int);
142 break;
143 case OCT_TILE:
144 ptile = va_arg(ap, const struct tile *);
145 arg = tile_index(ptile);
146 break;
147 case OCT_NEW_TURN:
148 /* nothing */
149 break;
150 }
151 va_end(ap);
152
153 pcall2 = fc_malloc(sizeof(struct call));
154
155 pcall2->agent = agent;
156 pcall2->type = type;
157 pcall2->cb_type = cb_type;
158 pcall2->arg = arg;
159
160 /* Ensure list is sorted so that calls to agents with lower levels
161 * come first, since that's how we'll want to pop them */
162 call_list_both_iterate(agents.calls, plink, pcall) {
163 if (calls_are_equal(pcall, pcall2)) {
164 /* Already got one like this, discard duplicate. */
165 free(pcall2);
166 return;
167 }
168 if (pcall->agent->agent.level - pcall2->agent->agent.level > 0) {
169 /* Found a level greater than ours. Can assume that calls_are_equal()
170 * will never be true from here on, since list is sorted by level and
171 * unequal levels => unequal agents => !calls_are_equal().
172 * Insert into list here. */
173 call_list_insert_before(agents.calls, pcall2, plink);
174 added = TRUE;
175 break;
176 }
177 } call_list_both_iterate_end;
178
179 if (!added) {
180 call_list_append(agents.calls, pcall2);
181 }
182
183 log_todo_lists("A: adding call");
184
185 /* agents_busy() may have changed */
186 update_turn_done_button_state();
187 }
188
189 /***********************************************************************
190 Return an outstanding call. The call is removed from the agents.calls
191 list. Returns NULL if there no more outstanding calls.
192 ***********************************************************************/
remove_and_return_a_call(void)193 static struct call *remove_and_return_a_call(void)
194 {
195 struct call *result;
196
197 if (call_list_size(agents.calls) == 0) {
198 return NULL;
199 }
200
201 result = call_list_front(agents.calls);
202 call_list_pop_front(agents.calls);
203
204 log_todo_lists("A: removed call");
205 return result;
206 }
207
208 /***********************************************************************
209 Calls an callback of an agent as described in the given call.
210 ***********************************************************************/
execute_call(const struct call * call)211 static void execute_call(const struct call *call)
212 {
213 switch (call->type) {
214 case OCT_NEW_TURN:
215 call->agent->agent.turn_start_notify();
216 break;
217 case OCT_UNIT:
218 call->agent->agent.unit_callbacks[call->cb_type] (call->arg);
219 break;
220 case OCT_CITY:
221 call->agent->agent.city_callbacks[call->cb_type] (call->arg);
222 break;
223 case OCT_TILE:
224 call->agent->agent.tile_callbacks[call->cb_type]
225 (index_to_tile(call->arg));
226 break;
227 }
228 }
229
230 /***********************************************************************
231 Execute all outstanding calls. This method will do nothing if the
232 dispatching is frozen (frozen_level > 0). Also call_handle_methods
233 will ensure that only one instance is running at any given time.
234 ***********************************************************************/
call_handle_methods(void)235 static void call_handle_methods(void)
236 {
237 if (currently_running) {
238 return;
239 }
240 if (frozen_level > 0) {
241 return;
242 }
243 currently_running = TRUE;
244
245 /*
246 * The following should ensure that the methods of agents which have
247 * a lower level are called first.
248 */
249 for (;;) {
250 struct call *pcall;
251
252 pcall = remove_and_return_a_call();
253 if (!pcall) {
254 break;
255 }
256
257 execute_call(pcall);
258 free(pcall);
259 }
260
261 currently_running = FALSE;
262
263 update_turn_done_button_state();
264 }
265
266 /***********************************************************************
267 Increase the frozen_level by one.
268 ***********************************************************************/
freeze(void)269 static void freeze(void)
270 {
271 if (!initialized) {
272 frozen_level = 0;
273 initialized = TRUE;
274 }
275 log_debug_freeze("A: freeze() current level=%d", frozen_level);
276 frozen_level++;
277 }
278
279 /***********************************************************************
280 Decrease the frozen_level by one. If the dispatching is not frozen
281 anymore (frozen_level == 0) all outstanding calls are executed.
282 ***********************************************************************/
thaw(void)283 static void thaw(void)
284 {
285 log_debug_freeze("A: thaw() current level=%d", frozen_level);
286 frozen_level--;
287 fc_assert(frozen_level >= 0);
288 if (0 == frozen_level && C_S_RUNNING == client_state()) {
289 call_handle_methods();
290 }
291 }
292
293 /***********************************************************************
294 Helper.
295 ***********************************************************************/
agent_by_name(const char * agent_name)296 static struct my_agent *agent_by_name(const char *agent_name)
297 {
298 int i;
299
300 for (i = 0; i < agents.entries_used; i++) {
301 if (strcmp(agent_name, agents.entries[i].agent.name) == 0)
302 return &agents.entries[i];
303 }
304
305 return NULL;
306 }
307
308 /***********************************************************************
309 Returns TRUE iff currently handled packet was caused by the given
310 agent.
311 ***********************************************************************/
is_outstanding_request(struct my_agent * agent)312 static bool is_outstanding_request(struct my_agent *agent)
313 {
314 if (agent->first_outstanding_request_id != 0 &&
315 client.conn.client.request_id_of_currently_handled_packet != 0 &&
316 agent->first_outstanding_request_id <=
317 client.conn.client.request_id_of_currently_handled_packet &&
318 agent->last_outstanding_request_id >=
319 client.conn.client.request_id_of_currently_handled_packet) {
320 log_debug("A:%s: ignoring packet; outstanding [%d..%d] got=%d",
321 agent->agent.name, agent->first_outstanding_request_id,
322 agent->last_outstanding_request_id,
323 client.conn.client.request_id_of_currently_handled_packet);
324 return TRUE;
325 }
326 return FALSE;
327 }
328
329 /***********************************************************************
330 Called once per client startup.
331 ***********************************************************************/
agents_init(void)332 void agents_init(void)
333 {
334 agents.entries_used = 0;
335 agents.calls = call_list_new();
336
337 /* Add init calls of agents here */
338 cma_init();
339 cmafec_init();
340 /*simple_historian_init();*/
341 }
342
343 /***********************************************************************
344 Free resources allocated for agents framework
345 ***********************************************************************/
agents_free(void)346 void agents_free(void)
347 {
348 int i;
349
350 /* FIXME: doing this will wipe out any presets on disconnect.
351 * a proper solution should be to split up the client_free functions
352 * for a simple disconnect and a client quit. for right now, we just
353 * let the OS free the memory on exit instead of doing it ourselves. */
354 /* cmafec_free(); */
355
356 /*simple_historian_done();*/
357
358 for (;;) {
359 struct call *pcall = remove_and_return_a_call();
360 if (!pcall) {
361 break;
362 }
363
364 free(pcall);
365 }
366
367 for (i = 0; i < agents.entries_used; i++) {
368 struct my_agent *agent = &agents.entries[i];
369
370 timer_destroy(agent->stats.network_wall_timer);
371 }
372 call_list_destroy(agents.calls);
373 }
374
375 /***********************************************************************
376 Registers an agent.
377 ***********************************************************************/
register_agent(const struct agent * agent)378 void register_agent(const struct agent *agent)
379 {
380 struct my_agent *priv_agent = &agents.entries[agents.entries_used];
381
382 fc_assert_ret(agents.entries_used < MAX_AGENTS);
383 fc_assert_ret(agent->level > 0);
384
385 memcpy(&priv_agent->agent, agent, sizeof(struct agent));
386
387 priv_agent->first_outstanding_request_id = 0;
388 priv_agent->last_outstanding_request_id = 0;
389
390 priv_agent->stats.network_wall_timer = timer_new(TIMER_USER, TIMER_ACTIVE);
391 priv_agent->stats.wait_at_network = 0;
392 priv_agent->stats.wait_at_network_requests = 0;
393
394 agents.entries_used++;
395 }
396
397 /***********************************************************************
398 Called from client/packhand.c.
399 ***********************************************************************/
agents_disconnect(void)400 void agents_disconnect(void)
401 {
402 log_meta_callback("agents_disconnect()");
403 initialized = FALSE;
404 }
405
406 /***********************************************************************
407 Called from client/packhand.c.
408 ***********************************************************************/
agents_processing_started(void)409 void agents_processing_started(void)
410 {
411 log_meta_callback("agents_processing_started()");
412 freeze();
413 }
414
415 /***********************************************************************
416 Called from client/packhand.c.
417 ***********************************************************************/
agents_processing_finished(void)418 void agents_processing_finished(void)
419 {
420 log_meta_callback("agents_processing_finished()");
421 thaw();
422 }
423
424 /***********************************************************************
425 Called from client/packhand.c.
426 ***********************************************************************/
agents_freeze_hint(void)427 void agents_freeze_hint(void)
428 {
429 log_meta_callback("agents_freeze_hint()");
430 freeze();
431 }
432
433 /***********************************************************************
434 Called from client/packhand.c.
435 ***********************************************************************/
agents_thaw_hint(void)436 void agents_thaw_hint(void)
437 {
438 log_meta_callback("agents_thaw_hint()");
439 thaw();
440 }
441
442 /***********************************************************************
443 Called from client/packhand.c.
444 ***********************************************************************/
agents_game_joined(void)445 void agents_game_joined(void)
446 {
447 log_meta_callback("agents_game_joined()");
448 }
449
450 /***********************************************************************
451 Called from client/packhand.c.
452 ***********************************************************************/
agents_game_start(void)453 void agents_game_start(void)
454 {
455 log_meta_callback("agents_game_start()");
456 call_handle_methods();
457 }
458
459 /***********************************************************************
460 Called from client/packhand.c.
461 ***********************************************************************/
agents_before_new_turn(void)462 void agents_before_new_turn(void)
463 {
464 log_meta_callback("agents_before_new_turn()");
465 }
466
467 /***********************************************************************
468 Called from client/packhand.c.
469 ***********************************************************************/
agents_start_turn(void)470 void agents_start_turn(void)
471 {
472 log_meta_callback("agents_start_turn()");
473 }
474
475 /***********************************************************************
476 Called from client/packhand.c. See agents_unit_changed for a generic
477 documentation.
478 ***********************************************************************/
agents_new_turn(void)479 void agents_new_turn(void)
480 {
481 int i;
482
483 for (i = 0; i < agents.entries_used; i++) {
484 struct my_agent *agent = &agents.entries[i];
485
486 if (is_outstanding_request(agent)) {
487 continue;
488 }
489 if (agent->agent.turn_start_notify) {
490 enqueue_call(OCT_NEW_TURN, CB_LAST, agent);
491 }
492 }
493 /*
494 * call_handle_methods() isn't called here because the agents are
495 * frozen anyway.
496 */
497 }
498
499 /***********************************************************************
500 Called from client/packhand.c. A call is created and added to the
501 list of outstanding calls if an agent wants to be informed about this
502 event and the change wasn't caused by the agent. We then try (this
503 may not be successful in every case since we can be frozen or another
504 call_handle_methods may be running higher up on the stack) to execute
505 all outstanding calls.
506 ***********************************************************************/
agents_unit_changed(struct unit * punit)507 void agents_unit_changed(struct unit *punit)
508 {
509 int i;
510
511 log_debug("A: agents_unit_changed(unit=%d) type=%s pos=(%d,%d) owner=%s",
512 punit->id, unit_rule_name(punit), TILE_XY(unit_tile(punit)),
513 player_name(unit_owner(punit)));
514
515 for (i = 0; i < agents.entries_used; i++) {
516 struct my_agent *agent = &agents.entries[i];
517
518 if (is_outstanding_request(agent)) {
519 continue;
520 }
521 if (agent->agent.unit_callbacks[CB_CHANGE]) {
522 enqueue_call(OCT_UNIT, CB_CHANGE, agent, punit->id);
523 }
524 }
525 call_handle_methods();
526 }
527
528 /***********************************************************************
529 Called from client/packhand.c. See agents_unit_changed for a generic
530 documentation.
531 ***********************************************************************/
agents_unit_new(struct unit * punit)532 void agents_unit_new(struct unit *punit)
533 {
534 int i;
535
536 log_debug("A: agents_new_unit(unit=%d) type=%s pos=(%d,%d) owner=%s",
537 punit->id, unit_rule_name(punit), TILE_XY(unit_tile(punit)),
538 player_name(unit_owner(punit)));
539
540 for (i = 0; i < agents.entries_used; i++) {
541 struct my_agent *agent = &agents.entries[i];
542
543 if (is_outstanding_request(agent)) {
544 continue;
545 }
546 if (agent->agent.unit_callbacks[CB_NEW]) {
547 enqueue_call(OCT_UNIT, CB_NEW, agent, punit->id);
548 }
549 }
550
551 call_handle_methods();
552 }
553
554 /***********************************************************************
555 Called from client/packhand.c. See agents_unit_changed for a generic
556 documentation.
557 ***********************************************************************/
agents_unit_remove(struct unit * punit)558 void agents_unit_remove(struct unit *punit)
559 {
560 int i;
561
562 log_debug("A: agents_remove_unit(unit=%d) type=%s pos=(%d,%d) owner=%s",
563 punit->id, unit_rule_name(punit), TILE_XY(unit_tile(punit)),
564 player_name(unit_owner(punit)));
565
566 for (i = 0; i < agents.entries_used; i++) {
567 struct my_agent *agent = &agents.entries[i];
568
569 if (is_outstanding_request(agent)) {
570 continue;
571 }
572 if (agent->agent.unit_callbacks[CB_REMOVE]) {
573 enqueue_call(OCT_UNIT, CB_REMOVE, agent, punit->id);
574 }
575 }
576
577 call_handle_methods();
578 }
579
580 /***********************************************************************
581 Called from client/packhand.c. See agents_unit_changed for a generic
582 documentation.
583 ***********************************************************************/
agents_city_changed(struct city * pcity)584 void agents_city_changed(struct city *pcity)
585 {
586 int i;
587
588 log_debug("A: agents_city_changed(city %d=\"%s\") owner=%s",
589 pcity->id, city_name_get(pcity),
590 nation_rule_name(nation_of_city(pcity)));
591
592 for (i = 0; i < agents.entries_used; i++) {
593 struct my_agent *agent = &agents.entries[i];
594
595 if (is_outstanding_request(agent)) {
596 continue;
597 }
598 if (agent->agent.city_callbacks[CB_CHANGE]) {
599 enqueue_call(OCT_CITY, CB_CHANGE, agent, pcity->id);
600 }
601 }
602
603 call_handle_methods();
604 }
605
606 /***********************************************************************
607 Called from client/packhand.c. See agents_unit_changed for a generic
608 documentation.
609 ***********************************************************************/
agents_city_new(struct city * pcity)610 void agents_city_new(struct city *pcity)
611 {
612 int i;
613
614 log_debug("A: agents_city_new(city %d=\"%s\") pos=(%d,%d) owner=%s",
615 pcity->id, city_name_get(pcity), TILE_XY(pcity->tile),
616 nation_rule_name(nation_of_city(pcity)));
617
618 for (i = 0; i < agents.entries_used; i++) {
619 struct my_agent *agent = &agents.entries[i];
620
621 if (is_outstanding_request(agent)) {
622 continue;
623 }
624 if (agent->agent.city_callbacks[CB_NEW]) {
625 enqueue_call(OCT_CITY, CB_NEW, agent, pcity->id);
626 }
627 }
628
629 call_handle_methods();
630 }
631
632 /***********************************************************************
633 Called from client/packhand.c. See agents_unit_changed for a generic
634 documentation.
635 ***********************************************************************/
agents_city_remove(struct city * pcity)636 void agents_city_remove(struct city *pcity)
637 {
638 int i;
639
640 log_debug("A: agents_city_remove(city %d=\"%s\") pos=(%d,%d) owner=%s",
641 pcity->id, city_name_get(pcity), TILE_XY(pcity->tile),
642 nation_rule_name(nation_of_city(pcity)));
643
644 for (i = 0; i < agents.entries_used; i++) {
645 struct my_agent *agent = &agents.entries[i];
646
647 if (is_outstanding_request(agent)) {
648 continue;
649 }
650 if (agent->agent.city_callbacks[CB_REMOVE]) {
651 enqueue_call(OCT_CITY, CB_REMOVE, agent, pcity->id);
652 }
653 }
654
655 call_handle_methods();
656 }
657
658 /***********************************************************************
659 Called from client/packhand.c. See agents_unit_changed for a generic
660 documentation.
661 Tiles got removed because of FOW.
662 ***********************************************************************/
agents_tile_remove(struct tile * ptile)663 void agents_tile_remove(struct tile *ptile)
664 {
665 int i;
666
667 log_debug("A: agents_tile_remove(tile=(%d, %d))", TILE_XY(ptile));
668
669 for (i = 0; i < agents.entries_used; i++) {
670 struct my_agent *agent = &agents.entries[i];
671
672 if (is_outstanding_request(agent)) {
673 continue;
674 }
675 if (agent->agent.tile_callbacks[CB_REMOVE]) {
676 enqueue_call(OCT_TILE, CB_REMOVE, agent, ptile);
677 }
678 }
679
680 call_handle_methods();
681 }
682
683 /***********************************************************************
684 Called from client/packhand.c. See agents_unit_changed for a generic
685 documentation.
686 ***********************************************************************/
agents_tile_changed(struct tile * ptile)687 void agents_tile_changed(struct tile *ptile)
688 {
689 int i;
690
691 log_debug("A: agents_tile_changed(tile=(%d, %d))", TILE_XY(ptile));
692
693 for (i = 0; i < agents.entries_used; i++) {
694 struct my_agent *agent = &agents.entries[i];
695
696 if (is_outstanding_request(agent)) {
697 continue;
698 }
699 if (agent->agent.tile_callbacks[CB_CHANGE]) {
700 enqueue_call(OCT_TILE, CB_CHANGE, agent, ptile);
701 }
702 }
703
704 call_handle_methods();
705 }
706
707 /***********************************************************************
708 Called from client/packhand.c. See agents_unit_changed for a generic
709 documentation.
710 ***********************************************************************/
agents_tile_new(struct tile * ptile)711 void agents_tile_new(struct tile *ptile)
712 {
713 int i;
714
715 log_debug("A: agents_tile_new(tile=(%d, %d))", TILE_XY(ptile));
716
717 for (i = 0; i < agents.entries_used; i++) {
718 struct my_agent *agent = &agents.entries[i];
719
720 if (is_outstanding_request(agent)) {
721 continue;
722 }
723 if (agent->agent.tile_callbacks[CB_NEW]) {
724 enqueue_call(OCT_TILE, CB_NEW, agent, ptile);
725 }
726 }
727
728 call_handle_methods();
729 }
730
731 /***********************************************************************
732 Called from an agent. This function will return until the last
733 request has been processed by the server.
734 ***********************************************************************/
wait_for_requests(const char * agent_name,int first_request_id,int last_request_id)735 void wait_for_requests(const char *agent_name, int first_request_id,
736 int last_request_id)
737 {
738 struct my_agent *agent = agent_by_name(agent_name);
739
740 log_request_ids("A:%s: wait_for_request(ids=[%d..%d])",
741 agent->agent.name, first_request_id, last_request_id);
742
743 fc_assert_ret(first_request_id != 0 && last_request_id != 0
744 && first_request_id <= last_request_id);
745 fc_assert_ret(agent->first_outstanding_request_id == 0);
746 agent->first_outstanding_request_id = first_request_id;
747 agent->last_outstanding_request_id = last_request_id;
748
749 timer_start(agent->stats.network_wall_timer);
750 wait_till_request_got_processed(last_request_id);
751 timer_stop(agent->stats.network_wall_timer);
752
753 agent->stats.wait_at_network++;
754 agent->stats.wait_at_network_requests +=
755 (1 + (last_request_id - first_request_id));
756
757 log_request_ids("A:%s: wait_for_request: ids=[%d..%d]; got it",
758 agent->agent.name, first_request_id, last_request_id);
759
760 agent->first_outstanding_request_id = 0;
761
762 log_debug("A:%s: waited %fs in total for network; "
763 "requests=%d; waited %d times",
764 agent->agent.name,
765 timer_read_seconds(agent->stats.network_wall_timer),
766 agent->stats.wait_at_network_requests,
767 agent->stats.wait_at_network);
768 }
769
770 /***********************************************************************
771 Adds a specific call for the given agent.
772 ***********************************************************************/
cause_a_unit_changed_for_agent(const char * name_of_calling_agent,struct unit * punit)773 void cause_a_unit_changed_for_agent(const char *name_of_calling_agent,
774 struct unit *punit)
775 {
776 struct my_agent *agent = agent_by_name(name_of_calling_agent);
777
778 fc_assert_ret(agent->agent.unit_callbacks[CB_CHANGE] != NULL);
779 enqueue_call(OCT_UNIT, CB_CHANGE, agent, punit->id);
780 call_handle_methods();
781 }
782
783 /***********************************************************************
784 Adds a specific call for the given agent.
785 ***********************************************************************/
cause_a_city_changed_for_agent(const char * name_of_calling_agent,struct city * pcity)786 void cause_a_city_changed_for_agent(const char *name_of_calling_agent,
787 struct city *pcity)
788 {
789 struct my_agent *agent = agent_by_name(name_of_calling_agent);
790
791 fc_assert_ret(agent->agent.city_callbacks[CB_CHANGE] != NULL);
792 enqueue_call(OCT_CITY, CB_CHANGE, agent, pcity->id);
793 call_handle_methods();
794 }
795
796 /***********************************************************************
797 Returns TRUE iff some agent is currently busy.
798 ***********************************************************************/
agents_busy(void)799 bool agents_busy(void)
800 {
801 int i;
802
803 if (!initialized) {
804 return FALSE;
805 }
806
807 if (call_list_size(agents.calls) > 0 || frozen_level > 0
808 || currently_running) {
809 return TRUE;
810 }
811
812 for (i = 0; i < agents.entries_used; i++) {
813 struct my_agent *agent = &agents.entries[i];
814
815 if (agent->first_outstanding_request_id != 0) {
816 return TRUE;
817 }
818 }
819 return FALSE;
820 }
821