diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 29178e97f35d..b239e6181464 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -960,6 +960,7 @@ set(wesnoth-main_SRC utils/markov_generator.cpp variable.cpp variable_info.cpp + wesnothd_connection.cpp whiteboard/action.cpp whiteboard/attack.cpp whiteboard/highlighter.cpp diff --git a/src/SConscript b/src/SConscript index 174b8741be1b..68f07883614b 100644 --- a/src/SConscript +++ b/src/SConscript @@ -563,6 +563,7 @@ wesnoth_sources = Split(""" utils/markov_generator.cpp variable_info.cpp variable.cpp + wesnothd_connection.cpp whiteboard/action.cpp whiteboard/attack.cpp whiteboard/highlighter.cpp diff --git a/src/addon/client.cpp b/src/addon/client.cpp index 632d27e7cfba..4db3158811f2 100644 --- a/src/addon/client.cpp +++ b/src/addon/client.cpp @@ -293,16 +293,35 @@ void addons_client::send_simple_request(const std::string& request_string, confi request.add_child(request_string); this->send_request(request, response); } - +struct read_addon_connection_data : public gui2::tnetwork_transmission::connection_data +{ + read_addon_connection_data(network_asio::connection& conn) : conn_(conn) {} + size_t total() override { return conn_.bytes_to_read(); } + virtual size_t current() override { return conn_.bytes_read(); } + virtual bool finished() override { return conn_.done(); } + virtual void cancel() override { return conn_.cancel(); } + virtual void poll() override { conn_.poll(); } + network_asio::connection& conn_; +}; +struct write_addon_connection_data : public gui2::tnetwork_transmission::connection_data +{ + write_addon_connection_data(network_asio::connection& conn) : conn_(conn) {} + size_t total() override { return conn_.bytes_to_write(); } + virtual size_t current() override { return conn_.bytes_written(); } + virtual bool finished() override { return conn_.done(); } + virtual void cancel() override { return conn_.cancel(); } + virtual void poll() override { conn_.poll(); } + network_asio::connection& conn_; +}; void addons_client::wait_for_transfer_done(const std::string& status_message, bool track_upload) { check_connected(); - + std::unique_ptr cd(track_upload ? new write_addon_connection_data{ *conn_ } : new write_addon_connection_data{ *conn_ }); if(!stat_) { - stat_ = new gui2::tnetwork_transmission(*conn_, _("Add-ons Manager"), status_message); + stat_ = new gui2::tnetwork_transmission(*cd, _("Add-ons Manager"), status_message); } else { stat_->set_subtitle(status_message); - stat_->set_track_upload(track_upload); + stat_->set_connection_data(*cd); } if(!stat_->show(v_)) { diff --git a/src/dialogs.cpp b/src/dialogs.cpp index dff871a163fe..71cd47aed761 100644 --- a/src/dialogs.cpp +++ b/src/dialogs.cpp @@ -14,7 +14,7 @@ /** * @file - * Various dialogs: advance_unit, show_objectives, save+load game, network::connection. + * Various dialogs: advance_unit, show_objectives, save+load game */ #include "global.hpp" @@ -59,8 +59,9 @@ #include "formula/string_utils.hpp" #include "gui/dialogs/game_save.hpp" #include "gui/dialogs/transient_message.hpp" +#include "gui/dialogs/network_transmission.hpp" #include "ai/lua/aspect_advancements.hpp" - +#include "wesnothd_connection.hpp" #include #include @@ -1153,123 +1154,40 @@ void unit_types_preview_pane::process_event() } - -network::connection network_receive_dialog(CVideo& video, const std::string& msg, config& cfg, network::connection connection_num) +struct read_wesnothd_connection_data : public gui2::tnetwork_transmission::connection_data { - const size_t width = 300; - const size_t height = 80; - const size_t border = 20; - const int left = video.getx()/2 - width/2; - const int top = video.gety()/2 - height/2; - - const events::event_context dialog_events_context; - - gui::button cancel_button(video, _("Cancel")); - std::vector buttons_ptr(1,&cancel_button); - - gui::dialog_frame frame(video, msg, gui::dialog_frame::default_style, true, &buttons_ptr); - SDL_Rect centered_layout = frame.layout(left,top,width,height).interior; - centered_layout.x = video.getx() / 2 - centered_layout.w / 2; - centered_layout.y = video.gety() / 2 - centered_layout.h / 2; - // HACK: otherwise we get an empty useless space in the dialog below the progressbar - centered_layout.h = height; - frame.layout(centered_layout); - frame.draw(); - - const SDL_Rect progress_rect = sdl::create_rect(centered_layout.x + border - , centered_layout.y + border - , centered_layout.w - border * 2 - , centered_layout.h - border * 2); - - gui::progress_bar progress(video); - progress.set_location(progress_rect); - - events::raise_draw_event(); - video.flip(); - - network::statistics old_stats = network::get_receive_stats(connection_num); - - cfg.clear(); - for(;;) { - const network::connection res = network::receive_data(cfg,connection_num,100); - const network::statistics stats = network::get_receive_stats(connection_num); - if(stats.current_max != 0 && stats != old_stats) { - old_stats = stats; - progress.set_progress_percent((stats.current*100)/stats.current_max); - std::ostringstream stream; - stream << utils::si_string(stats.current, true, _("unit_byte^B")) << "/" << utils::si_string(stats.current_max, true, _("unit_byte^B")); - progress.set_text(stream.str()); - } - - events::raise_draw_event(); - video.flip(); - events::pump(); - - if(res != 0) { - return res; - } - - - if(cancel_button.pressed()) { - return res; - } - } -} - -} // end namespace dialogs - -namespace { - -class connect_waiter : public threading::waiter -{ -public: - connect_waiter(CVideo& v, gui::button& button) : v_(v), button_(button) - {} - ACTION process(); - -private: - CVideo& v_; - gui::button& button_; + read_wesnothd_connection_data(twesnothd_connection& conn) : conn_(conn) {} + size_t total() override { return conn_.bytes_to_read(); } + virtual size_t current() override { return conn_.bytes_read(); } + virtual bool finished() override { return conn_.has_data_received(); } + virtual void cancel() override { } + virtual void poll() override { conn_.poll(); } + twesnothd_connection& conn_; }; -connect_waiter::ACTION connect_waiter::process() +bool network_receive_dialog(CVideo& video, const std::string& msg, config& cfg, twesnothd_connection& wesnothd_connection) { - events::raise_draw_event(); - v_.flip(); - events::pump(); - if(button_.pressed()) { - return ABORT; - } else { - return WAIT; - } + read_wesnothd_connection_data gui_data(wesnothd_connection); + gui2::tnetwork_transmission(gui_data, msg, _("Waiting")).show(video); + return wesnothd_connection.receive_data(cfg); } -} - -namespace dialogs +struct connect_wesnothd_connection_data : public gui2::tnetwork_transmission::connection_data { + connect_wesnothd_connection_data(twesnothd_connection& conn) : conn_(conn) {} + virtual bool finished() override { return conn_.handshake_finished(); } + virtual void cancel() override { } + virtual void poll() override { conn_.poll(); } + twesnothd_connection& conn_; +}; -network::connection network_connect_dialog(CVideo& v, const std::string& msg, const std::string& hostname, int port) +std::unique_ptr network_connect_dialog(CVideo& v, const std::string& msg, const std::string& hostname, int port) { - const size_t width = 250; - const size_t height = 20; - const int left = v.getx()/2 - width/2; - const int top = v.gety()/2 - height/2; - - const events::event_context dialog_events_context; - - gui::button cancel_button(v,_("Cancel")); - std::vector buttons_ptr(1,&cancel_button); - - gui::dialog_frame frame(v, msg, gui::dialog_frame::default_style, true, &buttons_ptr); - frame.layout(left,top,width,height); - frame.draw(); - - events::raise_draw_event(); - v.flip(); + std::unique_ptr res(new twesnothd_connection(hostname, std::to_string(port))); + connect_wesnothd_connection_data gui_data(*res); + gui2::tnetwork_transmission(gui_data, msg, _("Connecting")).show(v); + return std::move(res); - connect_waiter waiter(v,cancel_button); - return network::connect(hostname,port,waiter); } } // end namespace dialogs diff --git a/src/dialogs.hpp b/src/dialogs.hpp index dbc5b524086c..5c97bfb4f043 100644 --- a/src/dialogs.hpp +++ b/src/dialogs.hpp @@ -22,7 +22,7 @@ class unit; class unit_map; class unit_type; class terrain_type; - +class twesnothd_connection; #include "map/location.hpp" #include "construct_dialog.hpp" #include "network.hpp" @@ -137,8 +137,8 @@ class unit_types_preview_pane : public dialogs::unit_preview_pane int side_; }; -network::connection network_receive_dialog(CVideo& video, const std::string& msg, config& cfg, network::connection connection_num=0); -network::connection network_connect_dialog(CVideo& video, const std::string& msg, const std::string& hostname, int port); +bool network_receive_dialog(CVideo& video, const std::string& msg, config& cfg, twesnothd_connection& wesnothd_connection); +std::unique_ptr network_connect_dialog(CVideo& video, const std::string& msg, const std::string& hostname, int port); } //end namespace dialogs diff --git a/src/game_initialization/connect_engine.cpp b/src/game_initialization/connect_engine.cpp index 7a9599fc87eb..2fcbef2f4dac 100644 --- a/src/game_initialization/connect_engine.cpp +++ b/src/game_initialization/connect_engine.cpp @@ -26,6 +26,8 @@ #include "playcampaign.hpp" #include "tod_manager.hpp" #include "multiplayer_ui.hpp" // For get_color_string +#include "wesnothd_connection.hpp" + #include #include #include @@ -199,7 +201,7 @@ connect_engine::connect_engine(saved_game& state, update_level(); // If we are connected, send data to the connected host. - send_level_data(0); + send_level_data(); } connect_engine::~connect_engine() @@ -308,7 +310,7 @@ void connect_engine::update_and_send_diff(bool /*update_time_of_day*/) if (!diff.empty()) { config scenario_diff; scenario_diff.add_child("scenario_diff", diff); - network::send_data(scenario_diff, 0); + send_to_server(scenario_diff); } } @@ -346,6 +348,24 @@ bool connect_engine::can_start_game() const return false; } +void connect_engine::send_to_server(const config& cfg) const +{ + if (campaign_info_) { + campaign_info_->wesnothd_connection.send_data(cfg); + } +} + +bool connect_engine::receive_from_server(config& dst) const +{ + if (campaign_info_) { + return campaign_info_->wesnothd_connection.receive_data(dst); + } + else { + return false; + } +} + + std::vector side_engine::get_children_to_swap() { std::vector children; @@ -459,7 +479,7 @@ void connect_engine::start_game() // Make other clients not show the results of resolve_random(). config lock("stop_updates"); - network::send_data(lock, 0); + send_to_server(lock); update_and_send_diff(true); @@ -468,7 +488,7 @@ void connect_engine::start_game() // Build the gamestate object after updating the level. mp::level_to_gamestate(level_, state_); - network::send_data(config("start_game"), 0); + send_to_server(config("start_game")); } void connect_engine::start_game_commandline( @@ -587,11 +607,10 @@ void connect_engine::start_game_commandline( // Build the gamestate object after updating the level mp::level_to_gamestate(level_, state_); - network::send_data(config("start_game"), 0); + send_to_server(config("start_game")); } -std::pair connect_engine::process_network_data(const config& data, - const network::connection sock) +std::pair connect_engine::process_network_data(const config& data) { std::pair result(std::make_pair(false, true)); @@ -628,7 +647,7 @@ std::pair connect_engine::process_network_data(const config& data, if (name.empty()) { config response; response["failed"] = true; - network::send_data(response, sock); + send_to_server(response); ERR_CF << "ERROR: No username provided with the side." << std::endl; @@ -643,7 +662,7 @@ std::pair connect_engine::process_network_data(const config& data, response["failed"] = true; response["message"] = "The nickname '" + name + "' is already in use."; - network::send_data(response, sock); + send_to_server(response); return result; } else { @@ -651,7 +670,7 @@ std::pair connect_engine::process_network_data(const config& data, update_side_controller_options(); config observer_quit; observer_quit.add_child("observer_quit")["name"] = name; - network::send_data(observer_quit, 0); + send_to_server(observer_quit); } } @@ -672,12 +691,12 @@ std::pair connect_engine::process_network_data(const config& data, if (side_taken >= side_engines_.size()) { config response; response["failed"] = true; - network::send_data(response, sock); + send_to_server(response); config res; config& kick = res.add_child("kick"); kick["username"] = data["name"]; - network::send_data(res, 0); + send_to_server(res); update_and_send_diff(); @@ -707,7 +726,7 @@ std::pair connect_engine::process_network_data(const config& data, config response; response["failed"] = true; - network::send_data(response, sock); + send_to_server(response); } } @@ -745,7 +764,6 @@ void connect_engine::process_network_error(network::error& error) { // The problem isn't related to any specific connection and // it's a general error. So we should just re-throw the error. - error.disconnect(); throw network::error(error.message); } @@ -767,22 +785,22 @@ int connect_engine::find_user_side_index_by_id(const std::string& id) const return i; } -void connect_engine::send_level_data(const network::connection sock) const +void connect_engine::send_level_data() const { // Send initial information. if (first_scenario_) { - network::send_data(config_of + send_to_server(config_of ("create_game", config_of ("name", params_.name) ("password", params_.password) ) ); - network::send_data(level_, sock); + send_to_server(level_); } else { - network::send_data(config_of("update_game", config()), 0); + send_to_server(config_of("update_game", config())); config next_level; next_level.add_child("store_next_scenario", level_); - network::send_data(next_level, sock); + send_to_server(next_level); } } diff --git a/src/game_initialization/connect_engine.hpp b/src/game_initialization/connect_engine.hpp index c995a9763335..ae57d8c6e363 100644 --- a/src/game_initialization/connect_engine.hpp +++ b/src/game_initialization/connect_engine.hpp @@ -49,7 +49,7 @@ class connect_engine public: /// @param players the player which are already connected to the current game. /// This is always empty unless we advance form a previous scenario. - connect_engine(saved_game& state, + connect_engine(saved_game& state, const bool first_scenario, mp_campaign_info* campaign_info); ~connect_engine(); @@ -74,8 +74,7 @@ class connect_engine // Return pair first element specifies whether to leave the game // and second element whether to silently update UI. - std::pair process_network_data(const config& data, - const network::connection sock); + std::pair process_network_data(const config& data); void process_network_error(network::error& error); // Returns the side which is taken by a given user, @@ -107,7 +106,7 @@ class connect_engine connect_engine(const connect_engine&); void operator=(const connect_engine&); - void send_level_data(const network::connection sock) const; + void send_level_data() const; void save_reserved_sides_information(); void load_previous_sides_users(); @@ -134,6 +133,8 @@ class connect_engine std::vector player_teams_; std::set& connected_users_rw(); + void send_to_server(const config& cfg) const; + bool receive_from_server(config& dst) const; }; class side_engine diff --git a/src/game_initialization/mp_game_utils.cpp b/src/game_initialization/mp_game_utils.cpp index 8c1344ed7451..0151288a5d0d 100644 --- a/src/game_initialization/mp_game_utils.cpp +++ b/src/game_initialization/mp_game_utils.cpp @@ -128,7 +128,7 @@ void level_to_gamestate(const config& level, saved_game& state) state.mp_settings().show_connect = show_connect; } -void check_response(network::connection res, const config& data) +void check_response(bool res, const config& data) { if (!res) { throw network::error(_("Connection timed out")); diff --git a/src/game_initialization/mp_game_utils.hpp b/src/game_initialization/mp_game_utils.hpp index f4844d06d0c2..dccd40142d6d 100644 --- a/src/game_initialization/mp_game_utils.hpp +++ b/src/game_initialization/mp_game_utils.hpp @@ -26,7 +26,7 @@ config initial_level_config(saved_game& state); void level_to_gamestate(const config& level, saved_game& state); -void check_response(network::connection res, const config& data); +void check_response(bool res, const config& data); } // end namespace mp diff --git a/src/game_initialization/multiplayer.cpp b/src/game_initialization/multiplayer.cpp index 027bd2fb4422..066f7f3beb01 100644 --- a/src/game_initialization/multiplayer.cpp +++ b/src/game_initialization/multiplayer.cpp @@ -26,6 +26,7 @@ #include "gui/dialogs/multiplayer/mp_connect.hpp" #include "gui/dialogs/multiplayer/mp_create_game.hpp" #include "gui/dialogs/multiplayer/mp_login.hpp" +#include "gui/dialogs/network_transmission.hpp" #include "gui/widgets/settings.hpp" #include "gui/widgets/window.hpp" #include "hash.hpp" @@ -48,6 +49,7 @@ #include "statistics.hpp" #include "units/id.hpp" #include "video.hpp" +#include "wesnothd_connection.hpp" #include "game_config_manager.hpp" #include "utils/functional.hpp" @@ -63,24 +65,6 @@ namespace { mp::chat gamechat; config gamelist; -class network_game_manager -{ -public: - // Add a constructor to avoid stupid warnings with some versions of GCC - network_game_manager() {} - - ~network_game_manager() - { - if(network::nconnections() > 0) { - LOG_NW << "sending leave_game\n"; - config cfg; - cfg.add_child("leave_game"); - network::send_data(cfg, 0); - LOG_NW << "sent leave_game\n"; - } - } -}; - } namespace mp { @@ -123,7 +107,7 @@ void run_lobby_loop(CVideo& video, mp::ui& ui) } -static bool open_connection(CVideo& video, const std::string& original_host) +static std::unique_ptr open_connection(CVideo& video, const std::string& original_host) { DBG_MP << "opening connection" << std::endl; std::string h = original_host; @@ -135,11 +119,10 @@ static bool open_connection(CVideo& video, const std::string& original_host) if(dlg.get_retval() == gui2::twindow::OK) { h = preferences::network_host(); } else { - return false; + return 0; } } - - network::connection sock; + std::unique_ptr sock; const int pos = h.find_first_of(":"); std::string host; @@ -160,21 +143,17 @@ static bool open_connection(CVideo& video, const std::string& original_host) shown_hosts.insert(hostpair(host, port)); config data; - sock = dialogs::network_connect_dialog(video,_("Connecting to Server..."),host,port); + sock = dialogs::network_connect_dialog(video, _("Connecting to Server..."), host, port); do { if (!sock) { - return false; + return std::move(sock); } data.clear(); - network::connection data_res = dialogs::network_receive_dialog( - video, _("Reading from Server..."), data); - if (!data_res) { - return false; - } - mp::check_response(data_res, data); + dialogs::network_receive_dialog(video, "", data, *sock); + //mp::check_response(data_res, data); if (data.has_child("reject") || data.has_attribute("version")) { std::string version; @@ -202,10 +181,8 @@ static bool open_connection(CVideo& video, const std::string& original_host) throw network::error(_("Server-side redirect loop")); } shown_hosts.insert(hostpair(host, port)); - - if(network::nconnections() > 0) - network::disconnect(); - sock = dialogs::network_connect_dialog(video,_("Connecting to Server..."),host,port); + sock.release(); + sock = dialogs::network_connect_dialog(video, _("Connecting to Server..."), host, port); continue; } @@ -214,7 +191,7 @@ static bool open_connection(CVideo& video, const std::string& original_host) config res; cfg["version"] = game_config::version; res.add_child("version", cfg); - network::send_data(res, 0); + sock->send_data(res); } //if we got a direction to login @@ -241,14 +218,8 @@ static bool open_connection(CVideo& video, const std::string& original_host) // server to optimize ping frequency as needed. sp["selective_ping"] = true; } - network::send_data(response, 0); - - // Get response for our login request... - network::connection data_res = network::receive_data(data, 0, 3000); - if(!data_res) { - throw network::error(_("Connection timed out")); - } - + sock->send_data(response); + dialogs::network_receive_dialog(video, "login response", data, *sock); config *warning = &data.child("warning"); if(*warning) { @@ -270,7 +241,7 @@ static bool open_connection(CVideo& video, const std::string& original_host) warning_msg += _("Do you want to continue?"); if(gui2::show_message(video, _("Warning"), warning_msg, gui2::tmessage::yes_no_buttons) != gui2::twindow::OK) { - return false; + return 0; } } @@ -327,12 +298,8 @@ static bool open_connection(CVideo& video, const std::string& original_host) sp["password_reminder"] = password_reminder; // Once again send our request... - network::send_data(response, 0); + dialogs::network_receive_dialog(video, "", response, *sock); - network::connection data_res = network::receive_data(data, 0, 3000); - if(!data_res) { - throw network::error(_("Connection timed out")); - } error = &data.child("error"); @@ -399,7 +366,7 @@ static bool open_connection(CVideo& video, const std::string& original_host) break; // Cancel default: - return false; + return 0; } // If we have got a new username we have to start all over again @@ -417,9 +384,9 @@ static bool open_connection(CVideo& video, const std::string& original_host) preferences::set_network_host(h); if (data.child("join_lobby")) { - return true; + return sock; } else { - return false; + return 0; } } @@ -437,17 +404,16 @@ static bool open_connection(CVideo& video, const std::string& original_host) // creating the dialogs, then, according to the dialog result, of calling other // of those screen functions. -static void enter_wait_mode(CVideo& video, const config& game_config, - saved_game& state, bool observe, int current_turn = 0) +static void enter_wait_mode(CVideo& video, const config& game_config, saved_game& state, twesnothd_connection* wesnothd_connection, + bool observe, int current_turn = 0) { DBG_MP << "entering wait mode" << std::endl; mp::ui::result res; - network_game_manager m; gamelist.clear(); statistics::fresh_stats(); - mp_campaign_info campaign_info; + mp_campaign_info campaign_info(*wesnothd_connection); campaign_info.is_host = false; if(preferences::skip_mp_replay() || preferences::blindfold_replay()) { campaign_info.skip_replay_until_turn = current_turn; @@ -455,7 +421,7 @@ static void enter_wait_mode(CVideo& video, const config& game_config, } { - mp::wait ui(video, game_config, state, gamechat, gamelist); + mp::wait ui(video, wesnothd_connection, game_config, state, gamechat, gamelist); ui.join_game(observe); @@ -483,31 +449,30 @@ static void enter_wait_mode(CVideo& video, const config& game_config, } } -static void enter_create_mode(CVideo& video, const config& game_config, - saved_game& state, bool local_players_only = false); +static void enter_create_mode(CVideo& video, const config& game_config, saved_game& state, twesnothd_connection* wesnothd_connection, + bool local_players_only = false); static bool enter_connect_mode(CVideo& video, const config& game_config, - saved_game& state, + saved_game& state, twesnothd_connection* wesnothd_connection, bool local_players_only = false) { DBG_MP << "entering connect mode" << std::endl; mp::ui::result res; - const network::manager net_manager(1,1); - network_game_manager m; gamelist.clear(); statistics::fresh_stats(); - boost::optional campaign_info; + std::unique_ptr campaign_info; if(!local_players_only) { - campaign_info = mp_campaign_info(); + assert(wesnothd_connection); + campaign_info.reset(new mp_campaign_info(*wesnothd_connection)); campaign_info->connected_players.insert(preferences::login()); campaign_info->is_host = true; } { - ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, campaign_info.get_ptr())); - mp::connect ui(video, state.mp_settings().name, game_config, gamechat, gamelist, + ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, campaign_info.get())); + mp::connect ui(video, wesnothd_connection, state.mp_settings().name, game_config, gamechat, gamelist, *connect_engine); run_lobby_loop(video, ui); @@ -523,28 +488,29 @@ static bool enter_connect_mode(CVideo& video, const config& game_config, switch (res) { case mp::ui::PLAY: { campaign_controller controller(video, state, game_config, game_config_manager::get()->terrain_types()); - controller.set_mp_info(campaign_info.get_ptr()); + controller.set_mp_info(campaign_info.get()); controller.play_game(); break; } case mp::ui::CREATE: - enter_create_mode(video, game_config, state, local_players_only); + enter_create_mode(video, game_config, state, wesnothd_connection, local_players_only); break; case mp::ui::QUIT: default: - network::send_data(config("refresh_lobby"), 0); + if (wesnothd_connection) { + wesnothd_connection->send_data(config("refresh_lobby")); + } return false; } return true; } -static bool enter_configure_mode(CVideo& video, const config& game_config, - saved_game& state, +static bool enter_configure_mode(CVideo& video, const config& game_config, saved_game& state, twesnothd_connection* wesnothd_connection, bool local_players_only = false); static void enter_create_mode(CVideo& video, const config& game_config, - saved_game& state, bool local_players_only) + saved_game& state, twesnothd_connection* wesnothd_connection, bool local_players_only) { DBG_MP << "entering create mode" << std::endl; @@ -561,13 +527,15 @@ static void enter_create_mode(CVideo& video, const config& game_config, dlg.show(video); - network::send_data(config("refresh_lobby"), 0); + if (wesnothd_connection) { + wesnothd_connection->send_data(config("refresh_lobby")); + } } else { mp::ui::result res; { - mp::create ui(video, game_config, state, gamechat, gamelist); + mp::create ui(video, wesnothd_connection, game_config, state, gamechat, gamelist); run_lobby_loop(video, ui); res = ui.get_result(); ui.get_parameters(); @@ -575,17 +543,17 @@ static void enter_create_mode(CVideo& video, const config& game_config, switch (res) { case mp::ui::CREATE: - configure_canceled = !enter_configure_mode(video, game_config, - state, local_players_only); + configure_canceled = !enter_configure_mode(video, game_config, state, wesnothd_connection, local_players_only); break; case mp::ui::LOAD_GAME: - connect_canceled = !enter_connect_mode(video, game_config, - state, local_players_only); + connect_canceled = !enter_connect_mode(video, game_config, state, wesnothd_connection, local_players_only); break; case mp::ui::QUIT: default: //update lobby content - network::send_data(config("refresh_lobby"), 0); + if (wesnothd_connection) { + wesnothd_connection->send_data(config("refresh_lobby")); + } break; } } @@ -593,7 +561,7 @@ static void enter_create_mode(CVideo& video, const config& game_config, } static bool enter_configure_mode(CVideo& video, const config& game_config, - saved_game& state, bool local_players_only) + saved_game& state, twesnothd_connection* wesnothd_connection, bool local_players_only) { DBG_MP << "entering configure mode" << std::endl; @@ -606,7 +574,7 @@ static bool enter_configure_mode(CVideo& video, const config& game_config, { if (state.get_starting_pos().child("side")) { - mp::configure ui(video, game_config, gamechat, gamelist, state, + mp::configure ui(video, wesnothd_connection, game_config, gamechat, gamelist, state, local_players_only); run_lobby_loop(video, ui); res = ui.get_result(); @@ -619,13 +587,14 @@ static bool enter_configure_mode(CVideo& video, const config& game_config, switch (res) { case mp::ui::CREATE: - connect_canceled = !enter_connect_mode(video, game_config, - state, local_players_only); + connect_canceled = !enter_connect_mode(video, game_config, state, wesnothd_connection, local_players_only); break; case mp::ui::QUIT: default: //update lobby content - network::send_data(config("refresh_lobby"), 0); + if (wesnothd_connection) { + wesnothd_connection->send_data(config("refresh_lobby")); + } return false; } } while(connect_canceled); @@ -654,8 +623,9 @@ static void do_preferences_dialog(CVideo& video, const config& game_config) } static void enter_lobby_mode(CVideo& video, const config& game_config, - saved_game& state, const std::vector & installed_addons) + saved_game& state, twesnothd_connection* wesnothd_connection, const std::vector & installed_addons) { + assert(wesnothd_connection); DBG_MP << "entering lobby mode" << std::endl; mp::ui::result res; @@ -703,7 +673,7 @@ static void enter_lobby_mode(CVideo& video, const config& game_config, res = mp::ui::QUIT; } } else { - mp::lobby ui(video, game_config, gamechat, gamelist, installed_addons); + mp::lobby ui(video, wesnothd_connection, game_config, gamechat, gamelist, installed_addons); run_lobby_loop(video, ui); res = ui.get_result(); current_turn = ui.current_turn; @@ -713,23 +683,23 @@ static void enter_lobby_mode(CVideo& video, const config& game_config, case mp::ui::JOIN: case mp::ui::OBSERVE: try { - enter_wait_mode(video, game_config, state, res == mp::ui::OBSERVE, current_turn); + enter_wait_mode(video, game_config, state, wesnothd_connection, res == mp::ui::OBSERVE, current_turn); } catch(config::error& error) { if(!error.message.empty()) { gui2::show_error_message(video, error.message); } //update lobby content - network::send_data(config("refresh_lobby"), 0); + wesnothd_connection->send_data(config("refresh_lobby")); } break; case mp::ui::CREATE: try { - enter_create_mode(video, game_config, state, false); + enter_create_mode(video, game_config, state, wesnothd_connection, false); } catch(config::error& error) { if (!error.message.empty()) gui2::show_error_message(video, error.message); //update lobby content - network::send_data(config("refresh_lobby"), 0); + wesnothd_connection->send_data(config("refresh_lobby")); } break; case mp::ui::QUIT: @@ -738,7 +708,7 @@ static void enter_lobby_mode(CVideo& video, const config& game_config, { do_preferences_dialog(video, game_config); //update lobby content - network::send_data(config("refresh_lobby"), 0); + wesnothd_connection->send_data(config("refresh_lobby")); } break; default: @@ -756,7 +726,7 @@ void start_local_game(CVideo& video, const config& game_config, gamechat.clear_history(); gamelist.clear(); preferences::set_message_private(false); - enter_create_mode(video, game_config, state, true); + enter_create_mode(video, game_config, state, nullptr, true); } void start_local_game_commandline(CVideo& video, const config& game_config, @@ -840,7 +810,7 @@ void start_local_game_commandline(CVideo& video, const config& game_config, { ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, nullptr)); - mp::connect ui(video, parameters.name, game_config, gamechat, gamelist, + mp::connect ui(video, 0, parameters.name, game_config, gamechat, gamelist, *connect_engine); // Update the parameters to reflect game start conditions @@ -873,16 +843,16 @@ void start_client(CVideo& video, const config& game_config, DBG_MP << "starting client" << std::endl; preferences::admin_authentication_reset r; - const network::manager net_manager(1,1); gamechat.clear_history(); gamelist.clear(); - if(open_connection(video, host)) { + std::unique_ptr wesnothd_connection = open_connection(video, host); + if(wesnothd_connection) { bool re_enter; do { re_enter = false; try { - enter_lobby_mode(video, *game_config_ptr, state, installed_addons); + enter_lobby_mode(video, *game_config_ptr, state, wesnothd_connection.get(), installed_addons); } catch (lobby_reload_request_exception &) { re_enter = true; game_config_manager * gcm = game_config_manager::get(); @@ -893,19 +863,19 @@ void start_client(CVideo& video, const config& game_config, installed_addons = ::installed_addons(); // Refersh the installed add-on list for this session. gamelist.clear(); //needed to make sure we update which games we have content for - network::send_data(config("refresh_lobby"), 0); + wesnothd_connection->send_data(config("refresh_lobby")); } } while (re_enter); } } mp::ui::result goto_mp_connect(CVideo& video, ng::connect_engine& engine, - const config& game_config, const std::string& game_name) + const config& game_config, twesnothd_connection* wesnothd_connection, const std::string& game_name) { mp::ui::result res; { - mp::connect ui(video, game_name, game_config, gamechat, gamelist, + mp::connect ui(video, wesnothd_connection, game_name, game_config, gamechat, gamelist, engine); run_lobby_loop(video, ui); @@ -918,13 +888,12 @@ mp::ui::result goto_mp_connect(CVideo& video, ng::connect_engine& engine, return res; } -mp::ui::result goto_mp_wait(saved_game& state, CVideo& video, - const config& game_config, bool observe) +mp::ui::result goto_mp_wait(CVideo& video, saved_game& state, const config& game_config, twesnothd_connection* wesnothd_connection, bool observe) { mp::ui::result res; { - mp::wait ui(video, game_config, state, gamechat, gamelist, false); + mp::wait ui(video, wesnothd_connection, game_config, state, gamechat, gamelist, false); ui.join_game(observe); run_lobby_loop(video, ui); diff --git a/src/game_initialization/multiplayer.hpp b/src/game_initialization/multiplayer.hpp index 8d8f887532eb..bd0290639da7 100644 --- a/src/game_initialization/multiplayer.hpp +++ b/src/game_initialization/multiplayer.hpp @@ -21,7 +21,7 @@ class config; class CVideo; - +class twesnothd_connection; namespace mp { // max. length of a player name @@ -63,14 +63,13 @@ void start_client(CVideo& video, const config& game_config, * changes made. */ mp::ui::result goto_mp_connect(CVideo& video, ng::connect_engine& engine, - const config& game_config, const std::string& game_name); + const config& game_config, twesnothd_connection* wesnothd_connection, const std::string& game_name); /** * Opens mp::wait screen and sets game state according to the * changes made. */ -mp::ui::result goto_mp_wait(saved_game& state, CVideo& video, - const config& game_config, bool observe); +mp::ui::result goto_mp_wait(CVideo& video, saved_game& state, const config& game_config, twesnothd_connection* wesnothd_connection, bool observe); } #endif diff --git a/src/game_initialization/multiplayer_configure.cpp b/src/game_initialization/multiplayer_configure.cpp index e38645e874b9..0faa551af7d4 100644 --- a/src/game_initialization/multiplayer_configure.cpp +++ b/src/game_initialization/multiplayer_configure.cpp @@ -62,8 +62,8 @@ configure::nolock_settings::nolock_settings(CVideo& video) { } -configure::configure(CVideo& video, const config &cfg, chat& c, config& gamelist, saved_game& game, bool local_players_only) : - ui(video, _("Configure Game"), cfg, c, gamelist), +configure::configure(CVideo& video, twesnothd_connection* wesnothd_connection, const config &cfg, chat& c, config& gamelist, saved_game& game, bool local_players_only) : + ui(video, wesnothd_connection, _("Configure Game"), cfg, c, gamelist), local_players_only_(local_players_only), tooltip_manager_(video), diff --git a/src/game_initialization/multiplayer_configure.hpp b/src/game_initialization/multiplayer_configure.hpp index 044c0b03881a..7934754357fd 100644 --- a/src/game_initialization/multiplayer_configure.hpp +++ b/src/game_initialization/multiplayer_configure.hpp @@ -34,7 +34,7 @@ class configure : public mp::ui public: ///gives the user the option to adjust the passed saved_game ///Call get_parameters to finalize; - configure(CVideo& v, const config& game_config, chat& c, config& gamelist, saved_game& game, bool local_players_only); + configure(CVideo& v, twesnothd_connection* wesnothd_connection, const config& game_config, chat& c, config& gamelist, saved_game& game, bool local_players_only); ~configure(); void get_parameters(); diff --git a/src/game_initialization/multiplayer_connect.cpp b/src/game_initialization/multiplayer_connect.cpp index d8f9fb1334a2..b4c15ef312a4 100644 --- a/src/game_initialization/multiplayer_connect.cpp +++ b/src/game_initialization/multiplayer_connect.cpp @@ -363,10 +363,10 @@ void connect::side::update_controller_ui() } } -connect::connect(CVideo& v, const std::string& game_name, +connect::connect(CVideo& v, twesnothd_connection* wesnothd_connection, const std::string& game_name, const config& game_config, chat& c, config& gamelist, ng::connect_engine& engine) : - mp::ui(v, _("Game Lobby: ") + game_name, game_config, c, gamelist), + mp::ui(v, wesnothd_connection, _("Game Lobby: ") + game_name, game_config, c, gamelist), ai_algorithms_(), sides_(), engine_(engine), @@ -470,10 +470,10 @@ void connect::process_event_impl(const process_event_data & data) } if (data.quit) { - if (network::nconnections() > 0) { + if (wesnothd_connection_) { config cfg; cfg.add_child("leave_game"); - network::send_data(cfg, 0); + send_to_server(cfg); } set_result(QUIT); @@ -560,13 +560,12 @@ void connect::layout_children(const SDL_Rect& rect) scroll_pane_.set_location(scroll_pane_rect); } -void connect::process_network_data(const config& data, - const network::connection sock) +void connect::process_network_data(const config& data) { - ui::process_network_data(data, sock); + ui::process_network_data(data); bool was_able_to_start = engine_.can_start_game(); - std::pair result = engine_.process_network_data(data, sock); + std::pair result = engine_.process_network_data(data); if (result.first) { set_result(QUIT); diff --git a/src/game_initialization/multiplayer_connect.hpp b/src/game_initialization/multiplayer_connect.hpp index 38229528d059..dce4d48ec7cb 100644 --- a/src/game_initialization/multiplayer_connect.hpp +++ b/src/game_initialization/multiplayer_connect.hpp @@ -92,7 +92,7 @@ class connect : public mp::ui typedef std::vector side_list; - connect(CVideo& v, const std::string& game_name, + connect(CVideo& v, twesnothd_connection* wesnothd_connection, const std::string& game_name, const config& game_config, chat& c, config& gamelist, ng::connect_engine& engine); ~connect(); @@ -109,8 +109,7 @@ class connect : public mp::ui virtual void layout_children(const SDL_Rect& rect); virtual void hide_children(bool hide = true); - virtual void process_network_data(const config& data, - const network::connection sock); + virtual void process_network_data(const config& data); virtual void process_network_error(network::error& error); private: diff --git a/src/game_initialization/multiplayer_create.cpp b/src/game_initialization/multiplayer_create.cpp index a58875bc7381..0451d3ab1359 100644 --- a/src/game_initialization/multiplayer_create.cpp +++ b/src/game_initialization/multiplayer_create.cpp @@ -82,9 +82,8 @@ static config find_helper(const ng::create_engine * eng_ptr, const config & cfg) return config_of("index", eng.find_level_by_id(str))("type", eng.find_level_type_by_id(str)); } -create::create(CVideo& video, const config& cfg, saved_game& state, - chat& c, config& gamelist) : - ui(video, _("Create Game"), cfg, c, gamelist), +create::create(CVideo& video, twesnothd_connection* wesnothd_connection, const config& cfg, saved_game& state, chat& c, config& gamelist) : + ui(video, wesnothd_connection, _("Create Game"), cfg, c, gamelist), tooltip_manager_(video), era_selection_(-1), mod_selection_(-1), diff --git a/src/game_initialization/multiplayer_create.hpp b/src/game_initialization/multiplayer_create.hpp index 6b82f753e1ed..c4383ca27be4 100644 --- a/src/game_initialization/multiplayer_create.hpp +++ b/src/game_initialization/multiplayer_create.hpp @@ -30,7 +30,7 @@ namespace mp { class create : public mp::ui { public: - create(CVideo& v, const config& game_config, saved_game& state, + create(CVideo& v, twesnothd_connection* wesnothd_connection, const config& game_config, saved_game& state, chat& c, config& gamelist); ~create(); diff --git a/src/game_initialization/multiplayer_lobby.cpp b/src/game_initialization/multiplayer_lobby.cpp index 6101ac4bbea2..c2864994180f 100644 --- a/src/game_initialization/multiplayer_lobby.cpp +++ b/src/game_initialization/multiplayer_lobby.cpp @@ -1023,8 +1023,8 @@ bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gu return basic_sorter::less(column,row1,row2); } -lobby::lobby(CVideo& v, const config& cfg, chat& c, config& gamelist, const std::vector & installed_addons) : - mp::ui(v, _("Game Lobby"), cfg, c, gamelist), +lobby::lobby(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, chat& c, config& gamelist, const std::vector & installed_addons) : + mp::ui(v, wesnothd_connection, _("Game Lobby"), cfg, c, gamelist), current_turn(0), game_vacant_slots_(), @@ -1296,7 +1296,7 @@ void lobby::process_event_impl(const process_event_data & data) if(!password.empty()) { join["password"] = password; } - network::send_data(response, 0); + send_to_server(response); if(observe) { this->current_turn = game.current_turn; @@ -1372,9 +1372,9 @@ bool lobby::plugin_event_helper(const process_event_data & data) return get_result() == mp::ui::CONTINUE; } -void lobby::process_network_data(const config& data, const network::connection sock) +void lobby::process_network_data(const config& data) { - ui::process_network_data(data, sock); + ui::process_network_data(data); // Invalidate game selection for the player list last_selected_game_ = -1; diff --git a/src/game_initialization/multiplayer_lobby.hpp b/src/game_initialization/multiplayer_lobby.hpp index ddc7f033ff79..a9c571716c62 100644 --- a/src/game_initialization/multiplayer_lobby.hpp +++ b/src/game_initialization/multiplayer_lobby.hpp @@ -186,7 +186,7 @@ class gamebrowser : public gui::menu { class lobby : public ui { public: - lobby(CVideo& v, const config& cfg, chat& c, config& gamelist, const std::vector & installed_addons); + lobby(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, chat& c, config& gamelist, const std::vector & installed_addons); virtual void process_event(); @@ -194,7 +194,7 @@ class lobby : public ui protected: virtual void hide_children(bool hide=true); virtual void layout_children(const SDL_Rect& rect); - virtual void process_network_data(const config& data, const network::connection sock); + virtual void process_network_data(const config& data); virtual void gamelist_updated(bool silent=true); private: diff --git a/src/game_initialization/multiplayer_ui.cpp b/src/game_initialization/multiplayer_ui.cpp index 21327ad697b9..53d52ca08fa6 100644 --- a/src/game_initialization/multiplayer_ui.cpp +++ b/src/game_initialization/multiplayer_ui.cpp @@ -35,6 +35,7 @@ #include "team.hpp" #include "sdl/utils.hpp" #include "sdl/rect.hpp" +#include "wesnothd_connection.hpp" static lg::log_domain log_config("config"); #define ERR_CF LOG_STREAM(err, log_config) @@ -178,9 +179,10 @@ SDL_Color chat::color_message(const msg& message) { return c; } -ui::ui(CVideo& video, const std::string& title, const config& cfg, chat& c, config& gamelist) : +ui::ui(CVideo& video, twesnothd_connection* wesnothd_connection, const std::string& title, const config& cfg, chat& c, config& gamelist) : gui::widget(video), video_(video), + wesnothd_connection_(wesnothd_connection), initialized_(false), gamelist_initialized_(false), @@ -216,10 +218,8 @@ void ui::process_network() { config data; try { - const network::connection sock = network::receive_data(data); - - if(sock) { - process_network_data(data, sock); + if(receive_from_server(data)) { + process_network_data(data); } } catch(network::error& e) { process_network_error(e); @@ -369,7 +369,7 @@ void ui::handle_event(const SDL_Event& event) //if the dialog has been open for a long time, refresh the lobby config request; request.add_child("refresh_lobby"); - network::send_data(request, 0); + send_to_server(request); } } if(users_menu_.selection() > 0 // -1 indicates an invalid selection @@ -393,7 +393,7 @@ void ui::send_chat_message(const std::string& message, bool /*allies_only*/) data.add_child("message", msg); add_chat_message(time(nullptr), preferences::login(),0, message); //local echo - network::send_data(data, 0); + send_to_server(data); } void ui::handle_key_event(const SDL_KeyboardEvent& event) @@ -514,7 +514,7 @@ void ui::process_message(const config& msg, const bool whisper) { plugins_manager::get()->notify_event("chat", temp); //notify plugins of the network message } -void ui::process_network_data(const config& data, const network::connection /*sock*/) +void ui::process_network_data(const config& data) { if (const config &c = data.child("error")) { throw network::error(c["message"]); @@ -536,7 +536,7 @@ void ui::process_network_data(const config& data, const network::connection /*so } catch(config::error& e) { ERR_CF << "Error while applying the gamelist diff: '" << e.message << "' Getting a new gamelist.\n"; - network::send_data(config("refresh_lobby"), 0); + send_to_server(config("refresh_lobby")); } gamelist_refresh_ = true; } @@ -789,13 +789,21 @@ const gui::label& ui::title() const return title_; } -plugins_context * ui::get_plugins_context() { +plugins_context * ui::get_plugins_context() +{ return plugins_context_.get(); } void ui::send_to_server(const config& cfg) { - network::send_data(cfg, 0); + if (wesnothd_connection_) { + wesnothd_connection_->send_data(cfg); + } +} + +bool ui::receive_from_server(config& cfg) +{ + return wesnothd_connection_ && wesnothd_connection_->receive_data(cfg); } }// namespace mp diff --git a/src/game_initialization/multiplayer_ui.hpp b/src/game_initialization/multiplayer_ui.hpp index 60dc892ba896..019741517444 100644 --- a/src/game_initialization/multiplayer_ui.hpp +++ b/src/game_initialization/multiplayer_ui.hpp @@ -33,6 +33,7 @@ class game_display; class config; class plugins_context; +class twesnothd_connection; namespace mp { std::string get_color_string(int id); @@ -79,7 +80,7 @@ class ui : public gui::widget, private events::chat_handler, private font::float enum result { CONTINUE, JOIN, OBSERVE, CREATE, LOAD_GAME, PREFERENCES, PLAY, QUIT }; - ui(CVideo& v, const std::string& title, + ui(CVideo& v, twesnothd_connection* wesnothd_connection, const std::string& title, const config& cfg, chat& c, config& gamelist); /** @@ -105,6 +106,7 @@ class ui : public gui::widget, private events::chat_handler, private font::float using widget::set_location; const std::vector& user_list() const { return user_list_; } void send_to_server(const config& cfg) override; + bool receive_from_server(config& dst); protected: int xscale(int x) const; int yscale(int y) const; @@ -114,6 +116,7 @@ class ui : public gui::widget, private events::chat_handler, private font::float SDL_Rect client_area() const; CVideo& video_; + twesnothd_connection* wesnothd_connection_; CVideo& video() { return video_; } /** @@ -144,7 +147,7 @@ class ui : public gui::widget, private events::chat_handler, private font::float * process_network() method. Overridden by subclasses who add more * behavior for network. */ - virtual void process_network_data(const config& data, const network::connection sock); + virtual void process_network_data(const config& data); /** * Processes any pending network error. Called by the public diff --git a/src/game_initialization/multiplayer_wait.cpp b/src/game_initialization/multiplayer_wait.cpp index 77466fc3674c..890bd02cc75a 100644 --- a/src/game_initialization/multiplayer_wait.cpp +++ b/src/game_initialization/multiplayer_wait.cpp @@ -193,9 +193,9 @@ sdl_handler_vector wait::leader_preview_pane::handler_members() { } -wait::wait(CVideo& v, const config& cfg, saved_game& state, +wait::wait(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, saved_game& state, mp::chat& c, config& gamelist, const bool first_scenario) : - ui(v, _("Game Lobby"), cfg, c, gamelist), + ui(v, wesnothd_connection, _("Game Lobby"), cfg, c, gamelist), cancel_button_(video(), first_scenario ? _("Cancel") : _("Quit")), start_label_(video(), _("Waiting for game to start..."), font::SIZE_SMALL, font::LOBBY_COLOR), game_menu_(video(), std::vector(), false, -1, -1, nullptr, &gui::menu::bluebg_style), @@ -385,7 +385,7 @@ void wait::join_game(bool observe) change["faction"] = flg.current_faction()["id"]; change["leader"] = flg.current_leader(); change["gender"] = flg.current_gender(); - network::send_data(faction, 0); + send_to_server(faction); } } @@ -429,9 +429,9 @@ void wait::hide_children(bool hide) game_menu_.hide(hide); } -void wait::process_network_data(const config& data, const network::connection sock) +void wait::process_network_data(const config& data) { - ui::process_network_data(data, sock); + ui::process_network_data(data); if(!data["message"].empty()) { gui2::show_transient_message(video() @@ -618,16 +618,17 @@ void wait::generate_menu() bool wait::download_level_data() { + assert(wesnothd_connection_); DBG_MP << "download_level_data()\n"; if (!first_scenario_) { // Ask for the next scenario data. - network::send_data(config("load_next_scenario"), 0); + send_to_server(config("load_next_scenario")); } bool has_scenario_and_controllers = false; while (!has_scenario_and_controllers) { config revc; - network::connection data_res = dialogs::network_receive_dialog( - video(), _("Getting game data..."), revc); + bool data_res = dialogs::network_receive_dialog( + video(), _("Getting game data..."), revc, *wesnothd_connection_); if (!data_res) { DBG_MP << "download_level_data bad results\n"; @@ -654,6 +655,7 @@ bool wait::download_level_data() } has_scenario_and_controllers = true; } + } DBG_MP << "download_level_data() success.\n"; diff --git a/src/game_initialization/multiplayer_wait.hpp b/src/game_initialization/multiplayer_wait.hpp index 6b7387ce4ff6..24386fc748c0 100644 --- a/src/game_initialization/multiplayer_wait.hpp +++ b/src/game_initialization/multiplayer_wait.hpp @@ -24,7 +24,7 @@ namespace mp { class wait : public ui { public: - wait(CVideo& v, const config& cfg, saved_game& state, chat& c, + wait(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, saved_game& state, chat& c, config& gamelist, const bool first_scenario = true); ~wait(); virtual void process_event(); @@ -36,7 +36,7 @@ class wait : public ui protected: virtual void layout_children(const SDL_Rect& rect); virtual void hide_children(bool hide=true); - virtual void process_network_data(const config& data, const network::connection sock); + virtual void process_network_data(const config& data); private: class leader_preview_pane : public gui::preview_pane diff --git a/src/game_initialization/playcampaign.cpp b/src/game_initialization/playcampaign.cpp index 6b8c2e39ab10..4e1147689c68 100644 --- a/src/game_initialization/playcampaign.cpp +++ b/src/game_initialization/playcampaign.cpp @@ -352,8 +352,8 @@ LEVEL_RESULT campaign_controller::play_game() if (mp_info_ && !mp_info_->is_host) { // Opens mp::connect dialog to get a new gamestate. - mp::ui::result wait_res = mp::goto_mp_wait(state_, video_, - game_config_, res == LEVEL_RESULT::OBSERVER_END); + mp::ui::result wait_res = mp::goto_mp_wait(video_, state_, + game_config_, &mp_info_->wesnothd_connection, res == LEVEL_RESULT::OBSERVER_END); if (wait_res == mp::ui::QUIT) { return LEVEL_RESULT::QUIT; } @@ -377,7 +377,7 @@ LEVEL_RESULT campaign_controller::play_game() if (!connect_engine->can_start_game() || (game_config::debug && game_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER)) { // Opens mp::connect dialog to allow users to make an adjustments for scenario. mp::ui::result connect_res = mp::goto_mp_connect(video_, - *connect_engine, game_config_, state_.mp_settings().name); + *connect_engine, game_config_, mp_info_ ? &mp_info_->wesnothd_connection : nullptr, state_.mp_settings().name); if (connect_res == mp::ui::QUIT) { return LEVEL_RESULT::QUIT; } diff --git a/src/game_initialization/playcampaign.hpp b/src/game_initialization/playcampaign.hpp index d9407840fc0a..cf572e51fde6 100644 --- a/src/game_initialization/playcampaign.hpp +++ b/src/game_initialization/playcampaign.hpp @@ -34,14 +34,15 @@ typedef boost::shared_ptr tdata_cache; class config; +class twesnothd_connection; struct mp_campaign_info { - mp_campaign_info() + mp_campaign_info(twesnothd_connection& wdc) : connected_players() , is_host() , skip_replay_until_turn(0) , skip_replay_blindfolded(false) - , is_connected(true) + , wesnothd_connection(wdc) { } @@ -50,7 +51,7 @@ struct mp_campaign_info bool is_host; int skip_replay_until_turn; bool skip_replay_blindfolded; - bool is_connected; + twesnothd_connection& wesnothd_connection; }; class campaign_controller diff --git a/src/game_initialization/singleplayer.cpp b/src/game_initialization/singleplayer.cpp index 652ad61505a7..1f45531123b0 100644 --- a/src/game_initialization/singleplayer.cpp +++ b/src/game_initialization/singleplayer.cpp @@ -136,7 +136,7 @@ bool enter_configure_mode(CVideo& video, const config& game_config, saved_game& mp::ui::result res; { - mp::configure ui(video, game_config, gamechat, gamelist, state, local_players_only); + mp::configure ui(video, 0, game_config, gamechat, gamelist, state, local_players_only); mp::run_lobby_loop(video, ui); res = ui.get_result(); ui.get_parameters(); @@ -163,7 +163,7 @@ bool enter_connect_mode(CVideo& video, const config& game_config, mp::ui::result res; gamelist.clear(); { - mp::connect ui(video, state.mp_settings().name, game_config, gamechat, gamelist, connect_eng); + mp::connect ui(video, 0, state.mp_settings().name, game_config, gamechat, gamelist, connect_eng); mp::run_lobby_loop(video, ui); res = ui.get_result(); diff --git a/src/gui/dialogs/network_transmission.cpp b/src/gui/dialogs/network_transmission.cpp index 811440e2cb50..94b1c6bf5caf 100644 --- a/src/gui/dialogs/network_transmission.cpp +++ b/src/gui/dialogs/network_transmission.cpp @@ -34,20 +34,15 @@ REGISTER_DIALOG(network_transmission) void tnetwork_transmission::pump_monitor::process(events::pump_info&) { - connection_.poll(); + connection_->poll(); if(!window_) return; - if(connection_.done()) { + if(connection_->finished()) { window_.get().set_retval(twindow::OK); } else { size_t completed, total; - if(track_upload_) { - completed = connection_.bytes_written(); - total = connection_.bytes_to_write(); - } else { - completed = connection_.bytes_read(); - total = connection_.bytes_to_read(); - } + completed = connection_->current(); + total = connection_->total(); if(total) { find_widget(&(window_.get()), "progress", false) .set_percentage((completed * 100.) / total); @@ -64,12 +59,11 @@ void tnetwork_transmission::pump_monitor::process(events::pump_info&) } tnetwork_transmission::tnetwork_transmission( - network_asio::connection& connection, + connection_data& connection, const std::string& title, const std::string& subtitle) - : connection_(connection) - , track_upload_(false) - , pump_monitor_(connection, track_upload_) + : connection_(&connection) + , pump_monitor_(connection_) , subtitle_(subtitle) { register_label("title", true, title, false); @@ -97,7 +91,7 @@ void tnetwork_transmission::pre_show(twindow& window) void tnetwork_transmission::post_show(twindow& /*window*/) { pump_monitor_.window_.reset(); - connection_.cancel(); + connection_->cancel(); } } // namespace gui2 diff --git a/src/gui/dialogs/network_transmission.hpp b/src/gui/dialogs/network_transmission.hpp index 763728a12564..76d5f9c1dac1 100644 --- a/src/gui/dialogs/network_transmission.hpp +++ b/src/gui/dialogs/network_transmission.hpp @@ -24,7 +24,6 @@ namespace gui2 { -class tnetwork_transmission; /** * Dialog that tracks network transmissions @@ -34,19 +33,27 @@ class tnetwork_transmission; */ class tnetwork_transmission : public tdialog { - network_asio::connection& connection_; - - bool track_upload_; +public: + class connection_data + { + public: + virtual size_t total() { return 0; } + virtual size_t current() { return 0; } + virtual bool finished() = 0; + virtual void cancel() = 0; + virtual void poll() = 0; + }; +private: + connection_data* connection_; class pump_monitor : public events::pump_monitor { - network_asio::connection& connection_; - bool& track_upload_; + public: + connection_data*& connection_; virtual void process(events::pump_info&); - public: - pump_monitor(network_asio::connection& connection, bool& track_upload) - : connection_(connection), track_upload_(track_upload), window_() + pump_monitor(connection_data*& connection) + : connection_(connection), window_() { } @@ -54,14 +61,14 @@ class tnetwork_transmission : public tdialog } pump_monitor_; public: - tnetwork_transmission(network_asio::connection& connection, + tnetwork_transmission(connection_data& connection, const std::string& title, const std::string& subtitle); void set_subtitle(const std::string&); - void set_track_upload(bool track_upload) + void set_connection_data(connection_data& connection) { - track_upload_ = track_upload; + connection_ = &connection; } protected: diff --git a/src/play_controller.hpp b/src/play_controller.hpp index a2e0181a70c9..4944f97c3e72 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -268,6 +268,7 @@ class play_controller : public controller_base, public events::observer, public virtual bool is_networked_mp() const { return false; } virtual void send_to_wesnothd(const config&, const std::string& = "unknown") const { } + virtual bool recieve_from_wesnothd(config&) const { return false; } protected: struct scoped_savegame_snapshot { diff --git a/src/playmp_controller.cpp b/src/playmp_controller.cpp index 0ee9f69f8994..838adbe9278b 100644 --- a/src/playmp_controller.cpp +++ b/src/playmp_controller.cpp @@ -36,6 +36,7 @@ #include "countdown_clock.hpp" #include "synced_context.hpp" #include "replay_helper.hpp" +#include "wesnothd_connection.hpp" static lg::log_domain log_engine("engine"); #define LOG_NG LOG_STREAM(info, log_engine) @@ -282,10 +283,10 @@ void playmp_controller::wait_for_upload() network_reader_.set_source(playturn_network_adapter::get_source_from_config(cfg)); while(true) { try { - const network::connection res = dialogs::network_receive_dialog( - gui_->video(), _("Waiting for next scenario..."), cfg); + const bool res = dialogs::network_receive_dialog( + gui_->video(), _("Waiting for next scenario..."), cfg, mp_info_->wesnothd_connection); - if(res != network::null_connection) { + if(res) { if (turn_data_.process_network_data_from_reader() == turn_info::PROCESS_END_LINGER) { break; } @@ -296,12 +297,12 @@ void playmp_controller::wait_for_upload() } } catch(const quit_game_exception&) { - network_reader_.set_source(playturn_network_adapter::read_network); + network_reader_.set_source([this](config& cfg) { return recieve_from_wesnothd(cfg);}); turn_data_.send_data(); throw; } } - network_reader_.set_source(playturn_network_adapter::read_network); + network_reader_.set_source([this](config& cfg) { return recieve_from_wesnothd(cfg);}); } void playmp_controller::after_human_turn(){ @@ -479,10 +480,21 @@ void playmp_controller::process_network_data() } bool playmp_controller::is_networked_mp() const { - return network::nconnections() != 0; + return mp_info_ != nullptr; } -void playmp_controller::send_to_wesnothd(const config& cfg, const std::string& packet_type) const +void playmp_controller::send_to_wesnothd(const config& cfg, const std::string&) const { - network::send_data(cfg, 0, packet_type); + if (mp_info_ != nullptr) { + mp_info_->wesnothd_connection.send_data(cfg); + } +} +bool playmp_controller::recieve_from_wesnothd(config& cfg) const +{ + if (mp_info_ != nullptr) { + return mp_info_->wesnothd_connection.receive_data(cfg); + } + else { + return false; + } } diff --git a/src/playmp_controller.hpp b/src/playmp_controller.hpp index 1b25e5503211..6b76dc377837 100644 --- a/src/playmp_controller.hpp +++ b/src/playmp_controller.hpp @@ -40,6 +40,7 @@ class playmp_controller : public playsingle_controller, public syncmp_handler bool is_networked_mp() const override; void send_to_wesnothd(const config& cfg, const std::string& packet_type = "unknown") const override; + bool recieve_from_wesnothd(config& cfg) const override; protected: virtual void handle_generic_event(const std::string& name); diff --git a/src/playsingle_controller.cpp b/src/playsingle_controller.cpp index 9fd52efe7cef..46971a6e41cf 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -69,14 +69,14 @@ static lg::log_domain log_enginerefac("enginerefac"); #define LOG_RG LOG_STREAM(info, log_enginerefac) playsingle_controller::playsingle_controller(const config& level, - saved_game& state_of_game, - const config& game_config, const tdata_cache & tdata, - CVideo& video, bool skip_replay) + saved_game& state_of_game, + const config& game_config, const tdata_cache & tdata, + CVideo& video, bool skip_replay) : play_controller(level, state_of_game, game_config, tdata, video, skip_replay) , cursor_setter(cursor::NORMAL) , textbox_info_() , replay_sender_(*resources::recorder) - , network_reader_() + , network_reader_([this](config& cfg) {return recieve_from_wesnothd(cfg);}) , turn_data_(replay_sender_, network_reader_) , end_turn_(END_TURN_NONE) , skip_next_turn_(false) diff --git a/src/playturn.cpp b/src/playturn.cpp index 69a6b88bfaa3..8032b60d1670 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -66,7 +66,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::sync_network() { //there should be nothing left on the replay and we should get turn_info::PROCESS_CONTINUE back. turn_info::PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay()); - if(network::nconnections() > 0) { + if(resources::controller->is_networked_mp()) { //receive data first, and then send data. When we sent the end of //the AI's turn, we don't want there to be any chance where we @@ -126,9 +126,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data_from_reader() turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg) { - // we cannot be connected to multiple peers anymore because // the simple wesnothserver implementation in wesnoth was removed years ago. - assert(network::nconnections() <= 1); assert(cfg.all_children_count() == 1); assert(cfg.attribute_range().first == cfg.attribute_range().second); if(!resources::recorder->at_end()) diff --git a/src/playturn_network_adapter.cpp b/src/playturn_network_adapter.cpp index 50866996d9d8..7f8e015289cf 100644 --- a/src/playturn_network_adapter.cpp +++ b/src/playturn_network_adapter.cpp @@ -123,6 +123,7 @@ playturn_network_adapter::playturn_network_adapter(source_type source) } + playturn_network_adapter::~playturn_network_adapter() { try { @@ -156,9 +157,4 @@ static bool read_config(config& src, config& dst) playturn_network_adapter::source_type playturn_network_adapter::get_source_from_config(config& cfg) { return std::bind(read_config, std::ref(cfg), _1); -} - -bool playturn_network_adapter::read_network(config& cfg) -{ - return network::receive_data(cfg) != network::null_connection; -} +} \ No newline at end of file diff --git a/src/playturn_network_adapter.hpp b/src/playturn_network_adapter.hpp index 92e9fdad9c23..62041cf321d5 100644 --- a/src/playturn_network_adapter.hpp +++ b/src/playturn_network_adapter.hpp @@ -15,7 +15,7 @@ class playturn_network_adapter public: typedef std::function source_type; - playturn_network_adapter(source_type source = read_network); + playturn_network_adapter(source_type source); ~playturn_network_adapter(); //returns true on success. @@ -27,8 +27,6 @@ class playturn_network_adapter void set_source(source_type source); //returns a function to be passed to set_source. static source_type get_source_from_config(config& src); - //a function to be passed to set_source. - static bool read_network(config& dst); private: //reads data from the network stream. void read_from_network(); diff --git a/src/replay.cpp b/src/replay.cpp index 213e01a1c8da..dbc79fd05fe7 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -902,7 +902,7 @@ void replay_network_sender::commit_and_sync() const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands())); if(data.empty() == false) { - resources::controller->send_to_wesnothd(data); + resources::controller->send_to_wesnothd(cfg); } upto_ = obj_.ncommands(); diff --git a/src/wesnothd_connection.cpp b/src/wesnothd_connection.cpp new file mode 100644 index 000000000000..47ce893cdd2e --- /dev/null +++ b/src/wesnothd_connection.cpp @@ -0,0 +1,249 @@ +/* + Copyright (C) 2011 - 2016 by Sergey Popov + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + 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. + + See the COPYING file for more details. +*/ + +#include +#include "utils/functional.hpp" +#include +#include +#include +#include "log.hpp" +#include "wesnothd_connection.hpp" +#include "serialization/parser.hpp" + +static lg::log_domain log_network("network"); +#define DBG_NW LOG_STREAM(debug, log_network) +#define LOG_NW LOG_STREAM(info, log_network) +#define WRN_NW LOG_STREAM(warn, log_network) +#define ERR_NW LOG_STREAM(err, log_network) + +using boost::system::system_error; +using boost::system::error_code; + +twesnothd_connection::twesnothd_connection(const std::string& host, const std::string& service) + : io_service_() + , resolver_(io_service_) + , socket_(io_service_) + , handshake_finished_(false) + , read_buf_() + , handshake_response_() + , payload_size_(0) + , bytes_to_write_(0) + , bytes_written_(0) + , bytes_to_read_(0) + , bytes_read_(0) +{ + resolver_.async_resolve( + boost::asio::ip::tcp::resolver::query(host, service), + std::bind(&twesnothd_connection::handle_resolve, this, _1, _2) + ); + LOG_NW << "Resolving hostname: " << host << '\n'; +} + +void twesnothd_connection::handle_resolve(const error_code& ec, resolver::iterator iterator) +{ + if (ec) { + throw system_error(ec); + } + connect(iterator); +} + +void twesnothd_connection::connect(resolver::iterator iterator) +{ + socket_.async_connect(*iterator, std::bind( + &twesnothd_connection::handle_connect, this, _1, iterator) + ); + LOG_NW << "Connecting to " << iterator->endpoint().address() << '\n'; +} + +void twesnothd_connection::handle_connect( + const boost::system::error_code& ec, + resolver::iterator iterator + ) +{ + if(ec) { + WRN_NW << "Failed to connect to " << + iterator->endpoint().address() << ": " << + ec.message() << '\n'; + socket_.close(); + if(++iterator == resolver::iterator()) { + ERR_NW << "Tried all IPs. Giving up" << std::endl; + throw system_error(ec); + } + else { + connect(iterator); + } + } else { + LOG_NW << "Connected to " << iterator->endpoint().address() << '\n'; + handshake(); + } +} + +void twesnothd_connection::handshake() +{ + static const boost::uint32_t handshake = 0; + boost::asio::async_write(socket_, + boost::asio::buffer(reinterpret_cast(&handshake), 4), + [](const error_code& ec, std::size_t) { if (ec) throw system_error(ec); } + ); + boost::asio::async_read(socket_, + boost::asio::buffer(&handshake_response_.binary, 4), + std::bind(&twesnothd_connection::handle_handshake, this, _1) + ); +} + +void twesnothd_connection::handle_handshake(const error_code& ec) +{ + if (ec) { + throw system_error(ec); + } + handshake_finished_ = true; + recv(); +} + +void twesnothd_connection::send_data(const config& request) +{ + poll(); + send_queue_.emplace_back(); + + std::ostream os(&send_queue_.back()); + write_gz(os, request); + if (send_queue_.size() == 1) { + send(); + } +} + +void twesnothd_connection::cancel() +{ + if(socket_.is_open()) { + boost::system::error_code ec; + socket_.cancel(ec); + if(ec) { + WRN_NW << "Failed to cancel network operations: " << ec.message() << std::endl; + } + } +} + +std::size_t twesnothd_connection::is_write_complete(const boost::system::error_code& ec, size_t bytes_transferred) +{ + if(ec) + throw system_error(ec); + bytes_written_ = bytes_transferred; + return bytes_to_write_ - bytes_transferred; +} + +void twesnothd_connection::handle_write( + const boost::system::error_code& ec, + std::size_t bytes_transferred + ) +{ + DBG_NW << "Written " << bytes_transferred << " bytes.\n"; + send_queue_.pop_front(); + if (ec) { + throw system_error(ec); + } + if (!send_queue_.empty()) { + send(); + } +} + +std::size_t twesnothd_connection::is_read_complete( + const boost::system::error_code& ec, + std::size_t bytes_transferred + ) +{ + if(ec) { + throw system_error(ec); + } + bytes_read_ = bytes_transferred; + if(bytes_transferred < 4) { + return 4; + } else { + if(!bytes_to_read_) { + std::istream is(&read_buf_); + union { char binary[4]; boost::uint32_t num; } data_size; + is.read(data_size.binary, 4); + bytes_to_read_ = ntohl(data_size.num) + 4; + //Close immediately if we receive an invalid length + if (bytes_to_read_ < 4) + bytes_to_read_ = bytes_transferred; + } + return bytes_to_read_ - bytes_transferred; + } +} + +void twesnothd_connection::handle_read( + const boost::system::error_code& ec, + std::size_t bytes_transferred + ) +{ + DBG_NW << "Read " << bytes_transferred << " bytes.\n"; + bytes_to_read_ = 0; + if(ec && ec != boost::asio::error::eof) + throw system_error(ec); + std::istream is(&read_buf_); + recv_queue_.push_back(config()); + read_gz(recv_queue_.back(), is); + DBG_NW << "Received " << recv_queue_.back() << " bytes.\n"; + + recv(); +} + +void twesnothd_connection::send() +{ + auto& buf = send_queue_.front(); + size_t buf_size = buf.size(); + bytes_to_write_ = buf_size + 4; + bytes_written_ = 0; + payload_size_ = htonl(buf_size); + + boost::asio::streambuf::const_buffers_type gzipped_data = buf.data(); + std::deque bufs(gzipped_data.begin(), gzipped_data.end()); + bufs.push_front(boost::asio::buffer(reinterpret_cast(&payload_size_), 4)); + boost::asio::async_write(socket_, bufs, + std::bind(&twesnothd_connection::is_write_complete, this, _1, _2), + std::bind(&twesnothd_connection::handle_write, this, _1, _2) + ); +} + +void twesnothd_connection::recv() +{ + boost::asio::async_read(socket_, read_buf_, + std::bind(&twesnothd_connection::is_read_complete, this, _1, _2), + std::bind(&twesnothd_connection::handle_read, this, _1, _2) + ); +} + +std::size_t twesnothd_connection::poll() +{ + try { + return io_service_.poll(); + } + catch (const boost::system::system_error& err) { + if (err.code() == boost::asio::error::operation_aborted) + return 1; + throw error(err.code()); + } +} +bool twesnothd_connection::receive_data(config& result) +{ + poll(); + if (recv_queue_.empty()) { + return false; + } + else { + result.swap(recv_queue_.front()); + recv_queue_.pop_front(); + return true; + } +} diff --git a/src/wesnothd_connection.hpp b/src/wesnothd_connection.hpp new file mode 100644 index 000000000000..1f63b154d3f4 --- /dev/null +++ b/src/wesnothd_connection.hpp @@ -0,0 +1,145 @@ +/* + Copyright (C) 2011 - 2016 by Sergey Popov + Part of the Battle for Wesnoth Project http://www.wesnoth.org/ + + 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. + + See the COPYING file for more details. +*/ + +#pragma once + +#ifdef _WIN32 +# define BOOST_ASIO_DISABLE_IOCP +# ifdef INADDR_ANY +# undef INADDR_ANY +# endif +# ifdef INADDR_BROADCAST +# undef INADDR_BROADCAST +# endif +# ifdef INADDR_NONE +# undef INADDR_NONE +# endif +#endif + +#include +#include +#include +#include +#include "exceptions.hpp" + +class config; + +/** A class that represents a TCP/IP connection to the wesnothd server. */ +class twesnothd_connection : boost::noncopyable +{ +public: + + struct error : public game::error + { + error(const boost::system::error_code& error) : game::error(error.message()) {} + }; + + /** + * Constructor. + * + * @param host Name of the host to connect to + * @param service Service identifier such as "80" or "http" + */ + twesnothd_connection(const std::string& host, const std::string& service); + + void send_data(const config& request); + + bool receive_data(config& result); + + /** Handle all pending asynchonous events and return */ + std::size_t poll(); + /** Run asio's event loop + * + * Handle asynchronous events blocking until all asynchronous + * operations have finished + */ + + void cancel(); + + /** True if connected and no high-level operation is in progress */ + bool handshake_finished() const { return handshake_finished_; } + + std::size_t bytes_to_write() const + { + return bytes_to_write_; + } + std::size_t bytes_written() const + { + return bytes_written_; + } + std::size_t bytes_to_read() const + { + return bytes_to_read_; + } + std::size_t bytes_read() const + { + return bytes_read_; + } + bool has_data_received() const + { + return !recv_queue_.empty(); + } + bool is_sending_data() const + { + return !send_queue_.empty(); + } +private: + boost::asio::io_service io_service_; + typedef boost::asio::ip::tcp::resolver resolver; + resolver resolver_; + + typedef boost::asio::ip::tcp::socket socket; + socket socket_; + + bool handshake_finished_; + + boost::asio::streambuf read_buf_; + + void handle_resolve( + const boost::system::error_code& ec, + resolver::iterator iterator + ); + + void connect(resolver::iterator iterator); + void handle_connect( + const boost::system::error_code& ec, + resolver::iterator iterator + ); + void handshake(); + void handle_handshake( + const boost::system::error_code& ec + ); + union { + char binary[4]; + boost::uint32_t num; + } handshake_response_; + + std::size_t is_write_complete(const boost::system::error_code& error, std::size_t bytes_transferred); + void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred); + std::size_t is_read_complete(const boost::system::error_code& error, std::size_t bytes_transferred); + void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred); + + void send(); + void recv(); + + std::list send_queue_; + std::list recv_queue_; + + boost::uint32_t payload_size_; + std::size_t bytes_to_write_; + std::size_t bytes_written_; + std::size_t bytes_to_read_; + std::size_t bytes_read_; + +};