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>
22 #include "capability.h"
28 #include "client_main.h"
31 #include "mapctrl_g.h"
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__) */
50 struct my_agent
*agent
;
51 enum oct
{ OCT_NEW_TURN
, OCT_UNIT
, OCT_CITY
, OCT_TILE
} type
;
52 enum callback_type cb_type
;
56 #define SPECLIST_TAG call
57 #define SPECLIST_TYPE struct call
60 #define call_list_iterate(calllist, pcall) \
61 TYPED_LIST_ITERATE(struct call, calllist, pcall)
62 #define call_list_iterate_end LIST_ITERATE_END
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
70 * Main data structure. Contains all registered agents and all
77 int first_outstanding_request_id
, last_outstanding_request_id
;
79 struct timer
*network_wall_timer
;
80 int wait_at_network
, wait_at_network_requests
;
82 } entries
[MAX_AGENTS
];
83 struct call_list
*calls
;
86 static bool initialized
= FALSE
;
87 static int frozen_level
;
88 static bool currently_running
= FALSE
;
90 /****************************************************************************
91 Return TRUE iff the two agent calls are equal.
92 ****************************************************************************/
93 static bool calls_are_equal(const struct call
*pcall1
,
94 const struct call
*pcall2
)
96 if (pcall1
->agent
!= pcall2
->agent
) {
100 if (pcall1
->type
!= pcall2
->type
&& pcall1
->cb_type
!= pcall2
->cb_type
) {
104 switch (pcall1
->type
) {
108 return (pcall1
->arg
== pcall2
->arg
);
113 log_error("Unsupported call type %d.", pcall1
->type
);
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 ***********************************************************************/
122 static void enqueue_call(enum oct type
,
123 enum callback_type cb_type
,
124 struct my_agent
*agent
, ...)
129 const struct tile
*ptile
;
134 if (client_is_observer()) {
141 arg
= va_arg(ap
, int);
144 ptile
= va_arg(ap
, const struct tile
*);
145 arg
= tile_index(ptile
);
153 pcall2
= fc_malloc(sizeof(struct call
));
155 pcall2
->agent
= agent
;
157 pcall2
->cb_type
= cb_type
;
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. */
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
);
177 } call_list_both_iterate_end
;
180 call_list_append(agents
.calls
, pcall2
);
183 log_todo_lists("A: adding call");
185 /* agents_busy() may have changed */
186 update_turn_done_button_state();
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 ***********************************************************************/
193 static struct call
*remove_and_return_a_call(void)
197 if (call_list_size(agents
.calls
) == 0) {
201 result
= call_list_front(agents
.calls
);
202 call_list_pop_front(agents
.calls
);
204 log_todo_lists("A: removed call");
208 /***********************************************************************
209 Calls an callback of an agent as described in the given call.
210 ***********************************************************************/
211 static void execute_call(const struct call
*call
)
213 switch (call
->type
) {
215 call
->agent
->agent
.turn_start_notify();
218 call
->agent
->agent
.unit_callbacks
[call
->cb_type
] (call
->arg
);
221 call
->agent
->agent
.city_callbacks
[call
->cb_type
] (call
->arg
);
224 call
->agent
->agent
.tile_callbacks
[call
->cb_type
]
225 (index_to_tile(&(wld
.map
), call
->arg
));
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 ***********************************************************************/
235 static void call_handle_methods(void)
237 if (currently_running
) {
240 if (frozen_level
> 0) {
243 currently_running
= TRUE
;
246 * The following should ensure that the methods of agents which have
247 * a lower level are called first.
252 pcall
= remove_and_return_a_call();
261 currently_running
= FALSE
;
263 update_turn_done_button_state();
266 /***********************************************************************
267 Increase the frozen_level by one.
268 ***********************************************************************/
269 static void freeze(void)
275 log_debug_freeze("A: freeze() current level=%d", frozen_level
);
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 ***********************************************************************/
283 static void thaw(void)
285 log_debug_freeze("A: thaw() current level=%d", frozen_level
);
287 fc_assert(frozen_level
>= 0);
288 if (0 == frozen_level
&& C_S_RUNNING
== client_state()) {
289 call_handle_methods();
293 /***********************************************************************
295 ***********************************************************************/
296 static struct my_agent
*agent_by_name(const char *agent_name
)
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
];
308 /***********************************************************************
309 Returns TRUE iff currently handled packet was caused by the given
311 ***********************************************************************/
312 static bool is_outstanding_request(struct my_agent
*agent
)
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
);
329 /***********************************************************************
330 Called once per client startup.
331 ***********************************************************************/
332 void agents_init(void)
334 agents
.entries_used
= 0;
335 agents
.calls
= call_list_new();
337 /* Add init calls of agents here */
340 /*simple_historian_init();*/
343 /***********************************************************************
344 Free resources allocated for agents framework
345 ***********************************************************************/
346 void agents_free(void)
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. */
356 /*simple_historian_done();*/
359 struct call
*pcall
= remove_and_return_a_call();
367 for (i
= 0; i
< agents
.entries_used
; i
++) {
368 struct my_agent
*agent
= &agents
.entries
[i
];
370 timer_destroy(agent
->stats
.network_wall_timer
);
372 call_list_destroy(agents
.calls
);
375 /***********************************************************************
377 ***********************************************************************/
378 void register_agent(const struct agent
*agent
)
380 struct my_agent
*priv_agent
= &agents
.entries
[agents
.entries_used
];
382 fc_assert_ret(agents
.entries_used
< MAX_AGENTS
);
383 fc_assert_ret(agent
->level
> 0);
385 memcpy(&priv_agent
->agent
, agent
, sizeof(struct agent
));
387 priv_agent
->first_outstanding_request_id
= 0;
388 priv_agent
->last_outstanding_request_id
= 0;
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;
394 agents
.entries_used
++;
397 /***********************************************************************
398 Called from client/packhand.c.
399 ***********************************************************************/
400 void agents_disconnect(void)
402 log_meta_callback("agents_disconnect()");
406 /***********************************************************************
407 Called from client/packhand.c.
408 ***********************************************************************/
409 void agents_processing_started(void)
411 log_meta_callback("agents_processing_started()");
415 /***********************************************************************
416 Called from client/packhand.c.
417 ***********************************************************************/
418 void agents_processing_finished(void)
420 log_meta_callback("agents_processing_finished()");
424 /***********************************************************************
425 Called from client/packhand.c.
426 ***********************************************************************/
427 void agents_freeze_hint(void)
429 log_meta_callback("agents_freeze_hint()");
433 /***********************************************************************
434 Called from client/packhand.c.
435 ***********************************************************************/
436 void agents_thaw_hint(void)
438 log_meta_callback("agents_thaw_hint()");
442 /***********************************************************************
443 Called from client/packhand.c.
444 ***********************************************************************/
445 void agents_game_joined(void)
447 log_meta_callback("agents_game_joined()");
450 /***********************************************************************
451 Called from client/packhand.c.
452 ***********************************************************************/
453 void agents_game_start(void)
455 log_meta_callback("agents_game_start()");
456 call_handle_methods();
459 /***********************************************************************
460 Called from client/packhand.c.
461 ***********************************************************************/
462 void agents_before_new_turn(void)
464 log_meta_callback("agents_before_new_turn()");
467 /***********************************************************************
468 Called from client/packhand.c.
469 ***********************************************************************/
470 void agents_start_turn(void)
472 log_meta_callback("agents_start_turn()");
475 /***********************************************************************
476 Called from client/packhand.c. See agents_unit_changed for a generic
478 ***********************************************************************/
479 void agents_new_turn(void)
483 for (i
= 0; i
< agents
.entries_used
; i
++) {
484 struct my_agent
*agent
= &agents
.entries
[i
];
486 if (is_outstanding_request(agent
)) {
489 if (agent
->agent
.turn_start_notify
) {
490 enqueue_call(OCT_NEW_TURN
, CB_LAST
, agent
);
494 * call_handle_methods() isn't called here because the agents are
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 ***********************************************************************/
507 void agents_unit_changed(struct unit
*punit
)
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
)));
515 for (i
= 0; i
< agents
.entries_used
; i
++) {
516 struct my_agent
*agent
= &agents
.entries
[i
];
518 if (is_outstanding_request(agent
)) {
521 if (agent
->agent
.unit_callbacks
[CB_CHANGE
]) {
522 enqueue_call(OCT_UNIT
, CB_CHANGE
, agent
, punit
->id
);
525 call_handle_methods();
528 /***********************************************************************
529 Called from client/packhand.c. See agents_unit_changed for a generic
531 ***********************************************************************/
532 void agents_unit_new(struct unit
*punit
)
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
)));
540 for (i
= 0; i
< agents
.entries_used
; i
++) {
541 struct my_agent
*agent
= &agents
.entries
[i
];
543 if (is_outstanding_request(agent
)) {
546 if (agent
->agent
.unit_callbacks
[CB_NEW
]) {
547 enqueue_call(OCT_UNIT
, CB_NEW
, agent
, punit
->id
);
551 call_handle_methods();
554 /***********************************************************************
555 Called from client/packhand.c. See agents_unit_changed for a generic
557 ***********************************************************************/
558 void agents_unit_remove(struct unit
*punit
)
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
)));
566 for (i
= 0; i
< agents
.entries_used
; i
++) {
567 struct my_agent
*agent
= &agents
.entries
[i
];
569 if (is_outstanding_request(agent
)) {
572 if (agent
->agent
.unit_callbacks
[CB_REMOVE
]) {
573 enqueue_call(OCT_UNIT
, CB_REMOVE
, agent
, punit
->id
);
577 call_handle_methods();
580 /***********************************************************************
581 Called from client/packhand.c. See agents_unit_changed for a generic
583 ***********************************************************************/
584 void agents_city_changed(struct city
*pcity
)
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
)));
592 for (i
= 0; i
< agents
.entries_used
; i
++) {
593 struct my_agent
*agent
= &agents
.entries
[i
];
595 if (is_outstanding_request(agent
)) {
598 if (agent
->agent
.city_callbacks
[CB_CHANGE
]) {
599 enqueue_call(OCT_CITY
, CB_CHANGE
, agent
, pcity
->id
);
603 call_handle_methods();
606 /***********************************************************************
607 Called from client/packhand.c. See agents_unit_changed for a generic
609 ***********************************************************************/
610 void agents_city_new(struct city
*pcity
)
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
)));
618 for (i
= 0; i
< agents
.entries_used
; i
++) {
619 struct my_agent
*agent
= &agents
.entries
[i
];
621 if (is_outstanding_request(agent
)) {
624 if (agent
->agent
.city_callbacks
[CB_NEW
]) {
625 enqueue_call(OCT_CITY
, CB_NEW
, agent
, pcity
->id
);
629 call_handle_methods();
632 /***********************************************************************
633 Called from client/packhand.c. See agents_unit_changed for a generic
635 ***********************************************************************/
636 void agents_city_remove(struct city
*pcity
)
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
)));
644 for (i
= 0; i
< agents
.entries_used
; i
++) {
645 struct my_agent
*agent
= &agents
.entries
[i
];
647 if (is_outstanding_request(agent
)) {
650 if (agent
->agent
.city_callbacks
[CB_REMOVE
]) {
651 enqueue_call(OCT_CITY
, CB_REMOVE
, agent
, pcity
->id
);
655 call_handle_methods();
658 /***********************************************************************
659 Called from client/packhand.c. See agents_unit_changed for a generic
661 Tiles got removed because of FOW.
662 ***********************************************************************/
663 void agents_tile_remove(struct tile
*ptile
)
667 log_debug("A: agents_tile_remove(tile=(%d, %d))", TILE_XY(ptile
));
669 for (i
= 0; i
< agents
.entries_used
; i
++) {
670 struct my_agent
*agent
= &agents
.entries
[i
];
672 if (is_outstanding_request(agent
)) {
675 if (agent
->agent
.tile_callbacks
[CB_REMOVE
]) {
676 enqueue_call(OCT_TILE
, CB_REMOVE
, agent
, ptile
);
680 call_handle_methods();
683 /***********************************************************************
684 Called from client/packhand.c. See agents_unit_changed for a generic
686 ***********************************************************************/
687 void agents_tile_changed(struct tile
*ptile
)
691 log_debug("A: agents_tile_changed(tile=(%d, %d))", TILE_XY(ptile
));
693 for (i
= 0; i
< agents
.entries_used
; i
++) {
694 struct my_agent
*agent
= &agents
.entries
[i
];
696 if (is_outstanding_request(agent
)) {
699 if (agent
->agent
.tile_callbacks
[CB_CHANGE
]) {
700 enqueue_call(OCT_TILE
, CB_CHANGE
, agent
, ptile
);
704 call_handle_methods();
707 /***********************************************************************
708 Called from client/packhand.c. See agents_unit_changed for a generic
710 ***********************************************************************/
711 void agents_tile_new(struct tile
*ptile
)
715 log_debug("A: agents_tile_new(tile=(%d, %d))", TILE_XY(ptile
));
717 for (i
= 0; i
< agents
.entries_used
; i
++) {
718 struct my_agent
*agent
= &agents
.entries
[i
];
720 if (is_outstanding_request(agent
)) {
723 if (agent
->agent
.tile_callbacks
[CB_NEW
]) {
724 enqueue_call(OCT_TILE
, CB_NEW
, agent
, ptile
);
728 call_handle_methods();
731 /***********************************************************************
732 Called from an agent. This function will return until the last
733 request has been processed by the server.
734 ***********************************************************************/
735 void wait_for_requests(const char *agent_name
, int first_request_id
,
738 struct my_agent
*agent
= agent_by_name(agent_name
);
740 log_request_ids("A:%s: wait_for_request(ids=[%d..%d])",
741 agent
->agent
.name
, first_request_id
, last_request_id
);
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
;
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
);
753 agent
->stats
.wait_at_network
++;
754 agent
->stats
.wait_at_network_requests
+=
755 (1 + (last_request_id
- first_request_id
));
757 log_request_ids("A:%s: wait_for_request: ids=[%d..%d]; got it",
758 agent
->agent
.name
, first_request_id
, last_request_id
);
760 agent
->first_outstanding_request_id
= 0;
762 log_debug("A:%s: waited %fs in total for network; "
763 "requests=%d; waited %d times",
765 timer_read_seconds(agent
->stats
.network_wall_timer
),
766 agent
->stats
.wait_at_network_requests
,
767 agent
->stats
.wait_at_network
);
770 /***********************************************************************
771 Adds a specific call for the given agent.
772 ***********************************************************************/
773 void cause_a_unit_changed_for_agent(const char *name_of_calling_agent
,
776 struct my_agent
*agent
= agent_by_name(name_of_calling_agent
);
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();
783 /***********************************************************************
784 Adds a specific call for the given agent.
785 ***********************************************************************/
786 void cause_a_city_changed_for_agent(const char *name_of_calling_agent
,
789 struct my_agent
*agent
= agent_by_name(name_of_calling_agent
);
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();
796 /***********************************************************************
797 Returns TRUE iff some agent is currently busy.
798 ***********************************************************************/
799 bool agents_busy(void)
807 if (call_list_size(agents
.calls
) > 0 || frozen_level
> 0
808 || currently_running
) {
812 for (i
= 0; i
< agents
.entries_used
; i
++) {
813 struct my_agent
*agent
= &agents
.entries
[i
];
815 if (agent
->first_outstanding_request_id
!= 0) {