/********************************************************************/ /* */ /* L I QQ U U I DD W W A RR 555 */ /* L I Q Q U U I D D W W A A R R 5 */ /* L I Q Q U U I D D W W W AAA RR 55 */ /* L I Q Q U U I D D WW WW A A R R 5 */ /* LLL I Q Q U I DD W W A A R R 55 */ /* */ /* b */ /* bb y y */ /* b b yyy */ /* bb y */ /* yy */ /* */ /* U U FFF O O TTT */ /* U U F O O O O T */ /* U U TIRET FF O O O O T */ /* U U F O O O O T */ /* U F O O T */ /* */ /********************************************************************/ /*****************************************************************************/ /* Liquid War is a multiplayer wargame */ /* Copyright (C) 1998-2007 Christian Mauduit */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* */ /* Liquid War homepage : http://www.ufoot.org/liquidwar/v5 */ /* Contact author : ufoot@ufoot.org */ /*****************************************************************************/ /********************************************************************/ /* name : connect.c */ /* contenu : connect menu, the list of connected players */ /********************************************************************/ /*==================================================================*/ /* includes */ /*==================================================================*/ #include #include #include "back.h" #include "base.h" #include "config.h" #include "decal.h" #include "dialog.h" #include "help.h" #include "menu.h" #include "play.h" #include "disk.h" #include "protocol.h" #include "sockgen.h" #include "server.h" #include "network.h" #include "error.h" #include "internet.h" #include "connect.h" #include "lang.h" #include "mouse.h" #include "log.h" #include "thrdgen.h" #include "chat.h" #include "netmess.h" #include "sound.h" #include "macro.h" /*==================================================================*/ /* constants */ /*==================================================================*/ #define CONNECT_BUF_SIZE 200 /*==================================================================*/ /* structs */ /*==================================================================*/ typedef struct { int sock; LW_TEAMSTARTINFO team_start_info[NB_TEAMS]; int waited_teams; int start; int force_start; int thread_running; int result; int finished; char chat_buffer[LW_CHAT_MESSAGE_SIZE + 1]; LW_CHAT_HISTORY chat_history; int chat_send; } LW_CONNECT_DATA; LW_CONNECT_DATA GLOBAL_CONNECT_DATA; /*==================================================================*/ /* fonctions */ /*==================================================================*/ static void connect_on_server_keepalive (void *args); static int update_connect_menu (DIALOG * d, int waited_teams, LW_TEAMSTARTINFO * team_start_info, LW_CHAT_HISTORY * chat_history, int *connected_teams_prev); static void position_players_list (DIALOG * d); static void position_chat_history (DIALOG * d); /*------------------------------------------------------------------*/ int lw_connect_menu (int sock) { DIALOG d[12]; DIALOG_PLAYER *dp; int choix = 4, retour = 0; char buf_waited_teams[CONNECT_BUF_SIZE]; char buf_connected_players[NB_TEAMS * CONNECT_BUF_SIZE]; char buf_chat_history[LW_CHAT_HISTORY_SIZE * CONNECT_BUF_SIZE]; char buf_chat_message[LW_CHAT_MESSAGE_SIZE + 1]; int first_display = 1; int first_getinfo = 1; LW_CONNECT_DATA *data; int network_error = 0; int hide_start_button = 0; int connected_teams_prev = -1; memset (d, 0, sizeof (d)); buf_waited_teams[0] = '\0'; buf_connected_players[0] = '\0'; buf_chat_history[0] = '\0'; buf_chat_message[0] = '\0'; data = &GLOBAL_CONNECT_DATA; memset (data, 0, sizeof (LW_CONNECT_DATA)); data->sock = sock; data->waited_teams = NB_TEAMS; LW_NETWORK_ON = 1; while (retour == 0) { standard_button (d + 4, 1, 0, 2, 10); standard_button (d + 6, 1, 5, 2, 10); standard_button (d + 8, 1, 7, 2, 10); standard_button (d + 9, 1, 7, 2, 10); standard_button (d + 10, 1, 8, 2, 10); quick_buttons (d); d[4].proc = my_textbox_proc; d[5].proc = my_textbox_proc; d[7].proc = my_textbox_proc; d[8].proc = d_box_proc; d[9].proc = my_edit_proc; position_players_list (d + 5); position_chat_history (d + 7); d[4].w = d[7].w; d[8].w = d[7].w; d[9].w = d[7].w; d[9].x++; d[9].y++; d[9].w -= 2; d[9].h -= 2; d[4].dp = buf_waited_teams; d[5].dp = buf_connected_players; d[6].dp = lw_lang_string (LW_LANG_STRING_CONNECT_STARTNOW); d[7].dp = buf_chat_history; d[9].dp = buf_chat_message; d[9].d1 = LW_CHAT_MESSAGE_SIZE; d[9].flags = D_EXIT; d[10].dp = lw_lang_string (LW_LANG_STRING_CONNECT_SENDMESSAGE); if (hide_start_button) { /* * We disactivate these buttons. If we did not do it * it would lead to network errors if someone would * press them twice. */ d[6].proc = my_textbox_proc; d[6].dp = lw_lang_string (LW_LANG_STRING_CONNECT_WAITING); d[MENU_QUICK_PLAY].flags = D_HIDDEN; } d[11].proc = 0; if (first_display) { display_back_image (); first_display = 0; } dp = my_init_dialog (d, choix); while (my_update_dialog (dp) && !data->finished) { show_mouse (screen); if (!data->thread_running && !data->finished) { if (!first_getinfo) { if (data->result == 0) { network_error = 1; } else { update_connect_menu (d, data->waited_teams, data->team_start_info, &(data->chat_history), &connected_teams_prev); } } data->thread_running = 1; if (lw_thread_start (connect_on_server_keepalive, data)) { first_getinfo = 0; } else { data->thread_running = 0; } } rest (10); } choix = shutdown_dialog (dp); if (network_error) { error_network_problem (); lw_sock_close (&(data->sock)); retour = 1; } else { /* * If 0 teams are waited for, then the game must start, so * we simulate a key press on the "start" button. */ if (data->waited_teams == 0) { retour = MENU_OK; } else { switch (choix) { case -1: case MENU_QUICK_BACK: lw_sock_close (&(data->sock)); retour = 1; break; case MENU_QUICK_MAIN: lw_sock_close (&(data->sock)); retour = MENU_TOP; break; case MENU_QUICK_QUIT: lw_sock_close (&(data->sock)); if (confirm_quit ()) retour = MENU_EXIT; else display_back_image (); break; case MENU_QUICK_PLAY: /* * Pressing Play or Start has the same effect here * so there's no "break;" */ case 6: if (data->waited_teams > 0) { data->force_start = 1; hide_start_button = 1; display_back_image (); } break; case 9: case 10: if (strlen (d[9].dp) && !data->chat_send) { data->chat_send = 1; strncpy (data->chat_buffer, d[9].dp, LW_CHAT_MESSAGE_SIZE); data->chat_buffer[LW_CHAT_MESSAGE_SIZE] = '\0'; ((char *) (d[9].dp))[0] = '\0'; d[9].d2 = 0; d[9].proc (MSG_DRAW, d + 9, 0); } break; } } } } while (data->thread_running) { /* * Here, we wait until the thread is over, it shouldn't be very * long since we just wait for the server's answer */ rest (100); } if (retour > 0) retour--; return retour; } /*------------------------------------------------------------------*/ /* * Updates the fields of the menu */ static int update_connect_menu (DIALOG * d, int waited_teams, LW_TEAMSTARTINFO * team_start_info, LW_CHAT_HISTORY * chat_history, int *connected_teams_prev) { int result = 0; int i; char *sep; char buf[CONNECT_BUF_SIZE]; LW_CHAT_MESSAGE chat_mess; static int old_chat_length = 0; int chat_length; int l; int connected_teams = 0; LW_MACRO_SNPRINTF3 ((char *) d[4].dp, CONNECT_BUF_SIZE, "%s%d%s", lw_lang_string (LW_LANG_STRING_CONNECT_WAITINGFOR), waited_teams, lw_lang_string (LW_LANG_STRING_CONNECT_TEAMS)); ((char *) (d[5].dp))[0] = '\0'; for (i = 0; i < NB_TEAMS; ++i) { if (team_start_info[i].active) { if (team_start_info[i].start) { sep = "*"; } else { sep = "-"; } LW_MACRO_SPRINTF3 (buf, "%d %s %s\n", i + 1, sep, team_start_info[i].name); LW_MACRO_STRNCAT ((char *) d[5].dp, buf, NB_TEAMS * CONNECT_BUF_SIZE); connected_teams++; } } ((char *) (d[7].dp))[0] = '\0'; for (i = 0; lw_chat_get (chat_history, &chat_mess, i); ++i) { LW_MACRO_SPRINTF2 (buf, "%s: %s\n", chat_mess.author, chat_mess.content); LW_MACRO_STRNCAT ((char *) d[7].dp, buf, LW_CHAT_HISTORY_SIZE * CONNECT_BUF_SIZE); } /* * Now if the length of the string has changed, we * scroll to the end of the list. The "string length" * test is not 100% safe, but its purpose is just to * scroll through the list when a message is sent. * if it fails sometimes, it's not _that_ bad. */ chat_length = strlen (d[7].dp); if (chat_length != old_chat_length) { old_chat_length = chat_length; l = (d[7].h - 8) / text_height (font); d[7].d2 = MAX (0, d[7].d1 - l); } scare_mouse (); d[4].proc (MSG_DRAW, d + 4, 0); d[5].proc (MSG_DRAW, d + 5, 0); d[7].proc (MSG_DRAW, d + 7, 0); unscare_mouse (); show_mouse (screen); /* * If there are more teams connected than last times, we're likely * to announce it with a sound */ if (connected_teams > 0 && connected_teams > *connected_teams_prev) { /* * We check if this is not the first time * (*connected_teams_prev==-1) and in this case we play * nothing */ if ((*connected_teams_prev) >= 0) { play_connect (); } (*connected_teams_prev) = connected_teams; } else { /* * We updated the numner of connected teams, but take care * note to overwrite it if it's -1 */ if ((*connected_teams_prev) >= 0) { (*connected_teams_prev) = connected_teams; } } return result; } /*------------------------------------------------------------------*/ /* * Retrieves informations from the server once all players are * connected. Also displays information in a dialog box. */ static void connect_on_server_keepalive (void *arg) { int result = 1; int i; LW_CONNECT_DATA *data; data = (LW_CONNECT_DATA *) arg; if (!data->finished) { if (result == 1) { if (!lw_protocol_waiting (&data->sock, &(data->waited_teams))) { result = 0; } } if (result == 1) { if (data->waited_teams > 0) { /* * We are still waiting for teams */ for (i = 0; i < NB_TEAMS; ++i) { if (result == 1) { if (!lw_protocol_ask_teamstartinfo (&data->sock, i, &(data-> team_start_info [i]))) { result = 0; } } } if (result == 1 && data->chat_send) { lw_netmess_cleanup_arg (data->chat_buffer); if (!lw_protocol_chat_talk (&data->sock, data->chat_buffer)) { result = 0; } data->chat_send = 0; } if (result == 1) { if (!lw_protocol_chat_listen (&data->sock, &(data->chat_history))) { result = 0; } } if (result == 1 && data->force_start) { if (!lw_protocol_force_start (&data->sock)) { result = 0; } /* * It's useless to keep on sending the START message, once is * just enough, let's save bandwidth! */ data->force_start = 0; } if (result == 1) { if (!lw_protocol_next (&data->sock)) { result = 0; } } } else { /* * No more teams waited, we say we're ready */ if (result == 1) { if (!lw_protocol_i_am_ready (&data->sock)) { result = 0; } data->finished = 1; } } } } data->result = result; data->thread_running = 0; } /*------------------------------------------------------------------*/ static void position_players_list (DIALOG * d) { d->x = (MENU_VIRTUAL_W + MENU_W_SPACE) / 2; d->y = (MENU_VIRTUAL_H - MENU_H_INTERNET_LIST) / 2 + MENU_H_STANDARD + MENU_H_SPACE; d->w = (MENU_W_STANDARD + MENU_W_INTERNET_LIST) / 2; d->h = 4 * MENU_H_STANDARD + 3 * MENU_H_SPACE; color_one_dialog (d); menu_real_coord (d); } /*------------------------------------------------------------------*/ static void position_chat_history (DIALOG * d) { d->x = (MENU_VIRTUAL_W - MENU_W_SPACE) / 2 - MENU_W_INTERNET_LIST; d->y = (MENU_VIRTUAL_H - MENU_H_INTERNET_LIST) / 2; d->w = MENU_W_INTERNET_LIST; d->h = MENU_H_INTERNET_LIST; color_one_dialog (d); menu_real_coord (d); d->key = 0; d->dp = ""; d->flags = D_EXIT; }