Freeciv-3.1
Loading...
Searching...
No Matches
agents.c
Go to the documentation of this file.
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
47struct my_agent;
48
49struct call {
50 struct my_agent *agent;
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 */
73static struct {
75 struct my_agent {
76 struct agent agent;
78 struct {
83 struct call_list *calls;
85
86static bool initialized = FALSE;
87static int frozen_level;
89
90/************************************************************************/
93static 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/************************************************************************/
122static 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 }
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 */
187}
188
189/************************************************************************/
193static 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/************************************************************************/
211static 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(&(wld.map), call->arg));
226 break;
227 }
228}
229
230/************************************************************************/
235static void call_handle_methods(void)
236{
237 if (currently_running) {
238 return;
239 }
240 if (frozen_level > 0) {
241 return;
242 }
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
262
264}
265
266/************************************************************************/
269static void freeze(void)
270{
271 if (!initialized) {
272 frozen_level = 0;
274 }
275 log_debug_freeze("A: freeze() current level=%d", frozen_level);
276 frozen_level++;
277}
278
279/************************************************************************/
283static void thaw(void)
284{
285 log_debug_freeze("A: thaw() current level=%d", frozen_level);
286 frozen_level--;
288 if (0 == frozen_level && C_S_RUNNING == client_state()) {
290 }
291}
292
293/************************************************************************/
296static 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/************************************************************************/
312static bool is_outstanding_request(struct my_agent *agent)
313{
314 if (agent->first_outstanding_request_id != 0
316 && agent->first_outstanding_request_id <=
318 && agent->last_outstanding_request_id >=
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,
324 return TRUE;
325 }
326 return FALSE;
327}
328
329/************************************************************************/
332void 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/************************************************************************/
346void 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/************************************************************************/
378void 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);
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/************************************************************************/
401{
402 log_meta_callback("agents_disconnect()");
404}
405
406/************************************************************************/
410{
411 log_meta_callback("agents_processing_started()");
412 freeze();
413}
414
415/************************************************************************/
419{
420 log_meta_callback("agents_processing_finished()");
421 thaw();
422}
423
424/************************************************************************/
428{
429 log_meta_callback("agents_freeze_hint()");
430 freeze();
431}
432
433/************************************************************************/
437{
438 log_meta_callback("agents_thaw_hint()");
439 thaw();
440}
441
442/************************************************************************/
446{
447 log_meta_callback("agents_game_joined()");
448}
449
450/************************************************************************/
454{
455 log_meta_callback("agents_game_start()");
457}
458
459/************************************************************************/
463{
464 log_meta_callback("agents_before_new_turn()");
465}
466
467/************************************************************************/
471{
472 log_meta_callback("agents_start_turn()");
473}
474
475/************************************************************************/
480{
481 int i;
482
483 for (i = 0; i < agents.entries_used; i++) {
484 struct my_agent *agent = &agents.entries[i];
485
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/************************************************************************/
508{
509 int i;
510
511 log_debug("A: agents_unit_changed(unit=%d) type=%s pos=(%d,%d) owner=%s",
514
515 for (i = 0; i < agents.entries_used; i++) {
516 struct my_agent *agent = &agents.entries[i];
517
519 continue;
520 }
521 if (agent->agent.unit_callbacks[CB_CHANGE]) {
522 enqueue_call(OCT_UNIT, CB_CHANGE, agent, punit->id);
523 }
524 }
526}
527
528/************************************************************************/
533{
534 int i;
535
536 log_debug("A: agents_new_unit(unit=%d) type=%s pos=(%d,%d) owner=%s",
539
540 for (i = 0; i < agents.entries_used; i++) {
541 struct my_agent *agent = &agents.entries[i];
542
544 continue;
545 }
546 if (agent->agent.unit_callbacks[CB_NEW]) {
547 enqueue_call(OCT_UNIT, CB_NEW, agent, punit->id);
548 }
549 }
550
552}
553
554/************************************************************************/
559{
560 int i;
561
562 log_debug("A: agents_remove_unit(unit=%d) type=%s pos=(%d,%d) owner=%s",
565
566 for (i = 0; i < agents.entries_used; i++) {
567 struct my_agent *agent = &agents.entries[i];
568
570 continue;
571 }
572 if (agent->agent.unit_callbacks[CB_REMOVE]) {
573 enqueue_call(OCT_UNIT, CB_REMOVE, agent, punit->id);
574 }
575 }
576
578}
579
580/************************************************************************/
584void 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),
591
592 for (i = 0; i < agents.entries_used; i++) {
593 struct my_agent *agent = &agents.entries[i];
594
596 continue;
597 }
598 if (agent->agent.city_callbacks[CB_CHANGE]) {
599 enqueue_call(OCT_CITY, CB_CHANGE, agent, pcity->id);
600 }
601 }
602
604}
605
606/************************************************************************/
610void 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),
617
618 for (i = 0; i < agents.entries_used; i++) {
619 struct my_agent *agent = &agents.entries[i];
620
622 continue;
623 }
624 if (agent->agent.city_callbacks[CB_NEW]) {
625 enqueue_call(OCT_CITY, CB_NEW, agent, pcity->id);
626 }
627 }
628
630}
631
632/************************************************************************/
636void 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),
643
644 for (i = 0; i < agents.entries_used; i++) {
645 struct my_agent *agent = &agents.entries[i];
646
648 continue;
649 }
650 if (agent->agent.city_callbacks[CB_REMOVE]) {
651 enqueue_call(OCT_CITY, CB_REMOVE, agent, pcity->id);
652 }
653 }
654
656}
657
658/************************************************************************/
663void 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
673 continue;
674 }
675 if (agent->agent.tile_callbacks[CB_REMOVE]) {
676 enqueue_call(OCT_TILE, CB_REMOVE, agent, ptile);
677 }
678 }
679
681}
682
683/************************************************************************/
687void 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
697 continue;
698 }
699 if (agent->agent.tile_callbacks[CB_CHANGE]) {
700 enqueue_call(OCT_TILE, CB_CHANGE, agent, ptile);
701 }
702 }
703
705}
706
707/************************************************************************/
711void 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
721 continue;
722 }
723 if (agent->agent.tile_callbacks[CB_NEW]) {
724 enqueue_call(OCT_TILE, CB_NEW, agent, ptile);
725 }
726 }
727
729}
730
731/************************************************************************/
735void 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/************************************************************************/
773void 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
779 enqueue_call(OCT_UNIT, CB_CHANGE, agent, punit->id);
781}
782
783/************************************************************************/
786void 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
792 enqueue_call(OCT_CITY, CB_CHANGE, agent, pcity->id);
794}
795
796/************************************************************************/
799bool 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
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}
struct agent agent
Definition agents.c:76
struct call_list * calls
Definition agents.c:83
#define log_todo_lists(...)
Definition agents.c:41
static struct my_agent * agent_by_name(const char *agent_name)
Definition agents.c:296
static void enqueue_call(enum oct type, enum callback_type cb_type, struct my_agent *agent,...)
Definition agents.c:122
static bool calls_are_equal(const struct call *pcall1, const struct call *pcall2)
Definition agents.c:93
void agents_unit_changed(struct unit *punit)
Definition agents.c:507
void agents_city_remove(struct city *pcity)
Definition agents.c:636
static int frozen_level
Definition agents.c:87
int entries_used
Definition agents.c:74
void agents_free(void)
Definition agents.c:346
void agents_thaw_hint(void)
Definition agents.c:436
void agents_disconnect(void)
Definition agents.c:400
void agents_game_start(void)
Definition agents.c:453
void wait_for_requests(const char *agent_name, int first_request_id, int last_request_id)
Definition agents.c:735
void agents_tile_remove(struct tile *ptile)
Definition agents.c:663
void agents_city_new(struct city *pcity)
Definition agents.c:610
struct timer * network_wall_timer
Definition agents.c:79
struct @124::my_agent entries[MAX_AGENTS]
int wait_at_network_requests
Definition agents.c:80
#define call_list_both_iterate_end
Definition agents.c:67
int first_outstanding_request_id
Definition agents.c:77
void agents_start_turn(void)
Definition agents.c:470
void agents_tile_changed(struct tile *ptile)
Definition agents.c:687
int wait_at_network
Definition agents.c:80
static void freeze(void)
Definition agents.c:269
static void call_handle_methods(void)
Definition agents.c:235
void agents_unit_new(struct unit *punit)
Definition agents.c:532
static struct call * remove_and_return_a_call(void)
Definition agents.c:193
bool agents_busy(void)
Definition agents.c:799
void agents_unit_remove(struct unit *punit)
Definition agents.c:558
static bool currently_running
Definition agents.c:88
static bool is_outstanding_request(struct my_agent *agent)
Definition agents.c:312
void agents_processing_started(void)
Definition agents.c:409
static struct @124 agents
void agents_tile_new(struct tile *ptile)
Definition agents.c:711
void agents_processing_finished(void)
Definition agents.c:418
void agents_new_turn(void)
Definition agents.c:479
#define log_request_ids(...)
Definition agents.c:40
void agents_city_changed(struct city *pcity)
Definition agents.c:584
int last_outstanding_request_id
Definition agents.c:77
void cause_a_city_changed_for_agent(const char *name_of_calling_agent, struct city *pcity)
Definition agents.c:786
void agents_game_joined(void)
Definition agents.c:445
#define MAX_AGENTS
Definition agents.c:45
struct @124::my_agent::@125 stats
static void thaw(void)
Definition agents.c:283
void register_agent(const struct agent *agent)
Definition agents.c:378
void agents_init(void)
Definition agents.c:332
void cause_a_unit_changed_for_agent(const char *name_of_calling_agent, struct unit *punit)
Definition agents.c:773
static void execute_call(const struct call *call)
Definition agents.c:211
#define call_list_both_iterate(calllist, plink, pcall)
Definition agents.c:64
static bool initialized
Definition agents.c:86
#define log_debug_freeze(...)
Definition agents.c:43
#define log_meta_callback(...)
Definition agents.c:42
void agents_freeze_hint(void)
Definition agents.c:427
void agents_before_new_turn(void)
Definition agents.c:462
callback_type
Definition agents.h:36
@ CB_LAST
Definition agents.h:37
@ CB_NEW
Definition agents.h:37
@ CB_REMOVE
Definition agents.h:37
@ CB_CHANGE
Definition agents.h:37
const char * city_name_get(const struct city *pcity)
Definition city.c:1115
bool client_is_observer(void)
struct civclient client
enum client_states client_state(void)
void wait_till_request_got_processed(int request_id)
@ C_S_RUNNING
Definition client_main.h:47
void cma_init(void)
Definition cma_core.c:478
void cmafec_init(void)
Definition cma_fec.c:76
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
struct world wld
Definition game.c:58
GType type
Definition repodlgs.c:1312
#define fc_assert_ret(condition)
Definition log.h:191
#define fc_assert(condition)
Definition log.h:176
#define log_debug(message,...)
Definition log.h:115
#define log_error(message,...)
Definition log.h:103
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:454
void update_turn_done_button_state(void)
#define fc_malloc(sz)
Definition mem.h:34
const char * nation_rule_name(const struct nation_type *pnation)
Definition nation.c:137
struct nation_type * nation_of_city(const struct city *pcity)
Definition nation.c:453
const char * player_name(const struct player *pplayer)
Definition player.c:886
Definition agents.h:40
int level
Definition agents.h:42
void(* unit_callbacks[CB_LAST])(int)
Definition agents.h:46
void(* tile_callbacks[CB_LAST])(struct tile *ptile)
Definition agents.h:47
char name[MAX_AGENT_NAME_LEN]
Definition agents.h:41
void(* city_callbacks[CB_LAST])(int)
Definition agents.h:45
void(* turn_start_notify)(void)
Definition agents.h:44
Definition agents.c:49
enum callback_type cb_type
Definition agents.c:52
struct my_agent * agent
Definition agents.c:50
oct
Definition agents.c:51
@ OCT_CITY
Definition agents.c:51
@ OCT_TILE
Definition agents.c:51
@ OCT_NEW_TURN
Definition agents.c:51
@ OCT_UNIT
Definition agents.c:51
enum call::oct type
int arg
Definition agents.c:53
Definition city.h:309
int id
Definition city.h:315
struct tile * tile
Definition city.h:311
struct connection conn
Definition client_main.h:96
struct connection::@57::@62 client
int request_id_of_currently_handled_packet
Definition connection.h:198
Definition tile.h:49
Definition timing.c:81
Definition unit.h:138
int id
Definition unit.h:145
struct civ_map map
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define tile_index(_pt_)
Definition tile.h:87
#define TILE_XY(ptile)
Definition tile.h:42
struct timer * timer_new(enum timer_timetype type, enum timer_use use)
Definition timing.c:157
void timer_destroy(struct timer *t)
Definition timing.c:191
void timer_start(struct timer *t)
Definition timing.c:224
void timer_stop(struct timer *t)
Definition timing.c:268
double timer_read_seconds(struct timer *t)
Definition timing.c:344
@ TIMER_ACTIVE
Definition timing.h:45
@ TIMER_USER
Definition timing.h:41
#define unit_tile(_pu)
Definition unit.h:395
#define unit_owner(_pu)
Definition unit.h:394
const char * unit_rule_name(const struct unit *punit)
Definition unittype.c:1639