From 265e902ed57ab9b9972653abaa6eca551f761a70 Mon Sep 17 00:00:00 2001 From: Dave White Date: Sun, 5 Oct 2003 08:09:56 +0000 Subject: [PATCH] added better error handling in network connection code --- data/translations/english.cfg | 3 + src/menu.cpp | 16 ++- src/menu.hpp | 15 ++- src/multiplayer.cpp | 185 ++++++++++++++++++++++------------ src/network.cpp | 13 ++- src/network.hpp | 5 +- 6 files changed, 162 insertions(+), 75 deletions(-) diff --git a/data/translations/english.cfg b/data/translations/english.cfg index 3a3dc31cd85..60933603fd0 100644 --- a/data/translations/english.cfg +++ b/data/translations/english.cfg @@ -128,6 +128,9 @@ network_controlled="Network Player" remote_host="Choose host to connect to" connection_failed="Could not connect to the remote host" connection_timeout="Connection timed out" +awaiting_connections="Waiting for players to connect" +position_taken="Filled" +position_vacant="Available" host_game="Host Multiplayer Game" join_game="Join Networked Multiplayer Game" diff --git a/src/menu.cpp b/src/menu.cpp index a5c88558c82..3cd592432bb 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -447,7 +447,8 @@ int show_dialog(display& disp, SDL_Surface* image, const std::vector* menu_items_ptr, const std::vector* units_ptr, const std::string& text_widget_label, - std::string* text_widget_text) + std::string* text_widget_text, + dialog_action* action) { if(disp.update_locked()) return -1; @@ -547,6 +548,12 @@ int show_dialog(display& disp, SDL_Surface* image, button_list = thebuttons; break; } + + case CANCEL_ONLY: { + static const std::string thebuttons[] = { "cancel_button", "" }; + button_list = thebuttons; + break; + } } const int button_height_padding = 10; @@ -829,6 +836,13 @@ int show_dialog(display& disp, SDL_Surface* image, } } + if(action != NULL) { + const int act = action->do_action(); + if(act != dialog_action::CONTINUE_DIALOG) { + return act; + } + } + SDL_Delay(20); } diff --git a/src/menu.hpp b/src/menu.hpp index 637a9f1bb0a..19d9d1a2fc3 100644 --- a/src/menu.hpp +++ b/src/menu.hpp @@ -32,7 +32,17 @@ void draw_solid_tinted_rectangle(int x, int y, int w, int h, int r, int g, int b, double alpha, SDL_Surface* target); -enum DIALOG_TYPE { MESSAGE, OK_ONLY, YES_NO, OK_CANCEL }; +class dialog_action +{ +public: + virtual ~dialog_action() {} + + virtual int do_action() = 0; + + enum { CONTINUE_DIALOG=-2 }; +}; + +enum DIALOG_TYPE { MESSAGE, OK_ONLY, YES_NO, OK_CANCEL, CANCEL_ONLY }; //if a menu is given, then returns -1 if the dialog was cancelled, and the //index of the selection otherwise. If no menu is given, returns the index @@ -43,7 +53,8 @@ int show_dialog(display& screen, SDL_Surface* image, const std::vector* menu_items=NULL, const std::vector* units=NULL, const std::string& text_widget_label="", - std::string* text_widget_text=NULL + std::string* text_widget_text=NULL, + dialog_action* action=NULL ); enum TITLE_RESULT { TUTORIAL, NEW_CAMPAIGN, MULTIPLAYER, LOAD_GAME, QUIT_GAME, diff --git a/src/multiplayer.cpp b/src/multiplayer.cpp index fff424dda40..2a5a4dd3718 100644 --- a/src/multiplayer.cpp +++ b/src/multiplayer.cpp @@ -26,94 +26,151 @@ namespace { -bool accept_network_connections(config& players) +class connection_acceptor : public gui::dialog_action { - log_scope("accept network connections"); - typedef std::map positions_map; - positions_map positions; +public: + typedef std::map positions_map; + + connection_acceptor(config& players); + int do_action(); + + bool is_complete() const; + + std::vector get_positions_status() const; + + enum { CONNECTIONS_PART_FILLED=1, CONNECTIONS_FILLED=2 }; + +private: + positions_map positions_; + config& players_; + std::vector& sides_; +}; + +connection_acceptor::connection_acceptor(config& players) + : players_(players), sides_(players.children["side"]) +{ std::vector& sides = players.children["side"]; for(std::vector::const_iterator i = sides.begin(); i != sides.end(); ++i) { if((*i)->values["controller"] == "network") { - positions[*i] = 0; + positions_[*i] = 0; } } +} - if(positions.empty()) - return true; +int connection_acceptor::do_action() +{ + network::connection sock = network::accept_connection(); + if(sock) { + std::cerr << "Received connection\n"; + network::send_data(players_,sock); + } - std::cerr << "waiting for connections...\n"; - for(;;) { - network::connection sock = network::accept_connection(); - if(sock) { - std::cerr << "Received connection\n"; - network::send_data(players,sock); - } + config cfg; + sock = network::receive_data(cfg); + if(sock) { + const int side_taken = atoi(cfg.values["side"].c_str())-1; + if(side_taken >= 0 && side_taken < int(sides_.size())) { + positions_map::iterator pos = positions_.find(sides_[side_taken]); + if(pos != positions_.end()) { + if(!pos->second) { + std::cerr << "client has taken a valid position\n"; - config cfg; - sock = network::receive_data(cfg); - if(sock) { - const int side_taken = atoi(cfg.values["side"].c_str())-1; - if(side_taken >= 0 && side_taken < int(sides.size())) { - positions_map::iterator pos = positions.find(sides[side_taken]); - if(pos != positions.end()) { - if(!pos->second) { - std::cerr << "client has taken a valid position\n"; + //broadcast to everyone the new game status + pos->first->values["taken"] = "yes"; + positions_[sides_[side_taken]] = sock; + network::send_data(players_); - //broadcast to everyone the new game status - pos->first->values["taken"] = "yes"; - positions[sides[side_taken]] = sock; - network::send_data(players); + std::cerr << "sent player data\n"; - std::cerr << "sent player data\n"; + //send a reply telling the client they have secured + //the side they asked for + std::stringstream side; + side << (side_taken+1); + config reply; + reply.values["side_secured"] = side.str(); + std::cerr << "going to send data...\n"; + network::send_data(reply,sock); - //send a reply telling the client they have secured - //the side they asked for - std::stringstream side; - side << (side_taken+1); - config reply; - reply.values["side_secured"] = side.str(); - std::cerr << "going to send data...\n"; - network::send_data(reply,sock); - - //see if all positions are now filled - bool unclaimed = false; - for(positions_map::const_iterator p = positions.begin(); - p != positions.end(); ++p) { - if(!p->second) { - unclaimed = true; - break; - } + //see if all positions are now filled + bool unclaimed = false; + for(positions_map::const_iterator p = positions_.begin(); + p != positions_.end(); ++p) { + if(!p->second) { + unclaimed = true; + break; } + } - if(!unclaimed) { - std::cerr << "starting game now...\n"; - config start_game; - start_game.children["start_game"]. - push_back(new config()); - network::send_data(start_game); - return true; - } - } else { - config response; - response.values["failed"] = "yes"; - network::send_data(response,sock); + if(!unclaimed) { + std::cerr << "starting game now...\n"; + config start_game; + start_game.children["start_game"]. + push_back(new config()); + network::send_data(start_game); + return CONNECTIONS_FILLED; } } else { - std::cerr << "tried to take illegal side: " << side_taken - << "\n"; + config response; + response.values["failed"] = "yes"; + network::send_data(response,sock); } } else { - std::cerr << "tried to take unknown side: " << side_taken + std::cerr << "tried to take illegal side: " << side_taken << "\n"; } + } else { + std::cerr << "tried to take unknown side: " << side_taken + << "\n"; } - std::cerr << "pump\n"; - pump_events(); - SDL_Delay(50); + return CONNECTIONS_PART_FILLED; } + + return CONTINUE_DIALOG; +} + +bool connection_acceptor::is_complete() const +{ + for(positions_map::const_iterator i = positions_.begin(); + i != positions_.end(); ++i) { + if(!i->second) { + return false; + } + } + + return true; +} + +std::vector connection_acceptor::get_positions_status() const +{ + std::vector result; + for(positions_map::const_iterator i = positions_.begin(); + i != positions_.end(); ++i) { + result.push_back(i->first->values["name"] + "," + + (i->second ? ("@" + string_table["position_taken"]) : + string_table["position_vacant"])); + } + + return result; +} + +bool accept_network_connections(display& disp, config& players) +{ + connection_acceptor acceptor(players); + + while(acceptor.is_complete() == false) { + const std::vector& items = acceptor.get_positions_status(); + const int res = gui::show_dialog(disp,NULL,"", + string_table["awaiting_connections"], + gui::CANCEL_ONLY,&items,NULL,"",NULL,&acceptor); + if(res == 0) { + return false; + } + } + + return true; } } @@ -337,7 +394,7 @@ void play_multiplayer(display& disp, game_data& units_data, config& cfg, const network::manager net_manager; const network::server_manager server_man; - const bool network_state = accept_network_connections(level); + const bool network_state = accept_network_connections(disp,level); if(network_state == false) return; diff --git a/src/network.cpp b/src/network.cpp index f23b0535b46..23f3c2446be 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -8,7 +8,7 @@ namespace { -SDLNet_SocketSet socket_set = SDLNet_AllocSocketSet(64); +SDLNet_SocketSet socket_set = 0; typedef std::vector sockets_list; sockets_list sockets; @@ -25,6 +25,8 @@ manager::manager() if(SDLNet_Init() == -1) { throw error(SDL_GetError()); } + + socket_set = SDLNet_AllocSocketSet(64); } manager::~manager() @@ -148,9 +150,8 @@ connection receive_data(config& cfg, connection connection_num, int timeout) if(len == 0) { break; } else if(len < 0) { - disconnect(*i); throw error(std::string("error receiving data: ") + - SDLNet_GetError()); + SDLNet_GetError(),*i); } buffer.resize(buffer.size()+1); @@ -162,8 +163,7 @@ connection receive_data(config& cfg, connection connection_num, int timeout) } if(buffer == "") { - disconnect(*i); - throw error("error receiving data"); + throw error("error receiving data",*i); } std::cerr << "received: " << buffer << "\n"; @@ -222,8 +222,7 @@ void send_data(config& cfg, connection connection_num) if(res < int(value.size()+1)) { std::cerr << "sending data failed: " << res << "/" << value.size() << ": " << SDL_GetError() << "\n"; - disconnect(connection_num); - throw error("Could not send data over socket"); + throw error("Could not send data over socket",connection_num); } } diff --git a/src/network.hpp b/src/network.hpp index bd2676a1bf1..3bd87dd4259 100644 --- a/src/network.hpp +++ b/src/network.hpp @@ -33,8 +33,11 @@ void send_data(config& cfg, connection connection_num=0); struct error { - error(const std::string& msg) : message(msg) {} + error(const std::string& msg, connection sock=0) : message(msg) {} std::string message; + connection socket; + + void disconnect() { if(socket) { network::disconnect(socket); } } }; }