webperimental: killstack decides stack protects.
[freeciv.git] / client / client_main.c
blobf87633103177509f42d40c87edac243a881534a9
1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
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 "fc_prehdrs.h"
20 #ifdef FREECIV_MSWINDOWS
21 #include <windows.h> /* LoadLibrary() */
22 #endif
24 #include <math.h>
25 #include <signal.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <time.h>
31 /* utility */
32 #include "bitvector.h"
33 #include "capstr.h"
34 #include "dataio.h"
35 #include "deprecations.h"
36 #include "fcbacktrace.h"
37 #include "fc_cmdline.h"
38 #include "fciconv.h"
39 #include "fcintl.h"
40 #include "log.h"
41 #include "mem.h"
42 #include "rand.h"
43 #include "registry.h"
44 #include "support.h"
45 #include "timing.h"
47 /* common */
48 #include "ai.h"
49 #include "diptreaty.h"
50 #include "fc_cmdhelp.h"
51 #include "fc_interface.h"
52 #include "game.h"
53 #include "idex.h"
54 #include "map.h"
55 #include "mapimg.h"
56 #include "netintf.h"
57 #include "packets.h"
58 #include "player.h"
59 #include "research.h"
60 #include "server_settings.h"
61 #include "version.h"
63 /* client/include */
64 #include "chatline_g.h"
65 #include "citydlg_g.h"
66 #include "connectdlg_g.h"
67 #include "dialogs_g.h"
68 #include "diplodlg_g.h"
69 #include "editgui_g.h"
70 #include "graphics_g.h"
71 #include "gui_main_g.h"
72 #include "mapctrl_g.h"
73 #include "mapview_g.h"
74 #include "menu_g.h"
75 #include "messagewin_g.h"
76 #include "pages_g.h"
77 #include "plrdlg_g.h"
78 #include "repodlgs_g.h"
79 #include "voteinfo_bar_g.h"
81 /* client */
82 #include "attribute.h"
83 #include "audio.h"
84 #include "cityrepdata.h"
85 #include "climisc.h"
86 #include "clinet.h"
87 #include "connectdlg_common.h" /* client_kill_server() */
88 #include "control.h"
89 #include "editor.h"
90 #include "global_worklist.h"
91 #include "helpdata.h" /* boot_help_texts() */
92 #include "mapview_common.h"
93 #include "music.h"
94 #include "options.h"
95 #include "overview_common.h"
96 #include "packhand.h"
97 #include "tilespec.h"
98 #include "themes_common.h"
99 #include "update_queue.h"
100 #include "voteinfo.h"
101 #include "zoom.h"
103 /* client/agents */
104 #include "agents.h"
105 #include "cma_core.h" /* kludge */
107 /* client/luascript */
108 #include "script_client.h"
110 #include "client_main.h"
113 static enum known_type mapimg_client_tile_known(const struct tile *ptile,
114 const struct player *pplayer,
115 bool knowledge);
116 static struct terrain
117 *mapimg_client_tile_terrain(const struct tile *ptile,
118 const struct player *pplayer, bool knowledge);
119 static struct player *mapimg_client_tile_owner(const struct tile *ptile,
120 const struct player *pplayer,
121 bool knowledge);
122 static struct player *mapimg_client_tile_city(const struct tile *ptile,
123 const struct player *pplayer,
124 bool knowledge);
125 static struct player *mapimg_client_tile_unit(const struct tile *ptile,
126 const struct player *pplayer,
127 bool knowledge);
128 static int mapimg_client_plrcolor_count(void);
129 static struct rgbcolor *mapimg_client_plrcolor_get(int i);
131 static void fc_interface_init_client(void);
133 char *logfile = NULL;
134 char *scriptfile = NULL;
135 char *savefile = NULL;
136 char forced_tileset_name[512] = "\0";
137 char sound_plugin_name[512] = "\0";
138 char sound_set_name[512] = "\0";
139 char music_set_name[512] = "\0";
140 char server_host[512] = "\0";
141 char user_name[512] = "\0";
142 char password[MAX_LEN_PASSWORD] = "\0";
143 char metaserver[512] = "\0";
144 int server_port = -1;
145 bool auto_connect = FALSE; /* TRUE = skip "Connect to Freeciv Server" dialog */
146 bool auto_spawn = FALSE; /* TRUE = skip main menu, start local server */
147 enum announce_type announce;
149 struct civclient client;
151 static enum client_states civclient_state = C_S_INITIAL;
153 /* TRUE if an end turn request is blocked by busy agents */
154 bool waiting_for_end_turn = FALSE;
157 * TRUE between receiving PACKET_END_TURN and PACKET_BEGIN_TURN
159 static bool server_busy = FALSE;
161 #ifdef FREECIV_DEBUG
162 bool hackless = FALSE;
163 #endif
165 static bool client_quitting = FALSE;
167 /**************************************************************************
168 Convert a text string from the internal to the data encoding, when it
169 is written to the network.
170 **************************************************************************/
171 static char *put_conv(const char *src, size_t *length)
173 char *out = internal_to_data_string_malloc(src);
175 if (out) {
176 *length = strlen(out);
177 return out;
178 } else {
179 *length = 0;
180 return NULL;
184 /**************************************************************************
185 Convert a text string from the data to the internal encoding when it is
186 first read from the network. Returns FALSE if the destination isn't
187 large enough or the source was bad.
188 **************************************************************************/
189 static bool get_conv(char *dst, size_t ndst,
190 const char *src, size_t nsrc)
192 char *out = data_to_internal_string_malloc(src);
193 bool ret = TRUE;
194 size_t len;
196 if (!out) {
197 dst[0] = '\0';
198 return FALSE;
201 len = strlen(out);
202 if (ndst > 0 && len >= ndst) {
203 ret = FALSE;
204 len = ndst - 1;
207 memcpy(dst, out, len);
208 dst[len] = '\0';
209 free(out);
211 return ret;
214 /**************************************************************************
215 Set up charsets for the client.
216 **************************************************************************/
217 static void charsets_init(void)
219 dio_set_put_conv_callback(put_conv);
220 dio_set_get_conv_callback(get_conv);
223 /**************************************************************************
224 This is called at program exit in any emergency. This is registered
225 as at_quick_exit() callback, so no destructor kind of actions here
226 **************************************************************************/
227 static void emergency_exit(void)
229 client_kill_server(TRUE);
232 /**************************************************************************
233 This is called at program exit.
234 **************************************************************************/
235 static void at_exit(void)
237 emergency_exit();
238 packets_deinit();
239 fc_shutdown_network();
240 update_queue_free();
241 fc_destroy_ow_mutex();
244 /**************************************************************************
245 Called only by set_client_state() below.
246 **************************************************************************/
247 static void client_game_init(void)
249 client.conn.playing = NULL;
250 client.conn.observer = FALSE;
252 game_init(FALSE);
253 attribute_init();
254 agents_init();
255 control_init();
256 link_marks_init();
257 voteinfo_queue_init();
258 server_options_init();
259 update_queue_init();
260 mapimg_init(mapimg_client_tile_known, mapimg_client_tile_terrain,
261 mapimg_client_tile_owner, mapimg_client_tile_city,
262 mapimg_client_tile_unit, mapimg_client_plrcolor_count,
263 mapimg_client_plrcolor_get);
266 /**************************************************************************
267 Called by set_client_state() and client_exit() below.
268 **************************************************************************/
269 static void client_game_free(void)
271 editgui_popdown_all();
273 mapimg_free();
274 packhand_free();
275 server_options_free();
276 voteinfo_queue_free();
277 link_marks_free();
278 control_free();
279 free_help_texts();
280 attribute_free();
281 agents_free();
282 game.client.ruleset_init = FALSE;
283 game.client.ruleset_ready = FALSE;
284 game_free();
285 /* update_queue_init() is correct at this point. The queue is reset to
286 a clean state which is also needed if the client is not connected to
287 the server! */
288 update_queue_init();
290 client.conn.playing = NULL;
291 client.conn.observer = FALSE;
294 /**************************************************************************
295 Called only by set_client_state() below. Just free what is needed to
296 change view (player target).
297 **************************************************************************/
298 static void client_game_reset(void)
300 editgui_popdown_all();
302 packhand_free();
303 link_marks_free();
304 control_free();
305 attribute_free();
306 agents_free();
308 game_reset();
309 mapimg_reset();
311 attribute_init();
312 agents_init();
313 control_init();
314 link_marks_init();
317 /**************************************************************************
318 Entry point for common client code.
319 **************************************************************************/
320 int client_main(int argc, char *argv[])
322 int i;
323 enum log_level loglevel = LOG_NORMAL;
324 int ui_options = 0;
325 bool ui_separator = FALSE;
326 char *option = NULL;
327 int fatal_assertions = -1;
328 int aii;
330 /* Load win32 post-crash debugger */
331 #ifdef FREECIV_MSWINDOWS
332 # ifndef FREECIV_NDEBUG
333 if (LoadLibrary("exchndl.dll") == NULL) {
334 # ifdef FREECIV_DEBUG
335 fprintf(stderr, "exchndl.dll could not be loaded, no crash debugger\n");
336 # endif /* FREECIV_DEBUG */
338 # endif /* FREECIV_NDEBUG */
339 #endif /* FREECIV_MSWINDOWS */
341 i_am_client(); /* Tell to libfreeciv that we are client */
343 fc_interface_init_client();
345 game.client.ruleset_init = FALSE;
347 /* Ensure that all AIs are initialized to unused state
348 * Not using ai_type_iterate as it would stop at
349 * current ai type count, ai_type_get_count(), i.e., 0 */
350 for (aii = 0; aii < FREECIV_AI_MOD_LAST; aii++) {
351 struct ai_type *ai = get_ai_type(aii);
353 init_ai(ai);
356 init_nls();
357 #ifdef ENABLE_NLS
358 (void) bindtextdomain("freeciv-nations", get_locale_dir());
359 #endif
361 registry_module_init();
362 audio_init();
363 init_character_encodings(gui_character_encoding, gui_use_transliteration);
364 #ifdef ENABLE_NLS
365 bind_textdomain_codeset("freeciv-nations", get_internal_encoding());
366 #endif
368 i = 1;
370 announce = ANNOUNCE_DEFAULT;
372 while (i < argc) {
373 if (ui_separator) {
374 argv[1 + ui_options] = argv[i];
375 ui_options++;
376 } else if (is_option("--help", argv[i])) {
377 struct cmdhelp *help = cmdhelp_new(argv[0]);
379 cmdhelp_add(help, "A",
380 /* TRANS: "Announce" is exactly what user must type, do not translate. */
381 _("Announce PROTO"),
382 _("Announce game in LAN using protocol PROTO "
383 "(IPv4/IPv6/none)"));
384 cmdhelp_add(help, "a", "autoconnect",
385 _("Skip connect dialog"));
386 #ifdef FREECIV_DEBUG
387 cmdhelp_add(help, "d",
388 /* TRANS: "debug" is exactly what user must type, do not translate. */
389 _("debug LEVEL"),
390 _("Set debug log level (%d to %d, or "
391 "%d:file1,min,max:...)"), LOG_FATAL, LOG_DEBUG,
392 LOG_DEBUG);
393 #else /* FREECIV_DEBUG */
394 cmdhelp_add(help, "d",
395 /* TRANS: "debug" is exactly what user must type, do not translate. */
396 _("debug LEVEL"),
397 _("Set debug log level (%d to %d)"),
398 LOG_FATAL, LOG_VERBOSE);
399 #endif /* FREECIV_DEBUG */
400 #ifndef FREECIV_NDEBUG
401 cmdhelp_add(help, "F",
402 /* TRANS: "Fatal" is exactly what user must type, do not translate. */
403 _("Fatal [SIGNAL]"),
404 _("Raise a signal on failed assertion"));
405 #endif /* FREECIV_NDEBUG */
406 cmdhelp_add(help, "f",
407 /* TRANS: "file" is exactly what user must type, do not translate. */
408 _("file FILE"),
409 _("Load saved game FILE"));
410 cmdhelp_add(help, "h", "help",
411 _("Print a summary of the options"));
412 #ifdef FREECIV_DEBUG
413 cmdhelp_add(help, "H", "Hackless",
414 _("Do not request hack access to local, but not spawned, server"));
415 #endif /* FREECIV_DEBUG */
416 cmdhelp_add(help, "l",
417 /* TRANS: "log" is exactly what user must type, do not translate. */
418 _("log FILE"),
419 _("Use FILE as logfile (spawned server also uses this)"));
420 cmdhelp_add(help, "M",
421 /* TRANS: "Meta" is exactly what user must type, do not translate. */
422 _("Meta HOST"),
423 _("Connect to the metaserver at HOST"));
424 cmdhelp_add(help, "n",
425 /* TRANS: "name" is exactly what user must type, do not translate. */
426 _("name NAME"),
427 _("Use NAME as username on server"));
428 cmdhelp_add(help, "p",
429 /* TRANS: "port" is exactly what user must type, do not translate. */
430 _("port PORT"),
431 _("Connect to server port PORT (usually with -a)"));
432 cmdhelp_add(help, "P",
433 /* TRANS: "Plugin" is exactly what user must type, do not translate. */
434 _("Plugin PLUGIN"),
435 _("Use PLUGIN for sound output %s"),
436 audio_get_all_plugin_names());
437 cmdhelp_add(help, "r",
438 /* TRANS: "read" is exactly what user must type, do not translate. */
439 _("read FILE"),
440 _("Read startup script FILE (for spawned server only)"));
441 cmdhelp_add(help, "s",
442 /* TRANS: "server" is exactly what user must type, do not translate. */
443 _("server HOST"),
444 _("Connect to the server at HOST (usually with -a)"));
445 cmdhelp_add(help, "S",
446 /* TRANS: "Sound" is exactly what user must type, do not translate. */
447 _("Sound FILE"),
448 _("Read sound tags from FILE"));
449 cmdhelp_add(help, "m",
450 /* TRANS: "music" is exactly what user must type, do not translate. */
451 _("music FILE"),
452 _("Read music tags from FILE"));
453 cmdhelp_add(help, "t",
454 /* TRANS: "tiles" is exactly what user must type, do not translate. */
455 _("tiles FILE"),
456 _("Use data file FILE.tilespec for tiles"));
457 cmdhelp_add(help, "v", "version",
458 _("Print the version number"));
459 cmdhelp_add(help, "w", "warnings",
460 _("Warn about deprecated modpack constructs"));
462 /* The function below prints a header and footer for the options.
463 * Furthermore, the options are sorted. */
464 cmdhelp_display(help, TRUE, TRUE, TRUE);
465 cmdhelp_destroy(help);
467 exit(EXIT_SUCCESS);
468 } else if (is_option("--version", argv[i])) {
469 fc_fprintf(stderr, "%s %s\n", freeciv_name_version(), client_string);
470 exit(EXIT_SUCCESS);
471 #ifdef FREECIV_DEBUG
472 } else if (is_option("--Hackless", argv[i])) {
473 hackless = TRUE;
474 #endif /* FREECIV_DEBUG */
475 } else if ((option = get_option_malloc("--log", argv, &i, argc, TRUE))) {
476 logfile = option;
477 #ifndef FREECIV_NDEBUG
478 } else if (is_option("--Fatal", argv[i])) {
479 if (i + 1 >= argc || '-' == argv[i + 1][0]) {
480 fatal_assertions = SIGABRT;
481 } else if (str_to_int(argv[i + 1], &fatal_assertions)) {
482 i++;
483 } else {
484 fc_fprintf(stderr, _("Invalid signal number \"%s\".\n"),
485 argv[i + 1]);
486 fc_fprintf(stderr, _("Try using --help.\n"));
487 exit(EXIT_FAILURE);
489 #endif /* FREECIV_NDEBUG */
490 } else if ((option = get_option_malloc("--read", argv, &i, argc, TRUE))) {
491 scriptfile = option;
492 } else if ((option = get_option_malloc("--file", argv, &i, argc, TRUE))) {
493 savefile = option;
494 auto_spawn = TRUE;
495 } else if ((option = get_option_malloc("--name", argv, &i, argc, FALSE))) {
496 sz_strlcpy(user_name, option);
497 free(option);
498 } else if ((option = get_option_malloc("--Meta", argv, &i, argc, FALSE))) {
499 sz_strlcpy(metaserver, option);
500 free(option);
501 } else if ((option = get_option_malloc("--Sound", argv, &i, argc, FALSE))) {
502 sz_strlcpy(sound_set_name, option);
503 free(option);
504 } else if ((option = get_option_malloc("--music", argv, &i, argc, FALSE))) {
505 sz_strlcpy(music_set_name, option);
506 free(option);
507 } else if ((option = get_option_malloc("--Plugin", argv, &i, argc, FALSE))) {
508 sz_strlcpy(sound_plugin_name, option);
509 free(option);
510 } else if ((option = get_option_malloc("--port", argv, &i, argc, FALSE))) {
511 if (!str_to_int(option, &server_port)) {
512 fc_fprintf(stderr,
513 _("Invalid port \"%s\" specified with --port option.\n"),
514 option);
515 fc_fprintf(stderr, _("Try using --help.\n"));
516 exit(EXIT_FAILURE);
518 free(option);
519 } else if ((option = get_option_malloc("--server", argv, &i, argc, FALSE))) {
520 sz_strlcpy(server_host, option);
521 free(option);
522 } else if (is_option("--autoconnect", argv[i])) {
523 auto_connect = TRUE;
524 } else if ((option = get_option_malloc("--debug", argv, &i, argc, FALSE))) {
525 if (!log_parse_level_str(option, &loglevel)) {
526 fc_fprintf(stderr,
527 _("Invalid debug level \"%s\" specified with --debug "
528 "option.\n"), option);
529 fc_fprintf(stderr, _("Try using --help.\n"));
530 exit(EXIT_FAILURE);
532 free(option);
533 } else if ((option = get_option_malloc("--tiles", argv, &i, argc, FALSE))) {
534 sz_strlcpy(forced_tileset_name, option);
535 free(option);
536 } else if ((option = get_option_malloc("--Announce", argv, &i, argc, FALSE))) {
537 if (!strcasecmp(option, "ipv4")) {
538 announce = ANNOUNCE_IPV4;
539 } else if (!strcasecmp(option, "none")) {
540 announce = ANNOUNCE_NONE;
541 #ifdef FREECIV_IPV6_SUPPORT
542 } else if (!strcasecmp(option, "ipv6")) {
543 announce = ANNOUNCE_IPV6;
544 #endif /* IPv6 support */
545 } else {
546 fc_fprintf(stderr, _("Invalid announce protocol \"%s\".\n"), option);
547 exit(EXIT_FAILURE);
549 free(option);
550 } else if (is_option("--warnings", argv[i])) {
551 deprecation_warnings_enable();
552 } else if (is_option("--", argv[i])) {
553 ui_separator = TRUE;
554 } else {
555 fc_fprintf(stderr, _("Unrecognized option: \"%s\"\n"), argv[i]);
556 exit(EXIT_FAILURE);
558 i++;
559 } /* of while */
561 if (auto_spawn && auto_connect) {
562 /* TRANS: don't translate option names */
563 fc_fprintf(stderr, _("-f/--file and -a/--autoconnect options are "
564 "incompatible\n"));
565 exit(EXIT_FAILURE);
568 /* Remove all options except those intended for the UI. */
569 argv[1 + ui_options] = NULL;
570 argc = 1 + ui_options;
572 /* disallow running as root -- too dangerous */
573 dont_run_as_root(argv[0], "freeciv_client");
575 log_init(logfile, loglevel, NULL, NULL, fatal_assertions);
576 backtrace_init();
578 /* after log_init: */
580 (void)user_username(gui_options.default_user_name, MAX_LEN_NAME);
581 if (!is_valid_username(gui_options.default_user_name)) {
582 char buf[sizeof(gui_options.default_user_name)];
584 fc_snprintf(buf, sizeof(buf), "_%s", gui_options.default_user_name);
585 if (is_valid_username(buf)) {
586 sz_strlcpy(gui_options.default_user_name, buf);
587 } else {
588 fc_snprintf(gui_options.default_user_name,
589 sizeof(gui_options.default_user_name),
590 "player%d", fc_rand(10000));
594 /* initialization */
596 game.all_connections = conn_list_new();
597 game.est_connections = conn_list_new();
599 ui_init();
600 charsets_init();
601 fc_init_network();
602 update_queue_init();
604 fc_init_ow_mutex();
606 /* register exit handler */
607 atexit(at_exit);
608 fc_at_quick_exit(emergency_exit);
610 init_our_capability();
611 init_player_dlg_common();
612 init_themes();
614 options_init();
615 options_load();
617 script_client_init();
619 if (sound_set_name[0] == '\0') {
620 sz_strlcpy(sound_set_name, gui_options.default_sound_set_name);
622 if (music_set_name[0] == '\0') {
623 sz_strlcpy(music_set_name, gui_options.default_music_set_name);
625 if (sound_plugin_name[0] == '\0') {
626 sz_strlcpy(sound_plugin_name, gui_options.default_sound_plugin_name);
628 if (server_host[0] == '\0') {
629 sz_strlcpy(server_host, gui_options.default_server_host);
630 } else if (gui_options.use_prev_server) {
631 sz_strlcpy(gui_options.default_server_host, server_host);
633 if (user_name[0] == '\0') {
634 sz_strlcpy(user_name, gui_options.default_user_name);
636 if (metaserver[0] == '\0') {
637 /* FIXME: Find a cleaner way to achieve this. */
638 /* www.cazfi.net/freeciv/metaserver/ was default metaserver
639 * over one release when meta.freeciv.org was unavailable. */
640 const char *oldaddr = "http://www.cazfi.net/freeciv/metaserver/";
642 if (0 == strcmp(gui_options.default_metaserver, oldaddr)) {
643 log_normal(_("Updating old metaserver address \"%s\"."), oldaddr);
644 sz_strlcpy(gui_options.default_metaserver, DEFAULT_METASERVER_OPTION);
645 log_normal(_("Default metaserver has been set to value \"%s\"."),
646 DEFAULT_METASERVER_OPTION);
648 if (0 == strcmp(gui_options.default_metaserver, DEFAULT_METASERVER_OPTION)) {
649 sz_strlcpy(metaserver, FREECIV_META_URL);
650 } else {
651 sz_strlcpy(metaserver, gui_options.default_metaserver);
654 if (server_port == -1) {
655 server_port = gui_options.default_server_port;
656 } else if (gui_options.use_prev_server) {
657 gui_options.default_server_port = server_port;
660 /* This seed is not saved anywhere; randoms in the client should
661 have cosmetic effects only (eg city name suggestions). --dwp */
662 fc_srand(time(NULL));
663 helpdata_init();
664 boot_help_texts();
666 fill_topo_ts_default();
668 if (forced_tileset_name[0] != '\0') {
669 tilespec_try_read(forced_tileset_name, TRUE, -1, TRUE);
670 } else {
671 tilespec_try_read(gui_options.default_tileset_name, FALSE, -1, TRUE);
674 audio_real_init(sound_set_name, music_set_name, sound_plugin_name);
675 start_menu_music("music_menu", NULL);
677 editor_init();
679 /* run gui-specific client */
680 ui_main(argc, argv);
682 /* termination */
683 client_exit();
685 /* not reached */
686 return EXIT_SUCCESS;
689 /**************************************************************************
690 Write messages from option saving to the log.
691 **************************************************************************/
692 static void log_option_save_msg(enum log_level lvl, const char *msg, ...)
694 va_list args;
696 va_start(args, msg);
697 log_va_list(lvl, msg, args);
698 va_end(args);
701 /**************************************************************************
702 Main client execution stop function. This calls ui_exit() and not the
703 other way around.
704 **************************************************************************/
705 void client_exit(void)
707 if (client_state() >= C_S_PREPARING) {
708 attribute_flush();
709 client_remove_all_cli_conn();
712 if (gui_options.save_options_on_exit) {
713 options_save(log_option_save_msg);
716 overview_free();
717 tileset_free(tileset);
719 ui_exit();
721 script_client_free();
723 editor_free();
724 options_free();
725 if (client_state() >= C_S_PREPARING) {
726 client_game_free();
729 helpdata_done(); /* client_exit() unlinks help text list */
730 conn_list_destroy(game.all_connections);
731 conn_list_destroy(game.est_connections);
733 registry_module_close();
734 free_libfreeciv();
735 free_nls();
737 backtrace_deinit();
738 log_close();
739 cmdline_option_values_free();
741 exit(EXIT_SUCCESS);
745 /**************************************************************************
746 Handle packet received from server.
747 **************************************************************************/
748 void client_packet_input(void *packet, int type)
750 if (!client.conn.established
751 && PACKET_CONN_PING != type
752 && PACKET_PROCESSING_STARTED != type
753 && PACKET_PROCESSING_FINISHED != type
754 && PACKET_SERVER_JOIN_REPLY != type
755 && PACKET_AUTHENTICATION_REQ != type
756 && PACKET_SERVER_SHUTDOWN != type
757 && PACKET_CONNECT_MSG != type
758 && PACKET_EARLY_CHAT_MSG != type) {
759 log_error("Received packet %s (%d) before establishing connection!",
760 packet_name(type), type);
761 disconnect_from_server();
762 } else if (!client_handle_packet(type, packet)) {
763 log_error("Received unknown packet (type %d) from server!", type);
764 disconnect_from_server();
768 /**************************************************************************
769 Handle user ending his/her turn.
770 **************************************************************************/
771 void user_ended_turn(void)
773 send_turn_done();
776 /**************************************************************************
777 Send information about player having finished his/her turn to server.
778 **************************************************************************/
779 void send_turn_done(void)
781 log_debug("send_turn_done() can_end_turn=%d",
782 can_end_turn());
784 if (!can_end_turn()) {
786 * The turn done button is disabled but the user may have pressed
787 * the return key.
790 if (agents_busy()) {
791 waiting_for_end_turn = TRUE;
794 return;
797 waiting_for_end_turn = FALSE;
799 attribute_flush();
801 dsend_packet_player_phase_done(&client.conn, game.info.turn);
803 update_turn_done_button_state();
806 /**************************************************************************
807 Send request for some report to server
808 **************************************************************************/
809 void send_report_request(enum report_type type)
811 dsend_packet_report_req(&client.conn, type);
814 /**************************************************************************
815 Change client state.
816 **************************************************************************/
817 void set_client_state(enum client_states newstate)
819 enum client_states oldstate = civclient_state;
820 struct player *pplayer = client_player();
822 if (auto_spawn) {
823 fc_assert(!auto_connect);
824 auto_spawn = FALSE;
825 if (!client_start_server()) {
826 log_fatal(_("Failed to start local server; aborting."));
827 exit(EXIT_FAILURE);
831 if (auto_connect && newstate == C_S_DISCONNECTED) {
832 if (oldstate == C_S_DISCONNECTED) {
833 log_fatal(_("There was an error while auto connecting; aborting."));
834 exit(EXIT_FAILURE);
835 } else {
836 start_autoconnecting_to_server();
837 auto_connect = FALSE; /* Don't try this again. */
841 if (C_S_PREPARING == newstate
842 && (client_has_player() || client_is_observer())) {
843 /* Reset the delta-state. */
844 conn_reset_delta_state(&client.conn);
847 if (oldstate == newstate) {
848 return;
851 if (oldstate == C_S_RUNNING && newstate != C_S_PREPARING) {
852 stop_style_music();
854 if (!is_client_quitting()) {
855 /* Back to menu */
856 start_menu_music("music_menu", NULL);
860 civclient_state = newstate;
862 switch (newstate) {
863 case C_S_INITIAL:
864 log_error("%d is not a valid client state to set.", C_S_INITIAL);
865 break;
867 case C_S_DISCONNECTED:
868 popdown_all_city_dialogs();
869 close_all_diplomacy_dialogs();
870 popdown_all_game_dialogs();
871 meswin_clear_older(MESWIN_CLEAR_ALL, 0);
873 if (oldstate > C_S_DISCONNECTED) {
874 unit_focus_set(NULL);
875 agents_disconnect();
876 editor_clear();
877 global_worklists_unbuild();
878 client_remove_all_cli_conn();
879 client_game_free();
880 if (oldstate > C_S_PREPARING) {
881 options_dialogs_update();
885 set_client_page(PAGE_MAIN);
886 break;
888 case C_S_PREPARING:
889 popdown_all_city_dialogs();
890 close_all_diplomacy_dialogs();
891 popdown_all_game_dialogs();
892 meswin_clear_older(MESWIN_CLEAR_ALL, 0);
894 if (oldstate < C_S_PREPARING) {
895 client_game_init();
896 } else {
897 /* From an upper state means that we didn't quit the server,
898 * so a lot of informations are still in effect. */
899 client_game_reset();
900 options_dialogs_update();
903 unit_focus_set(NULL);
905 if (get_client_page() != PAGE_SCENARIO
906 && get_client_page() != PAGE_LOAD) {
907 set_client_page(PAGE_START);
909 break;
911 case C_S_RUNNING:
912 if (oldstate == C_S_PREPARING) {
913 popdown_races_dialog();
914 stop_menu_music(); /* stop intro sound loop. */
917 init_city_report_game_data();
918 options_dialogs_set();
919 create_event(NULL, E_GAME_START, ftc_client, _("Game started."));
920 if (pplayer) {
921 research_update(research_get(pplayer));
923 role_unit_precalcs();
924 boot_help_texts(); /* reboot with player */
925 global_worklists_build();
926 can_slide = FALSE;
927 unit_focus_update();
928 can_slide = TRUE;
929 set_client_page(PAGE_GAME);
930 /* Find something sensible to display instead of the intro gfx. */
931 center_on_something();
932 free_intro_radar_sprites();
933 agents_game_start();
934 editgui_tileset_changed();
935 voteinfo_gui_update();
937 refresh_overview_canvas();
939 update_info_label(); /* get initial population right */
940 unit_focus_update();
941 update_unit_info_label(get_units_in_focus());
943 if (gui_options.auto_center_each_turn) {
944 center_on_something();
946 start_style_music();
947 break;
949 case C_S_OVER:
950 if (C_S_RUNNING == oldstate) {
951 /* Extra kludge for end-game handling of the CMA. */
952 if (pplayer && pplayer->cities) {
953 city_list_iterate(pplayer->cities, pcity) {
954 if (cma_is_city_under_agent(pcity, NULL)) {
955 cma_release_city(pcity);
957 } city_list_iterate_end;
959 popdown_all_city_dialogs();
960 close_all_diplomacy_dialogs();
961 popdown_all_game_dialogs();
962 unit_focus_set(NULL);
963 } else {
964 /* From C_S_PREPARING. */
965 init_city_report_game_data();
966 options_dialogs_set();
967 if (pplayer) {
968 research_update(research_get(pplayer));
970 role_unit_precalcs();
971 boot_help_texts(); /* reboot */
972 global_worklists_build();
973 unit_focus_set(NULL);
974 set_client_page(PAGE_GAME);
975 center_on_something();
977 refresh_overview_canvas();
979 update_info_label();
980 unit_focus_update();
981 update_unit_info_label(NULL);
983 break;
986 menus_init();
987 update_turn_done_button_state();
988 conn_list_dialog_update();
989 if (can_client_change_view()) {
990 update_map_canvas_visible();
993 /* If turn was going to change, that is now aborted. */
994 set_server_busy(FALSE);
997 /**************************************************************************
998 Return current client state.
999 **************************************************************************/
1000 enum client_states client_state(void)
1002 return civclient_state;
1005 /**************************************************************************
1006 Remove pconn from all connection lists in client, then free it.
1007 **************************************************************************/
1008 void client_remove_cli_conn(struct connection *pconn)
1010 fc_assert_msg(pconn != NULL,
1011 "Trying to remove a non existing connection");
1013 if (NULL != pconn->playing) {
1014 conn_list_remove(pconn->playing->connections, pconn);
1016 conn_list_remove(game.all_connections, pconn);
1017 conn_list_remove(game.est_connections, pconn);
1018 fc_assert_ret(pconn != &client.conn);
1019 free(pconn);
1022 /**************************************************************************
1023 Remove (and free) all connections from all connection lists in client.
1024 Assumes game.all_connections is properly maintained with all connections.
1025 **************************************************************************/
1026 void client_remove_all_cli_conn(void)
1028 fc_assert_msg(game.all_connections != NULL,
1029 "Connection list missing");
1031 while (conn_list_size(game.all_connections) > 0) {
1032 struct connection *pconn = conn_list_get(game.all_connections, 0);
1033 client_remove_cli_conn(pconn);
1037 /**************************************************************************
1038 Send attribute block.
1039 **************************************************************************/
1040 void send_attribute_block_request()
1042 send_packet_player_attribute_block(&client.conn);
1045 /**************************************************************************
1046 Wait until server has responsed to given request id.
1047 **************************************************************************/
1048 void wait_till_request_got_processed(int request_id)
1050 input_from_server_till_request_got_processed(client.conn.sock,
1051 request_id);
1054 /**************************************************************************
1055 Returns whether client is observer.
1056 **************************************************************************/
1057 bool client_is_observer(void)
1059 return client.conn.established && client.conn.observer;
1062 /* Seconds_to_turndone is the number of seconds the server has told us
1063 * are left. The timer tells exactly how much time has passed since the
1064 * server gave us that data. */
1065 static double seconds_to_turndone = 0.0;
1066 static struct timer *turndone_timer;
1068 /* The timer tells how long since server informed us about starting
1069 * turn-change activities. */
1070 static struct timer *between_turns = NULL;
1071 static bool waiting_turn_change = FALSE;
1073 /* This value shows what value the timeout label is currently showing for
1074 * the seconds-to-turndone. */
1075 static int seconds_shown_to_turndone;
1076 static int seconds_shown_to_new_turn;
1078 /**************************************************************************
1079 Reset the number of seconds to turndone from an "authentic" source.
1081 The seconds are taken as a double even though most callers will just
1082 know an integer value.
1083 **************************************************************************/
1084 void set_seconds_to_turndone(double seconds)
1086 if (current_turn_timeout() > 0) {
1087 seconds_to_turndone = seconds;
1088 turndone_timer = timer_renew(turndone_timer, TIMER_USER, TIMER_ACTIVE);
1089 timer_start(turndone_timer);
1091 /* Maybe we should do an update_timeout_label here, but it doesn't
1092 * seem to be necessary. */
1093 seconds_shown_to_turndone = ceil(seconds) + 0.1;
1097 /**************************************************************************
1098 Are we in turn-change wait state?
1099 **************************************************************************/
1100 bool is_waiting_turn_change(void)
1102 return waiting_turn_change;
1105 /**************************************************************************
1106 Start waiting of the server turn change activities.
1107 **************************************************************************/
1108 void start_turn_change_wait(void)
1110 seconds_shown_to_new_turn = ceil(game.tinfo.last_turn_change_time) + 0.1;
1111 between_turns = timer_renew(between_turns, TIMER_USER, TIMER_ACTIVE);
1112 timer_start(between_turns);
1114 waiting_turn_change = TRUE;
1117 /**************************************************************************
1118 Server is responsive again
1119 **************************************************************************/
1120 void stop_turn_change_wait(void)
1122 waiting_turn_change = FALSE;
1123 update_timeout_label();
1126 /**************************************************************************
1127 Return the number of seconds until turn-done. Don't call this unless
1128 current_turn_timeout() != 0.
1129 **************************************************************************/
1130 int get_seconds_to_turndone(void)
1132 if (current_turn_timeout() > 0) {
1133 return seconds_shown_to_turndone;
1134 } else {
1135 /* This shouldn't happen. */
1136 return FC_INFINITY;
1140 /**************************************************************************
1141 Return the number of seconds until turn-done. Don't call this unless
1142 current_turn_timeout() != 0.
1143 **************************************************************************/
1144 int get_seconds_to_new_turn(void)
1146 return seconds_shown_to_new_turn;
1149 /**************************************************************************
1150 This function should be called at least once per second. It does various
1151 updates (idle animations and timeout updates). It returns the number of
1152 seconds until it should be called again.
1153 **************************************************************************/
1154 double real_timer_callback(void)
1156 double time_until_next_call = 1.0;
1158 voteinfo_queue_check_removed();
1161 double autoconnect_time = try_to_autoconnect();
1162 time_until_next_call = MIN(time_until_next_call, autoconnect_time);
1165 if (C_S_RUNNING != client_state()) {
1166 return time_until_next_call;
1169 time_until_next_call = zoom_update(time_until_next_call);
1172 double blink_time = blink_turn_done_button();
1174 time_until_next_call = MIN(time_until_next_call, blink_time);
1177 if (get_num_units_in_focus() > 0) {
1178 double blink_time = blink_active_unit();
1180 time_until_next_call = MIN(time_until_next_call, blink_time);
1183 /* It is possible to have current_turn_timeout() > 0 but !turndone_timer,
1184 * in the first moments after the timeout is set. */
1185 if (current_turn_timeout() > 0 && turndone_timer) {
1186 double seconds = seconds_to_turndone - timer_read_seconds(turndone_timer);
1187 int iseconds = ceil(seconds) + 0.1; /* Turn should end right on 0. */
1189 if (iseconds < seconds_shown_to_turndone) {
1190 seconds_shown_to_turndone = iseconds;
1191 update_timeout_label();
1194 time_until_next_call = MIN(time_until_next_call,
1195 seconds - floor(seconds) + 0.001);
1197 if (waiting_turn_change) {
1198 double seconds = game.tinfo.last_turn_change_time - timer_read_seconds(between_turns);
1199 int iseconds = ceil(seconds) + 0.1; /* Turn should end right on 0. */
1201 if (iseconds < game.tinfo.last_turn_change_time) {
1202 seconds_shown_to_new_turn = iseconds;
1203 update_timeout_label();
1208 static long counter = 0;
1210 counter++;
1212 if (gui_options.heartbeat_enabled && (counter % (20 * 10) == 0)) {
1213 send_packet_client_heartbeat(&client.conn);
1217 /* Make sure we wait at least 50 ms, otherwise we may not give any other
1218 * code time to run. */
1219 return MAX(time_until_next_call, 0.05);
1222 /**************************************************************************
1223 Returns TRUE iff the client can control player.
1224 **************************************************************************/
1225 bool can_client_control(void)
1227 return (NULL != client.conn.playing
1228 && !client_is_observer());
1231 /**************************************************************************
1232 Returns TRUE iff the client can issue orders (such as giving unit
1233 commands). This function should be called each time before allowing the
1234 user to give an order.
1235 **************************************************************************/
1236 bool can_client_issue_orders(void)
1238 return (can_client_control()
1239 && C_S_RUNNING == client_state());
1242 /**************************************************************************
1243 Returns TRUE iff the client can do diplomatic meetings with another
1244 given player.
1245 **************************************************************************/
1246 bool can_meet_with_player(const struct player *pplayer)
1248 return (can_client_issue_orders()
1249 /* && NULL != client.conn.playing (above) */
1250 && could_meet_with_player(client.conn.playing, pplayer));
1253 /**************************************************************************
1254 Returns TRUE iff the client can get intelligence from another
1255 given player.
1256 **************************************************************************/
1257 bool can_intel_with_player(const struct player *pplayer)
1259 return (client_is_observer()
1260 || (NULL != client.conn.playing
1261 && could_intel_with_player(client.conn.playing, pplayer)));
1264 /**************************************************************************
1265 Return TRUE if the client can change the view; i.e. if the mapview is
1266 active. This function should be called each time before allowing the
1267 user to do mapview actions.
1268 **************************************************************************/
1269 bool can_client_change_view(void)
1271 return ((NULL != client.conn.playing || client_is_observer())
1272 && (C_S_RUNNING == client_state()
1273 || C_S_OVER == client_state()));
1276 /**************************************************************************
1277 Sets if server is considered busy. Currently it is considered busy
1278 between turns.
1279 **************************************************************************/
1280 void set_server_busy(bool busy)
1282 if (busy != server_busy) {
1283 /* server_busy value will change */
1284 server_busy = busy;
1286 /* This may mean that we have to change from or to wait cursor */
1287 control_mouse_cursor(NULL);
1291 /**************************************************************************
1292 Returns if server is considered busy at the moment
1293 **************************************************************************/
1294 bool is_server_busy(void)
1296 return server_busy;
1299 /****************************************************************************
1300 Returns whether client is global observer
1301 ****************************************************************************/
1302 bool client_is_global_observer(void)
1304 return client.conn.playing == NULL && client.conn.observer == TRUE;
1307 /****************************************************************************
1308 Returns number of player attached to client.
1309 ****************************************************************************/
1310 int client_player_number(void)
1312 if (client.conn.playing == NULL) {
1313 return -1;
1315 return player_number(client.conn.playing);
1318 /****************************************************************************
1319 Either controlling or observing.
1320 ****************************************************************************/
1321 bool client_has_player(void)
1323 return client.conn.playing != NULL;
1326 /****************************************************************************
1327 Either controlling or observing.
1328 ****************************************************************************/
1329 struct player *client_player(void)
1331 return client.conn.playing;
1334 /****************************************************************************
1335 Return the vision of the player on a tile. Client version of
1336 ./server/maphand/map_is_known_and_seen().
1337 ****************************************************************************/
1338 static bool client_map_is_known_and_seen(const struct tile *ptile,
1339 const struct player *pplayer,
1340 enum vision_layer vlayer)
1342 return dbv_isset(&pplayer->client.tile_vision[vlayer], tile_index(ptile));
1345 /***************************************************************************
1346 Returns the id of the city the player believes exists at 'ptile'.
1347 ***************************************************************************/
1348 static int client_plr_tile_city_id_get(const struct tile *ptile,
1349 const struct player *pplayer)
1351 struct city *pcity = tile_city(ptile);
1353 /* Can't look up what other players think. */
1354 fc_assert(pplayer == client_player());
1356 return pcity ? pcity->id : IDENTITY_NUMBER_ZERO;
1359 /***************************************************************************
1360 Returns the id of the server setting with the specified name.
1361 ***************************************************************************/
1362 static server_setting_id client_ss_by_name(const char *name)
1364 struct option *pset = optset_option_by_name(server_optset, name);
1366 if (pset) {
1367 return option_number(pset);
1368 } else {
1369 log_error("No server setting named %s exists.", name);
1370 return SERVER_SETTING_NONE;
1374 /***************************************************************************
1375 Returns the name of the server setting with the specified id.
1376 ***************************************************************************/
1377 static const char *client_ss_name_get(server_setting_id id)
1379 struct option *pset = optset_option_by_number(server_optset, id);
1381 if (pset) {
1382 return option_name(pset);
1383 } else {
1384 log_error("No server setting with the id %d exists.", id);
1385 return NULL;
1389 /***************************************************************************
1390 Returns the type of the server setting with the specified id.
1391 ***************************************************************************/
1392 static enum sset_type client_ss_type_get(server_setting_id id)
1394 enum option_type opt_type;
1395 struct option *pset = optset_option_by_number(server_optset, id);
1397 if (!pset) {
1398 log_error("No server setting with the id %d exists.", id);
1399 return sset_type_invalid();
1402 opt_type = option_type(pset);
1404 /* The option type isn't client only. */
1405 fc_assert_ret_val_msg((opt_type != OT_FONT
1406 && opt_type != OT_COLOR
1407 && opt_type != OT_VIDEO_MODE),
1408 sset_type_invalid(),
1409 "%s is a client option type but not a server "
1410 "setting type",
1411 option_type_name(option_type(pset)));
1413 /* The option type is valid. */
1414 fc_assert_ret_val(sset_type_is_valid((enum sset_type)opt_type),
1415 sset_type_invalid());
1417 /* Each server setting type value equals the client option type value with
1418 * the same meaning. */
1419 FC_STATIC_ASSERT((enum sset_type)OT_BOOLEAN == SST_BOOL
1420 && (enum sset_type)OT_INTEGER == SST_INT
1421 && (enum sset_type)OT_STRING == SST_STRING
1422 && (enum sset_type)OT_ENUM == SST_ENUM
1423 && (enum sset_type)OT_BITWISE == SST_BITWISE
1424 && SST_COUNT == (enum sset_type)5,
1425 server_setting_type_not_equal_to_client_option_type);
1427 /* Exploit the fact that each server setting type value corresponds to the
1428 * client option type value with the same meaning. */
1429 return (enum sset_type)opt_type;
1432 /***************************************************************************
1433 Returns the value of the boolean server setting with the specified id.
1434 ***************************************************************************/
1435 static bool client_ss_val_bool_get(server_setting_id id)
1437 struct option *pset = optset_option_by_number(server_optset, id);
1439 if (pset) {
1440 return option_bool_get(pset);
1441 } else {
1442 log_error("No server setting with the id %d exists.", id);
1443 return FALSE;
1447 /***************************************************************
1448 Initialize client specific functions.
1449 ***************************************************************/
1450 static void fc_interface_init_client(void)
1452 struct functions *funcs = fc_interface_funcs();
1454 funcs->server_setting_by_name = client_ss_by_name;
1455 funcs->server_setting_name_get = client_ss_name_get;
1456 funcs->server_setting_type_get = client_ss_type_get;
1457 funcs->server_setting_val_bool_get = client_ss_val_bool_get;
1458 funcs->create_extra = NULL;
1459 funcs->destroy_extra = NULL;
1460 funcs->player_tile_vision_get = client_map_is_known_and_seen;
1461 funcs->player_tile_city_id_get = client_plr_tile_city_id_get;
1462 funcs->gui_color_free = color_free;
1464 /* Keep this function call at the end. It checks if all required functions
1465 are defined. */
1466 fc_interface_init();
1469 /***************************************************************************
1470 Helper function for the mapimg module - tile knowledge.
1471 ****************************************************************************/
1472 static enum known_type mapimg_client_tile_known(const struct tile *ptile,
1473 const struct player *pplayer,
1474 bool knowledge)
1476 if (client_is_global_observer()) {
1477 return TILE_KNOWN_SEEN;
1480 return tile_get_known(ptile, pplayer);
1483 /****************************************************************************
1484 Helper function for the mapimg module - tile terrain.
1485 ****************************************************************************/
1486 static struct terrain *
1487 mapimg_client_tile_terrain(const struct tile *ptile,
1488 const struct player *pplayer, bool knowledge)
1490 return tile_terrain(ptile);
1493 /****************************************************************************
1494 Helper function for the mapimg module - tile owner.
1495 ****************************************************************************/
1496 static struct player *mapimg_client_tile_owner(const struct tile *ptile,
1497 const struct player *pplayer,
1498 bool knowledge)
1500 return tile_owner(ptile);
1503 /****************************************************************************
1504 Helper function for the mapimg module - city owner.
1505 ****************************************************************************/
1506 static struct player *mapimg_client_tile_city(const struct tile *ptile,
1507 const struct player *pplayer,
1508 bool knowledge)
1510 struct city *pcity = tile_city(ptile);
1512 if (!pcity) {
1513 return NULL;
1516 return city_owner(tile_city(ptile));
1519 /****************************************************************************
1520 Helper function for the mapimg module - unit owner.
1521 ****************************************************************************/
1522 static struct player *mapimg_client_tile_unit(const struct tile *ptile,
1523 const struct player *pplayer,
1524 bool knowledge)
1526 int unit_count = unit_list_size(ptile->units);
1528 if (unit_count == 0) {
1529 return NULL;
1532 return unit_owner(unit_list_get(ptile->units, 0));
1535 /****************************************************************************
1536 Helper function for the mapimg module - number of player colors.
1537 ****************************************************************************/
1538 static int mapimg_client_plrcolor_count(void)
1540 return player_count();
1543 /****************************************************************************
1544 Helper function for the mapimg module - one player color. For the client
1545 only the colors of the defined players are shown.
1546 ****************************************************************************/
1547 static struct rgbcolor *mapimg_client_plrcolor_get(int i)
1549 int count = 0;
1551 if (0 > i || i > player_count()) {
1552 return NULL;
1555 players_iterate(pplayer) {
1556 if (count == i) {
1557 return pplayer->rgb;
1559 count++;
1560 } players_iterate_end;
1562 return NULL;
1565 /****************************************************************************
1566 Is the client marked as one going down?
1567 ****************************************************************************/
1568 bool is_client_quitting(void)
1570 return client_quitting;
1573 /****************************************************************************
1574 Mark client as one going to quit as soon as possible,
1575 ****************************************************************************/
1576 void start_quitting(void)
1578 client_quitting = TRUE;