Qt client - remove right click menu from end turn sidebar
[freeciv.git] / client / agents / agents.c
blob0e00ccd251ee6f883dab666241d95e221aa01bd4
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdarg.h>
19 #include <string.h>
21 /* utility */
22 #include "capability.h"
23 #include "log.h"
24 #include "mem.h"
25 #include "timing.h"
27 /* client */
28 #include "client_main.h"
30 /* include */
31 #include "mapctrl_g.h"
33 /* agents */
34 #include "cma_core.h"
35 #include "cma_fec.h"
36 #include "sha.h"
38 #include "agents.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__) */
45 #define MAX_AGENTS 10
47 struct my_agent;
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;
56 #define SPECLIST_TAG call
57 #define SPECLIST_TYPE struct call
58 #include "speclist.h"
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
71 * outstanding calls.
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;
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) {
97 return FALSE;
100 if (pcall1->type != pcall2->type && pcall1->cb_type != pcall2->cb_type) {
101 return FALSE;
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;
113 log_error("Unsupported call type %d.", pcall1->type);
114 return FALSE;
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, ...)
126 va_list ap;
127 struct call *pcall2;
128 int arg = 0;
129 const struct tile *ptile;
130 bool added = FALSE;
132 va_start(ap, agent);
134 if (client_is_observer()) {
135 return;
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;
151 va_end(ap);
153 pcall2 = fc_malloc(sizeof(struct call));
155 pcall2->agent = agent;
156 pcall2->type = type;
157 pcall2->cb_type = cb_type;
158 pcall2->arg = arg;
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;
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;
177 } call_list_both_iterate_end;
179 if (!added) {
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)
195 struct call *result;
197 if (call_list_size(agents.calls) == 0) {
198 return NULL;
201 result = call_list_front(agents.calls);
202 call_list_pop_front(agents.calls);
204 log_todo_lists("A: removed call");
205 return result;
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) {
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;
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) {
238 return;
240 if (frozen_level > 0) {
241 return;
243 currently_running = TRUE;
246 * The following should ensure that the methods of agents which have
247 * a lower level are called first.
249 for (;;) {
250 struct call *pcall;
252 pcall = remove_and_return_a_call();
253 if (!pcall) {
254 break;
257 execute_call(pcall);
258 free(pcall);
261 currently_running = FALSE;
263 update_turn_done_button_state();
266 /***********************************************************************
267 Increase the frozen_level by one.
268 ***********************************************************************/
269 static void freeze(void)
271 if (!initialized) {
272 frozen_level = 0;
273 initialized = TRUE;
275 log_debug_freeze("A: freeze() current level=%d", frozen_level);
276 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);
286 frozen_level--;
287 fc_assert(frozen_level >= 0);
288 if (0 == frozen_level && C_S_RUNNING == client_state()) {
289 call_handle_methods();
293 /***********************************************************************
294 Helper.
295 ***********************************************************************/
296 static struct my_agent *agent_by_name(const char *agent_name)
298 int i;
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];
305 return NULL;
308 /***********************************************************************
309 Returns TRUE iff currently handled packet was caused by the given
310 agent.
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);
324 return TRUE;
326 return FALSE;
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 */
338 cma_init();
339 cmafec_init();
340 /*simple_historian_init();*/
343 /***********************************************************************
344 Free resources allocated for agents framework
345 ***********************************************************************/
346 void agents_free(void)
348 int i;
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(); */
356 /*simple_historian_done();*/
358 for (;;) {
359 struct call *pcall = remove_and_return_a_call();
360 if (!pcall) {
361 break;
364 free(pcall);
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 /***********************************************************************
376 Registers an agent.
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()");
403 initialized = FALSE;
406 /***********************************************************************
407 Called from client/packhand.c.
408 ***********************************************************************/
409 void agents_processing_started(void)
411 log_meta_callback("agents_processing_started()");
412 freeze();
415 /***********************************************************************
416 Called from client/packhand.c.
417 ***********************************************************************/
418 void agents_processing_finished(void)
420 log_meta_callback("agents_processing_finished()");
421 thaw();
424 /***********************************************************************
425 Called from client/packhand.c.
426 ***********************************************************************/
427 void agents_freeze_hint(void)
429 log_meta_callback("agents_freeze_hint()");
430 freeze();
433 /***********************************************************************
434 Called from client/packhand.c.
435 ***********************************************************************/
436 void agents_thaw_hint(void)
438 log_meta_callback("agents_thaw_hint()");
439 thaw();
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
477 documentation.
478 ***********************************************************************/
479 void agents_new_turn(void)
481 int i;
483 for (i = 0; i < agents.entries_used; i++) {
484 struct my_agent *agent = &agents.entries[i];
486 if (is_outstanding_request(agent)) {
487 continue;
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
495 * frozen anyway.
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)
509 int i;
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)) {
519 continue;
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
530 documentation.
531 ***********************************************************************/
532 void agents_unit_new(struct unit *punit)
534 int i;
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)) {
544 continue;
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
556 documentation.
557 ***********************************************************************/
558 void agents_unit_remove(struct unit *punit)
560 int i;
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)) {
570 continue;
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
582 documentation.
583 ***********************************************************************/
584 void agents_city_changed(struct city *pcity)
586 int i;
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)) {
596 continue;
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
608 documentation.
609 ***********************************************************************/
610 void agents_city_new(struct city *pcity)
612 int i;
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)) {
622 continue;
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
634 documentation.
635 ***********************************************************************/
636 void agents_city_remove(struct city *pcity)
638 int i;
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)) {
648 continue;
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
660 documentation.
661 Tiles got removed because of FOW.
662 ***********************************************************************/
663 void agents_tile_remove(struct tile *ptile)
665 int i;
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)) {
673 continue;
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
685 documentation.
686 ***********************************************************************/
687 void agents_tile_changed(struct tile *ptile)
689 int i;
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)) {
697 continue;
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
709 documentation.
710 ***********************************************************************/
711 void agents_tile_new(struct tile *ptile)
713 int i;
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)) {
721 continue;
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,
736 int last_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",
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);
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,
774 struct unit *punit)
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,
787 struct city *pcity)
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)
801 int i;
803 if (!initialized) {
804 return FALSE;
807 if (call_list_size(agents.calls) > 0 || frozen_level > 0
808 || currently_running) {
809 return TRUE;
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) {
816 return TRUE;
819 return FALSE;