/**************************************************************** handle response (by the server) if the client has got hack or not. *****************************************************************/ void handle_single_want_hack_reply(bool you_have_hack) { /* remove challenge file */ if (challenge_fullname[0] != '\0') { if (remove(challenge_fullname) == -1) { freelog(LOG_ERROR, "Couldn't remove temporary file: %s", challenge_fullname); } challenge_fullname[0] = '\0'; } if (you_have_hack) { output_window_append(FTC_CLIENT_INFO, NULL, _("Established control over the server. " "You have command access level 'hack'.")); client_has_hack = TRUE; } else if (is_server_running()) { /* only output this if we started the server and we NEED hack */ output_window_append(FTC_CLIENT_INFO, NULL, _("Failed to obtain the required access " "level to take control of the server. " "The server will now be shutdown.")); client_kill_server(TRUE); } }
/**************************************************************************** Callback for Connect button. ****************************************************************************/ void connectdlg_connect_callback(Widget w, XtPointer client_data, XtPointer call_data) { XtPointer pxp; char errbuf[512]; struct packet_authentication_reply reply; switch (connection_status) { case LOGIN_TYPE: XtVaGetValues(connectdlg_host_text, XtNstring, &pxp, NULL); sz_strlcpy(server_host, (char *)pxp); XtVaGetValues(connectdlg_port_text, XtNstring, &pxp, NULL); sscanf((char *)pxp, "%d", &server_port); XtVaGetValues(connectdlg_login_text, XtNstring, &pxp, NULL); sz_strlcpy(user_name, (char *)pxp); if (connect_to_server(user_name, server_host, server_port, errbuf, sizeof(errbuf)) != -1) { popup_start_page(); connectdlg_destroy(); XtSetSensitive(toplevel, True); return; } else { XtVaSetValues(connectdlg_message_label, XtNlabel, errbuf, NULL); output_window_append(ftc_client, errbuf); } break; case NEW_PASSWORD_TYPE: XtVaGetValues(connectdlg_password_text, XtNstring, &pxp, NULL); sz_strlcpy(password, (char *)pxp); XtVaGetValues(connectdlg_verify_text, XtNstring, &pxp, NULL); sz_strlcpy(reply.password, (char *)pxp); if (strncmp(reply.password, password, MAX_LEN_NAME) == 0) { password[0] = '\0'; send_packet_authentication_reply(&client.conn, &reply); XtVaSetValues(connectdlg_message_label, XtNlabel, "", NULL); XtVaSetValues(connectdlg_password_text, XtNsensitive, False, NULL); XtVaSetValues(connectdlg_verify_text, XtNsensitive, False, NULL); } else { XtVaSetValues(connectdlg_password_text, XtNstring, "", NULL); XtVaSetValues(connectdlg_verify_text, XtNstring, "", NULL); XtVaSetValues(connectdlg_message_label, XtNlabel, _("Passwords don't match, enter password."), NULL); output_window_append(ftc_client, _("Passwords don't match, enter password.")); } break; case ENTER_PASSWORD_TYPE: XtVaGetValues(connectdlg_verify_text, XtNstring, &pxp, NULL); sz_strlcpy(reply.password, (char *)pxp); send_packet_authentication_reply(&client.conn, &reply); XtVaSetValues(connectdlg_message_label, XtNlabel, "", NULL); XtVaSetValues(connectdlg_password_text, XtNsensitive, False, NULL); XtVaSetValues(connectdlg_verify_text, XtNsensitive, False, NULL); break; case WAITING_TYPE: break; } }
/**************************************************************************** Standard welcome message. ****************************************************************************/ void chat_welcome_message(void) { output_window_append(ftc_any, _("Freeciv is free software and you are " "welcome to distribute copies of it " "under certain conditions;")); output_window_append(ftc_any, _("See the \"Copying\" item on the " "Help menu.")); output_window_append(ftc_any, _("Now ... Go give 'em hell!")); }
/************************************************************************** Popup a dialog to display connection message from server. **************************************************************************/ void popup_connect_msg(const char *headline, const char *message) { /* FIXME: Needs proper implementation. * Now just puts to chat window so message is not completely lost. */ output_window_append(ftc_client, message); }
/************************************************************************** Add a line of text to the output ("chatline") window. The text is constructed in printf style. **************************************************************************/ void output_window_vprintf(const struct ft_color color, const char *format, va_list args) { char featured_text[MAX_LEN_MSG]; fc_vsnprintf(featured_text, sizeof(featured_text), format, args); output_window_append(color, featured_text); }
/************************************************************************** Writes the supplied string into the file defined by the variable 'default_chat_logfile'. **************************************************************************/ void write_chatline_content(const char *txt) { FILE *fp = fc_fopen(default_chat_logfile, "w"); char buf[512]; fc_snprintf(buf, sizeof(buf), _("Exporting output window to '%s' ..."), default_chat_logfile); output_window_append(ftc_client, buf); if (fp) { fputs(txt, fp); fclose(fp); output_window_append(ftc_client, _("Export complete.")); } else { output_window_append(ftc_client, _("Export failed, couldn't write to file.")); } }
/************************************************************************** ... **************************************************************************/ static void close_socket_callback(struct connection *pc) { close_socket_nomessage(pc); /* If we lost connection to the internal server - kill him */ client_kill_server(TRUE); freelog(LOG_ERROR, "Lost connection to server!"); output_window_append(FTC_CLIENT_INFO, NULL, _("Lost connection to server!")); if (with_ggz) { client_exit(); } }
/************************************************************************** Make an attempt to autoconnect to the server. It returns number of seconds it should be called again. **************************************************************************/ double try_to_autoconnect(void) { char errbuf[512]; static int count = 0; #ifndef WIN32_NATIVE static int warning_shown = 0; #endif if (!autoconnecting) { return FC_INFINITY; } count++; if (count >= MAX_AUTOCONNECT_ATTEMPTS) { freelog(LOG_FATAL, _("Failed to contact server \"%s\" at port " "%d as \"%s\" after %d attempts"), server_host, server_port, user_name, count); exit(EXIT_FAILURE); } switch (try_to_connect(user_name, errbuf, sizeof(errbuf))) { case 0: /* Success! */ /* Don't call me again */ autoconnecting = FALSE; return FC_INFINITY; #ifndef WIN32_NATIVE /* See PR#4042 for more info on issues with try_to_connect() and errno. */ case ECONNREFUSED: /* Server not available (yet) */ if (!warning_shown) { freelog(LOG_ERROR, "Connection to server refused. " "Please start the server."); output_window_append(FTC_CLIENT_INFO, NULL, _("Connection to server refused. " "Please start the server.")); warning_shown = 1; } /* Try again in 0.5 seconds */ return 0.001 * AUTOCONNECT_INTERVAL; #endif default: /* All other errors are fatal */ freelog(LOG_FATAL, _("Error contacting server \"%s\" at port %d " "as \"%s\":\n %s\n"), server_host, server_port, user_name, errbuf); exit(EXIT_FAILURE); } }
/**************************************************************** handle response (by the server) if the client has got hack or not. *****************************************************************/ void handle_single_want_hack_reply(bool you_have_hack) { /* remove challenge file */ if (challenge_fullname[0] != '\0') { if (fc_remove(challenge_fullname) == -1) { log_error("Couldn't remove temporary file: %s", challenge_fullname); } challenge_fullname[0] = '\0'; } if (you_have_hack) { output_window_append(ftc_client, _("Established control over the server. " "You have command access level 'hack'.")); client_has_hack = TRUE; } else if (is_server_running()) { /* only output this if we started the server and we NEED hack */ output_window_append(ftc_client, _("Failed to obtain the required access " "level to take control of the server. " "Attempting to shut down server.")); client_kill_server(TRUE); } }
/**************************************************************************** Callback function for when there's an error in the server scan. ****************************************************************************/ void server_scan_error(struct server_scan *scan, const char *message) { output_window_append(ftc_client, message); log_normal("%s", message); switch (server_scan_get_type(scan)) { case SERVER_SCAN_LOCAL: server_scan_finish(lan_scan); lan_scan = NULL; break; case SERVER_SCAN_GLOBAL: server_scan_finish(meta_scan); meta_scan = NULL; break; case SERVER_SCAN_LAST: break; } }
/************************************************************************** ... **************************************************************************/ void players_meet_callback(Widget w, XtPointer client_data, XtPointer call_data) { XawListReturnStruct *ret = XawListShowCurrent(players_list); if (ret->list_index != XAW_LIST_NONE) { int player_index = list_index_to_player_index[ret->list_index]; struct player *pplayer = player_by_number(player_index); if (can_meet_with_player(pplayer)) { dsend_packet_diplomacy_init_meeting_req(&client.conn, player_index); } else { output_window_append(ftc_client, _("You need an embassy to establish" " a diplomatic meeting.")); } } }
/************************************************************************** ... **************************************************************************/ void disconnect_from_server(void) { const bool force = !client.conn.used; attribute_flush(); /* If it's internal server - kill him * We assume that we are always connected to the internal server */ if (!force) { client_kill_server(FALSE); } close_socket_nomessage(&client.conn); if (force) { client_kill_server(TRUE); } output_window_append(ftc_client, _("Disconnected from server.")); if (with_ggz) { client_exit(); } if (save_options_on_exit) { options_save(); } }
/************************************************************************** ... **************************************************************************/ void clear_output_window(void) { SetWindowText(logoutput_win,""); output_window_append(ftc_client, _("Cleared output window.")); }
/************************************************************************** Make a chat link at the current position or make the current selection clickable. **************************************************************************/ void inputline_make_chat_link(struct tile *ptile, bool unit) { char buf[MAX_LEN_MSG]; GtkWidget *entry = toolkit.entry; GtkEditable *editable = GTK_EDITABLE(entry); gint start_pos, end_pos; gchar *chars; struct unit *punit; /* Get the target. */ if (unit) { punit = find_visible_unit(ptile); if (!punit) { output_window_append(ftc_client, _("No visible unit on this tile.")); return; } } else { punit = NULL; } if (gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) { /* There is a selection, make it clickable. */ gpointer target; enum text_link_type type; chars = gtk_editable_get_chars(editable, start_pos, end_pos); if (punit) { type = TLT_UNIT; target = punit; } else if (tile_city(ptile)) { type = TLT_CITY; target = tile_city(ptile); } else { type = TLT_TILE; target = ptile; } if (0 != featured_text_apply_tag(chars, buf, sizeof(buf), TTT_LINK, 0, FT_OFFSET_UNSET, type, target)) { /* Replace the selection. */ gtk_editable_delete_text(editable, start_pos, end_pos); end_pos = start_pos; gtk_editable_insert_text(editable, buf, -1, &end_pos); gtk_widget_grab_focus(entry); gtk_editable_select_region(editable, start_pos, end_pos); } } else { /* Just insert the link at the current position. */ start_pos = gtk_editable_get_position(editable); end_pos = start_pos; chars = gtk_editable_get_chars(editable, MAX(start_pos - 1, 0), start_pos + 1); if (punit) { sz_strlcpy(buf, unit_link(punit)); } else if (tile_city(ptile)) { sz_strlcpy(buf, city_link(tile_city(ptile))); } else { sz_strlcpy(buf, tile_link(ptile)); } if (start_pos > 0 && strlen(chars) > 0 && chars[0] != ' ') { /* Maybe insert an extra space. */ gtk_editable_insert_text(editable, " ", 1, &end_pos); } gtk_editable_insert_text(editable, buf, -1, &end_pos); if (chars[start_pos > 0 ? 1 : 0] != '\0' && chars[start_pos > 0 ? 1 : 0] != ' ') { /* Maybe insert an extra space. */ gtk_editable_insert_text(editable, " ", 1, &end_pos); } gtk_widget_grab_focus(entry); gtk_editable_set_position(editable, end_pos); } g_free(chars); }
/************************************************************************** ... **************************************************************************/ void clear_output_window(void) { SetWindowText(logoutput_win,""); output_window_append(FTC_CLIENT_INFO, NULL, _("Cleared output window.")); }
/************************************************************************** Click a link. **************************************************************************/ static gboolean event_after(GtkWidget *text_view, GdkEventButton *event) { GtkTextIter start, end, iter; GtkTextBuffer *buffer; GSList *tags, *tagp; gint x, y; struct tile *ptile = NULL; if (event->type != GDK_BUTTON_RELEASE || event->button != 1) { return FALSE; } buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); /* We shouldn't follow a link if the user has selected something. */ gtk_text_buffer_get_selection_bounds(buffer, &start, &end); if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) { return FALSE; } gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW (text_view), GTK_TEXT_WINDOW_WIDGET, event->x, event->y, &x, &y); gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y); if ((tags = gtk_text_iter_get_tags(&iter))) { for (tagp = tags; tagp; tagp = tagp->next) { GtkTextTag *tag = tagp->data; enum text_link_type type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "type")); if (type != 0) { /* This is a link. */ int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id")); ptile = NULL; /* Real type is type - 1. * See comment in apply_text_tag() for g_object_set_data(). */ type--; switch (type) { case TLT_CITY: { struct city *pcity = game_city_by_number(id); if (pcity) { ptile = client_city_tile(pcity); } else { output_window_append(ftc_client, _("This city isn't known!")); } } break; case TLT_TILE: ptile = index_to_tile(id); if (!ptile) { output_window_append(ftc_client, _("This tile doesn't exist in this game!")); } break; case TLT_UNIT: { struct unit *punit = game_unit_by_number(id); if (punit) { ptile = unit_tile(punit); } else { output_window_append(ftc_client, _("This unit isn't known!")); } } break; } if (ptile) { center_tile_mapcanvas(ptile); link_mark_restore(type, id); gtk_widget_grab_focus(GTK_WIDGET(map_canvas)); } } } g_slist_free(tags); } return FALSE; }
/**************************************************************** forks a server if it can. returns FALSE is we find we couldn't start the server. *****************************************************************/ bool client_start_server(void) { #if !defined(HAVE_WORKING_FORK) && !defined(WIN32_NATIVE) /* Can't do much without fork */ return FALSE; #else /* HAVE_WORKING_FORK || WIN32_NATIVE */ char buf[512]; int connect_tries = 0; # ifdef WIN32_NATIVE STARTUPINFO si; PROCESS_INFORMATION pi; char savesdir[MAX_LEN_PATH]; char scensdir[MAX_LEN_PATH]; char options[512]; char cmdline1[512]; char cmdline2[512]; char cmdline3[512]; char cmdline4[512]; char logcmdline[512]; char scriptcmdline[512]; char savescmdline[512]; char scenscmdline[512]; # endif /* WIN32_NATIVE */ #ifdef IPV6_SUPPORT /* We want port that is free in IPv4 even if we (the client) have * IPv6 support. In the unlikely case that local server is IPv4-only * (meaning that it has to be from different build than client) we * have to give port that it can use. IPv6-enabled client would first * try same port in IPv6 and if that fails, fallback to IPv4 too. */ enum fc_addr_family family = FC_ADDR_IPV4; #else enum fc_addr_family family = FC_ADDR_IPV4; #endif /* IPV6_SUPPORT */ /* only one server (forked from this client) shall be running at a time */ /* This also resets client_has_hack. */ client_kill_server(TRUE); output_window_append(ftc_client, _("Starting server...")); /* find a free port */ internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT, family); # ifdef HAVE_WORKING_FORK server_pid = fork(); if (server_pid == 0) { int fd, argc = 0; const int max_nargs = 18; char *argv[max_nargs + 1], port_buf[32]; /* inside the child */ /* Set up the command-line parameters. */ fc_snprintf(port_buf, sizeof(port_buf), "%d", internal_server_port); argv[argc++] = "freeciv-server"; argv[argc++] = "-p"; argv[argc++] = port_buf; argv[argc++] = "--bind"; argv[argc++] = "localhost"; argv[argc++] = "-q"; argv[argc++] = "1"; argv[argc++] = "-e"; argv[argc++] = "--saves"; argv[argc++] = "~/.freeciv/saves"; argv[argc++] = "--scenarios"; argv[argc++] = "~/.freeciv/scenarios"; if (logfile) { argv[argc++] = "--debug"; argv[argc++] = "3"; argv[argc++] = "--log"; argv[argc++] = logfile; } if (scriptfile) { argv[argc++] = "--read"; argv[argc++] = scriptfile; } argv[argc] = NULL; fc_assert(argc <= max_nargs); /* avoid terminal spam, but still make server output available */ fclose(stdout); fclose(stderr); /* FIXME: include the port to avoid duplication? */ if (logfile) { fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644); if (fd != 1) { dup2(fd, 1); } if (fd != 2) { dup2(fd, 2); } fchmod(1, 0644); } /* If it's still attatched to our terminal, things get messed up, but freeciv-server needs *something* */ fclose(stdin); fd = open("/dev/null", O_RDONLY); if (fd != 0) { dup2(fd, 0); } /* these won't return on success */ #ifdef DEBUG /* Search under current directory (what ever that happens to be) * only in debug builds. This allows running freeciv directly from build * tree, but could be considered security risk in release builds. */ execvp("./fcser", argv); execvp("./server/freeciv-server", argv); #endif /* DEBUG */ execvp(BINDIR "/freeciv-server", argv); execvp("freeciv-server", argv); /* This line is only reached if freeciv-server cannot be started, * so we kill the forked process. * Calling exit here is dangerous due to X11 problems (async replies) */ _exit(1); } # else /* HAVE_WORKING_FORK */ # ifdef WIN32_NATIVE if (logfile) { loghandle = CreateFile(logfile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); } ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdOutput = loghandle; si.hStdInput = INVALID_HANDLE_VALUE; si.hStdError = loghandle; si.dwFlags = STARTF_USESTDHANDLES; /* Set up the command-line parameters. */ logcmdline[0] = 0; scriptcmdline[0] = 0; /* the server expects command line arguments to be in local encoding */ if (logfile) { char *logfile_in_local_encoding = internal_to_local_string_malloc(logfile); fc_snprintf(logcmdline, sizeof(logcmdline), " --debug 3 --log %s", logfile_in_local_encoding); free(logfile_in_local_encoding); } if (scriptfile) { char *scriptfile_in_local_encoding = internal_to_local_string_malloc(scriptfile); fc_snprintf(scriptcmdline, sizeof(scriptcmdline), " --read %s", scriptfile_in_local_encoding); free(scriptfile_in_local_encoding); } interpret_tilde(savesdir, sizeof(savesdir), "~/.freeciv/saves"); internal_to_local_string_buffer(savesdir, savescmdline, sizeof(savescmdline)); interpret_tilde(scensdir, sizeof(scensdir), "~/.freeciv/scenarios"); internal_to_local_string_buffer(scensdir, scenscmdline, sizeof(scenscmdline)); fc_snprintf(options, sizeof(options), "-p %d --bind localhost -q 1 -e%s%s --saves \"%s\" " "--scenarios \"%s\"", internal_server_port, logcmdline, scriptcmdline, savescmdline, scenscmdline); fc_snprintf(cmdline1, sizeof(cmdline1), "./fcser %s", options); fc_snprintf(cmdline2, sizeof(cmdline2), "./server/freeciv-server %s", options); fc_snprintf(cmdline3, sizeof(cmdline3), BINDIR "/freeciv-server %s", options); fc_snprintf(cmdline4, sizeof(cmdline4), "freeciv-server %s", options); if ( #ifdef DEBUG !CreateProcess(NULL, cmdline1, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) && !CreateProcess(NULL, cmdline2, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) && #endif /* DEBUG */ !CreateProcess(NULL, cmdline3, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) && !CreateProcess(NULL, cmdline4, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { output_window_append(ftc_client, _("Couldn't start the server.")); output_window_append(ftc_client, _("You'll have to start one manually. Sorry...")); return FALSE; } server_process = pi.hProcess; # endif /* WIN32_NATIVE */ # endif /* HAVE_WORKING_FORK */ /* a reasonable number of tries */ while (connect_to_server(user_name, "localhost", internal_server_port, buf, sizeof(buf)) == -1) { fc_usleep(WAIT_BETWEEN_TRIES); #ifdef HAVE_WORKING_FORK #ifndef WIN32_NATIVE if (waitpid(server_pid, NULL, WNOHANG) != 0) { break; } #endif /* WIN32_NATIVE */ #endif /* HAVE_WORKING_FORK */ if (connect_tries++ > NUMBER_OF_TRIES) { break; } } /* weird, but could happen, if server doesn't support new startup stuff * capabilities won't help us here... */ if (!client.conn.used) { /* possible that server is still running. kill it */ client_kill_server(TRUE); output_window_append(ftc_client, _("Couldn't connect to the server.")); output_window_append(ftc_client, _("We probably couldn't start it from here.")); output_window_append(ftc_client, _("You'll have to start one manually. Sorry...")); return FALSE; } /* We set the topology to match the view. * * When a typical player launches a game, he wants the map orientation to * match the tileset orientation. So if you use an isometric tileset you * get an iso-map and for a classic tileset you get a classic map. In * both cases the map wraps in the X direction by default. * * This works with hex maps too now. A hex map always has * tileset_is_isometric(tileset) return TRUE. An iso-hex map has * tileset_hex_height(tileset) != 0, while a non-iso hex map * has tileset_hex_width(tileset) != 0. * * Setting the option here is a bit of a hack, but so long as the client * has sufficient permissions to do so (it doesn't have HACK access yet) it * is safe enough. Note that if you load a savegame the topology will be * set but then overwritten during the load. * * Don't send it now, it will be sent to the server when receiving the * server setting infos. */ { char buf[16]; fc_strlcpy(buf, "WRAPX", sizeof(buf)); if (tileset_is_isometric(tileset) && 0 == tileset_hex_height(tileset)) { fc_strlcat(buf, "|ISO", sizeof(buf)); } if (0 < tileset_hex_width(tileset) || 0 < tileset_hex_height(tileset)) { fc_strlcat(buf, "|HEX", sizeof(buf)); } desired_settable_option_update("topology", buf, FALSE); } return TRUE; #endif /* HAVE_WORKING_FORK || WIN32_NATIVE */ }
/**************************************************************** forks a server if it can. returns FALSE is we find we couldn't start the server. This is so system-intensive that it's *nix only. VMS and Windows code will come later *****************************************************************/ bool client_start_server(void) { #if !defined(HAVE_WORKING_FORK) && !defined(WIN32_NATIVE) /* Can't do much without fork */ return FALSE; #else /* HAVE_WORKING_FORK || WIN32_NATIVE */ char buf[512]; int connect_tries = 0; # ifdef WIN32_NATIVE STARTUPINFO si; PROCESS_INFORMATION pi; char savesdir[MAX_LEN_PATH]; char options[512]; char cmdline1[512]; char cmdline2[512]; char cmdline3[512]; char logcmdline[512]; char scriptcmdline[512]; # endif /* WIN32_NATIVE */ /* only one server (forked from this client) shall be running at a time */ /* This also resets client_has_hack. */ client_kill_server(TRUE); output_window_append(FTC_CLIENT_INFO, NULL, _("Starting server...")); /* find a free port */ internal_server_port = find_next_free_port(DEFAULT_SOCK_PORT); # ifdef HAVE_WORKING_FORK server_pid = fork(); if (server_pid == 0) { int fd, argc = 0; const int max_nargs = 13; char *argv[max_nargs + 1], port_buf[32]; /* inside the child */ /* Set up the command-line parameters. */ my_snprintf(port_buf, sizeof(port_buf), "%d", internal_server_port); argv[argc++] = "civserver"; argv[argc++] = "-p"; argv[argc++] = port_buf; argv[argc++] = "-q"; argv[argc++] = "1"; argv[argc++] = "-e"; argv[argc++] = "--saves"; argv[argc++] = "~/.freeciv/saves"; argv[argc++] = "--scenarios"; argv[argc++] = "~/.freeciv/scenarios"; if (logfile) { argv[argc++] = "--debug"; argv[argc++] = "3"; argv[argc++] = "--log"; argv[argc++] = logfile; } if (scriptfile) { argv[argc++] = "--read"; argv[argc++] = scriptfile; } argv[argc] = NULL; assert(argc <= max_nargs); /* avoid terminal spam, but still make server output available */ fclose(stdout); fclose(stderr); /* FIXME: include the port to avoid duplication? */ if (logfile) { fd = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644); if (fd != 1) { dup2(fd, 1); } if (fd != 2) { dup2(fd, 2); } fchmod(1, 0644); } /* If it's still attatched to our terminal, things get messed up, but civserver needs *something* */ fclose(stdin); fd = open("/dev/null", O_RDONLY); if (fd != 0) { dup2(fd, 0); } /* these won't return on success */ execvp("./ser", argv); execvp("./server/civserver", argv); execvp("civserver", argv); /* This line is only reached if civserver cannot be started, * so we kill the forked process. * Calling exit here is dangerous due to X11 problems (async replies) */ _exit(1); } # else /* HAVE_WORKING_FORK */ # ifdef WIN32_NATIVE if (logfile) { loghandle = CreateFile(logfile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); } ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); si.hStdOutput = loghandle; si.hStdInput = INVALID_HANDLE_VALUE; si.hStdError = loghandle; si.dwFlags = STARTF_USESTDHANDLES; /* Set up the command-line parameters. */ logcmdline[0] = 0; scriptcmdline[0] = 0; if (logfile) { my_snprintf(logcmdline, sizeof(logcmdline), " --debug 3 --log %s", logfile); } if (scriptfile) { my_snprintf(scriptcmdline, sizeof(scriptcmdline), " --read %s", scriptfile); } interpret_tilde(savesdir, sizeof(savesdir), "~/.freeciv/saves"); my_snprintf(options, sizeof(options), "-p %d -q 1 -e%s%s --saves \"%s\"", internal_server_port, logcmdline, scriptcmdline, savesdir); my_snprintf(cmdline1, sizeof(cmdline1), "./ser %s", options); my_snprintf(cmdline2, sizeof(cmdline2), "./server/civserver %s", options); my_snprintf(cmdline3, sizeof(cmdline3), "civserver %s", options); if (!CreateProcess(NULL, cmdline1, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) && !CreateProcess(NULL, cmdline2, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) && !CreateProcess(NULL, cmdline3, NULL, NULL, TRUE, DETACHED_PROCESS | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { output_window_append(FTC_CLIENT_INFO, NULL, _("Couldn't start the server.")); output_window_append(FTC_CLIENT_INFO, NULL, _("You'll have to start one manually. Sorry...")); return FALSE; } server_process = pi.hProcess; # endif /* WIN32_NATIVE */ # endif /* HAVE_WORKING_FORK */ /* a reasonable number of tries */ while (connect_to_server(user_name, "localhost", internal_server_port, buf, sizeof(buf)) == -1) { myusleep(WAIT_BETWEEN_TRIES); #ifdef HAVE_WORKING_FORK #ifndef WIN32_NATIVE if (waitpid(server_pid, NULL, WNOHANG) != 0) { break; } #endif /* WIN32_NATIVE */ #endif /* HAVE_WORKING_FORK */ if (connect_tries++ > NUMBER_OF_TRIES) { break; } } /* weird, but could happen, if server doesn't support new startup stuff * capabilities won't help us here... */ if (!client.conn.used) { /* possible that server is still running. kill it */ client_kill_server(TRUE); output_window_append(FTC_CLIENT_INFO, NULL, _("Couldn't connect to the server.")); output_window_append(FTC_CLIENT_INFO, NULL, _("We probably couldn't start it from here.")); output_window_append(FTC_CLIENT_INFO, NULL, _("You'll have to start one manually. Sorry...")); return FALSE; } /* We set the topology to match the view. * * When a typical player launches a game, he wants the map orientation to * match the tileset orientation. So if you use an isometric tileset you * get an iso-map and for a classic tileset you get a classic map. In * both cases the map wraps in the X direction by default. * * This works with hex maps too now. A hex map always has * tileset_is_isometric(tileset) return TRUE. An iso-hex map has * tileset_hex_height(tileset) != 0, while a non-iso hex map * has tileset_hex_width(tileset) != 0. * * Setting the option here is a bit of a hack, but so long as the client * has sufficient permissions to do so (it doesn't have HACK access yet) it * is safe enough. Note that if you load a savegame the topology will be * set but then overwritten during the load. */ send_chat_printf("/set topology %d", (TF_WRAPX | ((tileset_is_isometric(tileset) && tileset_hex_height(tileset) == 0) ? TF_ISO : 0) | ((tileset_hex_width(tileset) != 0 || tileset_hex_height(tileset) != 0) ? TF_HEX : 0))); return TRUE; #endif /* HAVE_WORKING_FORK || WIN32_NATIVE */ }