/*************************************************************************** client_handlers.c - description ------------------- begin : Sat Oct 26 12:02:57 CEST 2002 copyright : (C) 2002 by Michael Speck email : kulkanie@gmx.net ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include "lbreakout.h" #include "config.h" #include "../gui/gui.h" #include "client_data.h" #include "client_handlers.h" #include "comm.h" #include "game.h" /* ==================================================================== Externals ==================================================================== */ extern Config config; extern List *client_users; extern List *client_games; extern List *client_channels; extern List *client_levelsets; extern char *client_levelset; extern ClientUser *client_user; extern char chatter[CHAT_LINE_COUNT][CHAT_LINE_WIDTH]; extern GuiWidget *dlg_connect; extern GuiWidget *dlg_info; extern GuiWidget *dlg_confirm; extern GuiWidget *dlg_chatroom; extern GuiWidget *dlg_channels; extern GuiWidget *dlg_stats; extern GuiWidget *dlg_pauseroom; extern GuiWidget *dlg_help; extern GuiWidget *label_info; extern GuiWidget *label_stats; extern GuiWidget *label_winner; extern GuiWidget *label_channel; extern GuiWidget *edit_server; extern GuiWidget *edit_username; extern GuiWidget *list_chatter; extern GuiWidget *edit_chatter; extern GuiWidget *list_levels; extern GuiWidget *list_users; extern GuiWidget *list_channels; extern GuiWidget *edit_channel; extern GuiWidget *edit_pausechatter; extern GuiWidget *list_help; extern int levelset_version, levelset_update; extern List *levels; extern void client_popup_info( char *format, ... ); extern void client_run_game( int challenger ); extern int client_topic_count; extern char *client_helps[]; extern Text *client_help_text; /* ==================================================================== Client ==================================================================== */ #ifdef NETWORK_ENABLED NetSocket client; /* client socket to the game server */ #endif int client_is_connected; /* wether 'client' is a valid uplink */ char client_error[128]; /* error message */ int client_id; /* id assigned by server */ char client_name[16]; /* our local username */ int client_state = CLIENT_NONE; int client_recv_limit; /* ==================================================================== Challenge data ==================================================================== */ char *mp_diff_names[] = { "Easy", "Medium", "Hard" }; char mp_levelset[16]; /* name of levelset we play */ int mp_peer_id; char mp_peer_name[16]; /* remote player we want to play with */ int mp_levelset_version; int mp_levelset_update; /* version of levelset */ int mp_level_count; /* number of levels in set */ int mp_diff, mp_rounds, mp_frags, mp_balls; /* game configuration */ extern void close_pause_chat( void ); /* ==================================================================== Disconnect from current server if any. ==================================================================== */ void client_disconnect() { #ifdef NETWORK_ENABLED char buf[128]; if ( !client_is_connected ) return; /* disconnect */ socket_print_stats( &client ); sprintf( buf, _("disconnected from %s"), net_addr_to_string(&client.remote_addr) ); client_add_chatter( buf, 1 ); buf[0] = MSG_DISCONNECT; client_transmit( CODE_BLUE, 1, buf ); client_is_connected = 0; client_data_clear(); gui_label_set_text( label_channel, "MAIN" ); #endif } /* ==================================================================== Try to connect to a game server. Retry twice every second or quit then. ==================================================================== */ void client_connect( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED NetAddr newaddr; int attempt = 0; int type; char server[128]; if ( event->type != GUI_CLICKED ) return; /* close the connect window */ gui_widget_hide( dlg_connect ); /* disconnect from current server */ client_disconnect(); /* extract ip and port and build a new socket out of it */ gui_edit_get_text( edit_server, server, 128, 0, -1 ); snprintf( config.server, 64, "%s", server ); if ( !net_build_addr( &newaddr, server, 0 ) ) { client_printf_chatter( 1, _("ERROR: address %s does not resolve"), config.server ); return; } socket_init( &client, &newaddr ); /* get username */ gui_edit_get_text( edit_username, config.username, 16, 0,-1 ); /* build connect message */ msg_begin_writing( msgbuf, &msglen, 64 ); msg_write_int8( MSG_CONNECT ); msg_write_int8( PROTOCOL ); msg_write_string( config.username ); msg_write_string( _("unused") ); /* passwd */ while ( attempt < 3 ) { client_printf_chatter( 1, "%s: %s...", config.server, attempt==0?_("connecting"):_("retry") ); stk_display_update( STK_UPDATE_ALL ); net_transmit_connectionless( &newaddr, msglen, msgbuf ); SDL_Delay( 1000 ); while ( net_recv_packet() ) { if ( msg_is_connectionless() ) msg_begin_connectionless_reading(); else if ( !socket_process_header( &client ) ) continue; type = msg_read_int8(); switch ( type ) { case MSG_LOGIN_OKAY: client_id = msg_read_int32(); strcpy( client_name, msg_read_string() ); client_printf_chatter( 1, _("%s: connected!"), config.server ); client_is_connected = 1; return; case MSG_ERROR: client_printf_chatter( 1, _("ERROR: connection refused: %s"), msg_read_string() ); return; } } attempt++; } client_add_chatter( _("ERROR: server does not respond"), 1 ); #endif } /* ==================================================================== Open/close the connection window. ==================================================================== */ void client_open_connect_window( GuiWidget *widget, GuiEvent *event ) { if ( event->type == GUI_CLICKED ) gui_widget_show( dlg_connect ); } void client_close_connect_window( GuiWidget *widget, GuiEvent *event ) { if ( event->type == GUI_CLICKED ) gui_widget_hide( dlg_connect ); } /* ==================================================================== Close chatroom and return to LBreakout's menu. ==================================================================== */ void client_quit( GuiWidget *widget, GuiEvent *event ) { if ( event->type == GUI_CLICKED ) gui_widget_hide( dlg_chatroom ); /* disconnect is handled in client_run to cover * stk_quit_requests as well */ } /* ==================================================================== Close the info window and clear state. ==================================================================== */ void client_close_info( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED if ( event->type == GUI_CLICKED ) { gui_widget_hide( dlg_info ); msg_begin_writing( msgbuf, &msglen, 128 ); switch ( client_state ) { case CLIENT_AWAIT_ANSWER: msg_write_int8( MSG_CANCEL_GAME ); break; } client_transmit( CODE_BLUE, msglen, msgbuf ); client_state = CLIENT_NONE; } #endif } /* ==================================================================== Send chatter this function is either called by the send button or by the edit. ==================================================================== */ void client_send_chatter( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED char buf[MAX_CHATTER_SIZE + 1]; if ( ( widget->type == GUI_EDIT && event->type == GUI_KEY_RELEASED && event->key.keysym == SDLK_RETURN ) || ( widget->type == GUI_BUTTON && event->type == GUI_CLICKED ) ) { /* get message */ gui_edit_get_text( edit_chatter, buf, MAX_CHATTER_SIZE + 1, 0,-1 ); /* clear chat edit */ gui_edit_set_text( edit_chatter, "" ); msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); /* a prepended '/' means this is a command */ if ( buf[0] == '/' ) { msg_write_int8( MSG_COMMAND ); msg_write_string( buf+1 ); } else { msg_write_int8( MSG_CHATTER ); msg_write_string( buf ); } client_transmit( CODE_BLUE, msglen, msgbuf ); } #endif } /* ==================================================================== Whisper chatter if a user is selected. ==================================================================== */ void client_whisper_chatter( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED char buf[MAX_CHATTER_SIZE + 1]; if ( event->type != GUI_CLICKED ) return; /* get message */ gui_edit_get_text( edit_chatter, buf, MAX_CHATTER_SIZE + 1, 0,-1 ); /* send to selected user */ if ( client_user ) { msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); msg_write_int8( MSG_WHISPER ); msg_write_int32( client_user->id ); msg_write_string( buf ); client_transmit( CODE_BLUE, msglen, msgbuf ); /* clear chat edit */ gui_edit_set_text( edit_chatter, "" ); } else client_add_chatter( _("You must select a user to whisper!"), 1 ); #endif } /* ==================================================================== Update selected peer and levelset. ==================================================================== */ void client_handle_user_list( GuiWidget *widget, GuiEvent *event ) { if ( event->type == GUI_ITEM_SELECTED ) client_user = list_get( client_users, event->item.y ); else if ( event->type == GUI_ITEM_UNSELECTED ) client_user = 0; } void client_handle_levelset_list( GuiWidget *widget, GuiEvent *event ) { if ( event->type == GUI_ITEM_SELECTED ) client_levelset = list_get( client_levelsets, event->item.y ); else if ( event->type == GUI_ITEM_UNSELECTED ) client_levelset = 0; } /* ==================================================================== Handle confirmation/cancelling of confirmation dialogue. ==================================================================== */ void client_confirm( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_confirm ); msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); switch ( client_state ) { case CLIENT_ANSWER: msg_write_int8( MSG_ACCEPT_CHALLENGE ); client_transmit( CODE_BLUE, msglen, msgbuf ); /* play */ gui_disable_event_filter(); if ( client_game_init_network( mp_peer_name, mp_diff ) ) client_game_run(); client_game_finalize(); gui_enable_event_filter(); gui_widget_draw( dlg_chatroom ); stk_display_fade( STK_FADE_IN, STK_FADE_DEFAULT_TIME ); break; } #endif } void client_cancel( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_confirm ); msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); switch ( client_state ) { case CLIENT_ANSWER: msg_write_int8( MSG_REJECT_CHALLENGE ); break; } client_transmit( CODE_BLUE, msglen, msgbuf ); client_state = CLIENT_NONE; #endif } /* ==================================================================== Challenge selected user. ==================================================================== */ void client_challenge( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED if ( event->type != GUI_CLICKED ) return; /* everything valid? */ if ( client_user == 0 ) { client_popup_info( _("You must select a user for a challenge.") ); return; } if ( client_levelset == 0 ) { client_popup_info( _("You must select a levelset for a challenge.") ); return; } if ( client_user->id == client_id ) { client_popup_info( _("You can't challenge yourself.") ); return; } strcpy( mp_peer_name, client_user->name ); mp_peer_id = client_user->id; strcpy( mp_levelset, client_levelset ); mp_diff = config.mp_diff; mp_rounds = config.mp_rounds; mp_balls = config.mp_balls; mp_frags = config.mp_frags; /* challenger, challenged, levelset, diff, rounds, frags, balls */ msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); msg_write_int8( MSG_OPEN_GAME ); msg_write_int32( mp_peer_id ); msg_write_string( mp_levelset ); msg_write_int8( mp_diff ); msg_write_int8( mp_rounds ); msg_write_int8( mp_frags ); msg_write_int8( mp_balls ); client_transmit( CODE_BLUE, msglen, msgbuf ); client_popup_info( _("You have challenged %s. Let's see what (s)he says..."), mp_peer_name ); client_state = CLIENT_AWAIT_ANSWER; #endif } /* ==================================================================== Update multiplayer network configuration. ==================================================================== */ void client_update_difficulty( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CHANGED ) return; gui_spinbutton_get_value( widget, &config.mp_diff ); config.mp_diff--; } void client_update_rounds( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CHANGED ) return; gui_spinbutton_get_value( widget, &config.mp_rounds ); } void client_update_frags( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CHANGED ) return; gui_spinbutton_get_value( widget, &config.mp_frags ); } void client_update_balls( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CHANGED ) return; gui_spinbutton_get_value( widget, &config.mp_balls ); } /* ==================================================================== Open channel selector ==================================================================== */ void client_select_channel( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; /* select first channel (we always have MAIN) */ gui_list_update( list_channels, client_channels->count ); if ( client_channels->count > 0 ) { gui_edit_set_text( edit_channel, list_first( client_channels ) ); gui_list_select( list_channels, 0,0, 1 ); } gui_widget_show( dlg_channels ); client_state = CLIENT_SELECT_CHANNEL; } /* ==================================================================== Handle channel (un)selection. ==================================================================== */ void client_handle_channel_list( GuiWidget *widget, GuiEvent *event ) { char *name; /* if a channel is selected the name is copied into the edit. unselecting does not change anything. the channel by the caption in the edit is opened on enter_channel() */ if ( event->type == GUI_ITEM_SELECTED ) { name = list_get( client_channels, event->item.y ); if ( name ) gui_edit_set_text( edit_channel, name ); } } /* ==================================================================== Close channel selector or enter new channel. ==================================================================== */ void client_enter_channel( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED char buf[16]; if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_channels ); client_state = CLIENT_NONE; /* retreive name of channel we want to enter */ buf[0] = 0; gui_edit_get_text( edit_channel, buf, 16, 0,-1 ); /* send it */ msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); msg_write_int8( MSG_ENTER_CHANNEL ); msg_write_string( buf ); client_transmit( CODE_BLUE, msglen, msgbuf ); #endif } void client_cancel_channel( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_channels ); client_state = CLIENT_NONE; } /* ==================================================================== Close statistics ==================================================================== */ void client_close_stats( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_stats ); client_state = CLIENT_NONE; gui_label_set_text( label_stats, _("Awaiting stats...") ); gui_label_set_text( label_winner, "..." ); } /* ==================================================================== Send chatter to gamepeer in pauseroom when ENTER was pressed. ==================================================================== */ void client_send_pausechatter( GuiWidget *widget, GuiEvent *event ) { #ifdef NETWORK_ENABLED char buf[MAX_CHATTER_SIZE + 1]; if ( widget->type == GUI_EDIT && event->type == GUI_KEY_RELEASED && event->key.keysym == SDLK_RETURN ) { /* get message */ sprintf( buf, "<%s> ", client_name ); gui_edit_get_text( edit_pausechatter, buf+strlen(buf), MAX_CHATTER_SIZE + 1, 0,-1 ); /* clear chat edit */ gui_edit_set_text( edit_pausechatter, "" ); /* deliver message to remote ... */ msg_begin_writing( msgbuf, &msglen, MAX_MSG_SIZE ); msg_write_int8( MSG_CHATTER ); msg_write_string( buf ); client_transmit( CODE_BLUE, msglen, msgbuf ); client_add_pausechatter( buf, 0 ); } #endif } /* ==================================================================== Close pauseroom. ==================================================================== */ void client_close_pauseroom( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; close_pause_chat(); comm_send_short( MSG_UNPAUSE ); } /* ==================================================================== Popup help dialogue. ==================================================================== */ void client_popup_help( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; gui_widget_show( dlg_help ); client_state = CLIENT_HELP; } /* ==================================================================== Close help dialogue. ==================================================================== */ void client_close_help( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_CLICKED ) return; gui_widget_hide( dlg_help ); } /* ==================================================================== Select topic and display help text. ==================================================================== */ void client_handle_topic_list( GuiWidget *widget, GuiEvent *event ) { if ( event->type != GUI_ITEM_SELECTED ) return; if ( event->item.y >= client_topic_count ) return; gui_list_goto( list_help, 0 ); if ( client_help_text ) delete_text( client_help_text ); client_help_text = create_text( client_helps[event->item.y], 41 ); gui_list_update( list_help, client_help_text->count ); }