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