added better error handling in network connection code

This commit is contained in:
Dave White 2003-10-05 08:09:56 +00:00
parent d1c8b49df4
commit 265e902ed5
6 changed files with 162 additions and 75 deletions

View File

@ -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"

View File

@ -447,7 +447,8 @@ int show_dialog(display& disp, SDL_Surface* image,
const std::vector<std::string>* menu_items_ptr,
const std::vector<unit>* 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);
}

View File

@ -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<std::string>* menu_items=NULL,
const std::vector<unit>* 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,

View File

@ -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<config*,network::connection> positions_map;
positions_map positions;
public:
typedef std::map<config*,network::connection> positions_map;
connection_acceptor(config& players);
int do_action();
bool is_complete() const;
std::vector<std::string> get_positions_status() const;
enum { CONNECTIONS_PART_FILLED=1, CONNECTIONS_FILLED=2 };
private:
positions_map positions_;
config& players_;
std::vector<config*>& sides_;
};
connection_acceptor::connection_acceptor(config& players)
: players_(players), sides_(players.children["side"])
{
std::vector<config*>& sides = players.children["side"];
for(std::vector<config*>::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<std::string> connection_acceptor::get_positions_status() const
{
std::vector<std::string> 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<std::string>& 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;

View File

@ -8,7 +8,7 @@
namespace {
SDLNet_SocketSet socket_set = SDLNet_AllocSocketSet(64);
SDLNet_SocketSet socket_set = 0;
typedef std::vector<network::connection> 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);
}
}

View File

@ -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); } }
};
}