use boost asio on clientside wesnothd connection

The main advantage is that the new code is much shorter and easier to
understand then the preivous one.

It currently still used the network::error class on some places

This also removed support for 'ping' packages, the plan is to replace it
with SO_KEEPALIVE

Also this temporaily breaks the gui2 lobby since it still uses the old
network code.
This commit is contained in:
gfgtdf 2016-06-01 18:35:37 +02:00
parent c27a026608
commit a6b114f6ae
38 changed files with 692 additions and 354 deletions

View file

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

View file

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

View file

@ -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<gui2::tnetwork_transmission::connection_data> 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_)) {

View file

@ -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 <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
@ -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<gui::button*> 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<twesnothd_connection> 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;
std::unique_ptr<twesnothd_connection> 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);
const events::event_context dialog_events_context;
gui::button cancel_button(v,_("Cancel"));
std::vector<gui::button*> 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();
connect_waiter waiter(v,cancel_button);
return network::connect(hostname,port,waiter);
}
} // end namespace dialogs

View file

@ -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 @@ private:
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<twesnothd_connection> network_connect_dialog(CVideo& video, const std::string& msg, const std::string& hostname, int port);
} //end namespace dialogs

View file

@ -26,6 +26,8 @@
#include "playcampaign.hpp"
#include "tod_manager.hpp"
#include "multiplayer_ui.hpp" // For get_color_string
#include "wesnothd_connection.hpp"
#include <boost/assign.hpp>
#include <stdlib.h>
#include <ctime>
@ -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<std::string> side_engine::get_children_to_swap()
{
std::vector<std::string> 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<bool, bool> connect_engine::process_network_data(const config& data,
const network::connection sock)
std::pair<bool, bool> connect_engine::process_network_data(const config& data)
{
std::pair<bool, bool> result(std::make_pair(false, true));
@ -628,7 +647,7 @@ std::pair<bool, bool> 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<bool, bool> 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<bool, bool> 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<bool, bool> 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<bool, bool> 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);
}
}

View file

@ -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 @@ public:
// Return pair first element specifies whether to leave the game
// and second element whether to silently update UI.
std::pair<bool, bool> process_network_data(const config& data,
const network::connection sock);
std::pair<bool, bool> 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 @@ private:
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 @@ private:
std::vector<std::string> player_teams_;
std::set<std::string>& connected_users_rw();
void send_to_server(const config& cfg) const;
bool receive_from_server(config& dst) const;
};
class side_engine

View file

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

View file

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

View file

@ -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<twesnothd_connection> 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<twesnothd_connection> 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<mp_campaign_info> campaign_info;
std::unique_ptr<mp_campaign_info> 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<std::string> & installed_addons)
saved_game& state, twesnothd_connection* wesnothd_connection, const std::vector<std::string> & 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<twesnothd_connection> 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);

View file

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

View file

@ -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),

View file

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

View file

@ -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<bool, bool> result = engine_.process_network_data(data, sock);
std::pair<bool, bool> result = engine_.process_network_data(data);
if (result.first) {
set_result(QUIT);

View file

@ -92,7 +92,7 @@ public:
typedef std::vector<side> 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 @@ 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);
virtual void process_network_error(network::error& error);
private:

View file

@ -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),

View file

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

View file

@ -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<std::string> & 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<std::string> & 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;

View file

@ -186,7 +186,7 @@ private:
class lobby : public ui
{
public:
lobby(CVideo& v, const config& cfg, chat& c, config& gamelist, const std::vector<std::string> & installed_addons);
lobby(CVideo& v, twesnothd_connection* wesnothd_connection, const config& cfg, chat& c, config& gamelist, const std::vector<std::string> & installed_addons);
virtual void process_event();
@ -194,7 +194,7 @@ public:
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:

View file

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

View file

@ -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 @@ public:
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 @@ public:
using widget::set_location;
const std::vector<std::string>& 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 @@ protected:
SDL_Rect client_area() const;
CVideo& video_;
twesnothd_connection* wesnothd_connection_;
CVideo& video() { return video_; }
/**
@ -144,7 +147,7 @@ protected:
* 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

View file

@ -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<std::string>(), 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";

View file

@ -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 @@ public:
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

View file

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

View file

@ -34,14 +34,15 @@ typedef boost::shared_ptr<terrain_type_data> 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

View file

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

View file

@ -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<tprogress_bar>(&(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

View file

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

View file

@ -268,6 +268,7 @@ 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
{

View file

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

View file

@ -40,6 +40,7 @@ public:
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);

View file

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

View file

@ -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())

View file

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

View file

@ -15,7 +15,7 @@ class playturn_network_adapter
public:
typedef std::function<bool(config&)> 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 @@ public:
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();

View file

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

249
src/wesnothd_connection.cpp Normal file
View file

@ -0,0 +1,249 @@
/*
Copyright (C) 2011 - 2016 by Sergey Popov <loonycyborg@gmail.com>
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 <deque>
#include "utils/functional.hpp"
#include <boost/ref.hpp>
#include <boost/cstdint.hpp>
#include <boost/version.hpp>
#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<const char*>(&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<boost::asio::const_buffer> bufs(gzipped_data.begin(), gzipped_data.end());
bufs.push_front(boost::asio::buffer(reinterpret_cast<const char*>(&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;
}
}

145
src/wesnothd_connection.hpp Normal file
View file

@ -0,0 +1,145 @@
/*
Copyright (C) 2011 - 2016 by Sergey Popov <loonycyborg@gmail.com>
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 <boost/asio.hpp>
#include <boost/optional.hpp>
#include <deque>
#include <list>
#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<boost::asio::streambuf> send_queue_;
std::list<config> 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_;
};