diff --git a/src/server/common/forum_user_handler.cpp b/src/server/common/forum_user_handler.cpp index 2eadc62eb1e5..89a9b0190958 100644 --- a/src/server/common/forum_user_handler.cpp +++ b/src/server/common/forum_user_handler.cpp @@ -15,6 +15,7 @@ #ifdef HAVE_MYSQLPP #include "server/common/forum_user_handler.hpp" +#include "server/wesnothd/server.hpp" #include "hash.hpp" #include "log.hpp" #include "config.hpp" @@ -207,10 +208,10 @@ std::string fuh::get_tournaments(){ return conn_.get_tournaments(); } -void fuh::async_get_and_send_game_history(boost::asio::io_service& io_service, server_base& s_base, socket_ptr player_socket, int player_id, int offset) { - boost::asio::post([this, &s_base, player_socket, player_id, offset, &io_service] { - boost::asio::post(io_service, [player_socket, &s_base, doc = conn_.get_game_history(player_id, offset)]{ - s_base.async_send_doc_queued(player_socket, *doc); +void fuh::async_get_and_send_game_history(boost::asio::io_service& io_service, wesnothd::server& s, wesnothd::player_iterator player, int player_id, int offset) { + boost::asio::post([this, &s, player, player_id, offset, &io_service] { + boost::asio::post(io_service, [player, &s, doc = conn_.get_game_history(player_id, offset)]{ + s.send_to_player(player, *doc); }); }); } diff --git a/src/server/common/forum_user_handler.hpp b/src/server/common/forum_user_handler.hpp index f16d6461723b..c1d455a5c041 100644 --- a/src/server/common/forum_user_handler.hpp +++ b/src/server/common/forum_user_handler.hpp @@ -130,7 +130,7 @@ class fuh : public user_handler * @param player_id The forum ID of the player to get the game history for. * @param offset Where to start returning rows to the client from the query results. */ - void async_get_and_send_game_history(boost::asio::io_service& io_service, server_base& s_base, socket_ptr player_socket, int player_id, int offset); + void async_get_and_send_game_history(boost::asio::io_service& io_service, wesnothd::server& s, wesnothd::player_iterator player, int player_id, int offset); /** * Inserts game related information. diff --git a/src/server/common/server_base.cpp b/src/server/common/server_base.cpp index dc319b5086c8..fef8bcb50f69 100644 --- a/src/server/common/server_base.cpp +++ b/src/server/common/server_base.cpp @@ -219,17 +219,17 @@ void server_base::run() { } } -std::string client_address(const socket_ptr socket) +template std::string client_address(SocketPtr socket) { boost::system::error_code error; - std::string result = socket->remote_endpoint(error).address().to_string(); + std::string result = socket->lowest_layer().remote_endpoint(error).address().to_string(); if(error) return ""; else return result; } -bool check_error(const boost::system::error_code& error, socket_ptr socket) +template bool check_error(const boost::system::error_code& error, SocketPtr socket) { if(error) { if(error == boost::asio::error::eof) @@ -240,6 +240,7 @@ bool check_error(const boost::system::error_code& error, socket_ptr socket) } return false; } +template bool check_error(const boost::system::error_code& error, tls_socket_ptr socket); namespace { @@ -263,7 +264,7 @@ void info_table_into_simple_wml(simple_wml::document& doc, const std::string& pa * @param doc * @param yield The function will suspend on write operation using this yield context */ -void server_base::coro_send_doc(socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield) +template void server_base::coro_send_doc(SocketPtr socket, simple_wml::document& doc, boost::asio::yield_context yield) { if(dump_wml) { std::cout << "Sending WML to " << client_address(socket) << ": \n" << doc.output() << std::endl; @@ -290,10 +291,12 @@ void server_base::coro_send_doc(socket_ptr socket, simple_wml::document& doc, bo throw; } } +template void server_base::coro_send_doc(socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield); +template void server_base::coro_send_doc(tls_socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield); #ifdef HAVE_SENDFILE -void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield) +template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { std::size_t filesize { std::size_t(filesystem::file_size(filename)) }; int in_file { open(filename.c_str(), O_RDONLY) }; @@ -350,7 +353,7 @@ void server_base::coro_send_file(socket_ptr socket, const std::string& filename, #elif defined(_WIN32) -void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield) +template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { OVERLAPPED overlap; @@ -411,7 +414,7 @@ void server_base::coro_send_file(socket_ptr socket, const std::string& filename, #else -void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield) +template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { // TODO: Implement this for systems without sendfile() assert(false && "Not implemented yet"); @@ -419,7 +422,7 @@ void server_base::coro_send_file(socket_ptr socket, const std::string& filename, #endif -std::unique_ptr server_base::coro_receive_doc(socket_ptr socket, boost::asio::yield_context yield) +template std::unique_ptr server_base::coro_receive_doc(SocketPtr socket, boost::asio::yield_context yield) { union DataSize { @@ -457,12 +460,14 @@ std::unique_ptr server_base::coro_receive_doc(socket_ptr s return {}; } } +template std::unique_ptr server_base::coro_receive_doc(socket_ptr socket, boost::asio::yield_context yield); +template std::unique_ptr server_base::coro_receive_doc(tls_socket_ptr socket, boost::asio::yield_context yield); -void server_base::async_send_doc_queued(socket_ptr socket, simple_wml::document& doc) +template void server_base::async_send_doc_queued(SocketPtr socket, simple_wml::document& doc) { boost::asio::spawn( io_service_, [this, doc_ptr = doc.clone(), socket](boost::asio::yield_context yield) mutable { - static std::map>> queues; + static std::map>> queues; queues[socket].push(std::move(doc_ptr)); if(queues[socket].size() > 1) { @@ -478,7 +483,7 @@ void server_base::async_send_doc_queued(socket_ptr socket, simple_wml::document& ); } -void server_base::async_send_error(socket_ptr socket, const std::string& msg, const char* error_code, const info_table& info) +template void server_base::async_send_error(SocketPtr socket, const std::string& msg, const char* error_code, const info_table& info) { simple_wml::document doc; doc.root().add_child("error").set_attr_dup("message", msg.c_str()); @@ -489,8 +494,10 @@ void server_base::async_send_error(socket_ptr socket, const std::string& msg, co async_send_doc_queued(socket, doc); } +template void server_base::async_send_error(socket_ptr socket, const std::string& msg, const char* error_code, const info_table& info); +template void server_base::async_send_error(tls_socket_ptr socket, const std::string& msg, const char* error_code, const info_table& info); -void server_base::async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code, const info_table& info) +template void server_base::async_send_warning(SocketPtr socket, const std::string& msg, const char* warning_code, const info_table& info) { simple_wml::document doc; doc.root().add_child("warning").set_attr_dup("message", msg.c_str()); @@ -501,6 +508,8 @@ void server_base::async_send_warning(socket_ptr socket, const std::string& msg, async_send_doc_queued(socket, doc); } +template void server_base::async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code, const info_table& info); +template void server_base::async_send_warning(tls_socket_ptr socket, const std::string& msg, const char* warning_code, const info_table& info); void server_base::load_tls_config(const config& cfg) { diff --git a/src/server/common/server_base.hpp b/src/server/common/server_base.hpp index 2be065f15835..7e1263f8ba93 100644 --- a/src/server/common/server_base.hpp +++ b/src/server/common/server_base.hpp @@ -66,20 +66,20 @@ class server_base * @param doc * @param yield The function will suspend on write operation using this yield context */ - void coro_send_doc(socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield); + template void coro_send_doc(SocketPtr socket, simple_wml::document& doc, boost::asio::yield_context yield); /** * Send contents of entire file directly to socket from within a coroutine * @param socket * @param filename * @param yield The function will suspend on write operations using this yield context */ - void coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield); + template void coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield); /** * Receive WML document from a coroutine * @param socket * @param yield The function will suspend on read operation using this yield context */ - std::unique_ptr coro_receive_doc(socket_ptr socket, boost::asio::yield_context yield); + template std::unique_ptr coro_receive_doc(SocketPtr socket, boost::asio::yield_context yield); /** * High level wrapper for sending a WML document @@ -89,11 +89,11 @@ class server_base * @param socket * @param doc Document to send. A copy of it will be made so there is no need to keep the reference live after the function returns. */ - void async_send_doc_queued(socket_ptr socket, simple_wml::document& doc); + template void async_send_doc_queued(SocketPtr socket, simple_wml::document& doc); typedef std::map info_table; - void async_send_error(socket_ptr socket, const std::string& msg, const char* error_code = "", const info_table& info = {}); - void async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code = "", const info_table& info = {}); + template void async_send_error(SocketPtr socket, const std::string& msg, const char* error_code = "", const info_table& info = {}); + template void async_send_warning(SocketPtr socket, const std::string& msg, const char* warning_code = "", const info_table& info = {}); protected: unsigned short port_; @@ -135,5 +135,5 @@ class server_base void handle_termination(const boost::system::error_code& error, int signal_number); }; -std::string client_address(socket_ptr socket); -bool check_error(const boost::system::error_code& error, socket_ptr socket); +template std::string client_address(SocketPtr socket); +template bool check_error(const boost::system::error_code& error, SocketPtr socket); diff --git a/src/server/common/user_handler.hpp b/src/server/common/user_handler.hpp index 00c5b8cda356..30b16d8ef46f 100644 --- a/src/server/common/user_handler.hpp +++ b/src/server/common/user_handler.hpp @@ -17,13 +17,19 @@ class config; #include "exceptions.hpp" -#include "server/common/server_base.hpp" #include #include #include +#include "server/wesnothd/player_connection.hpp" + +namespace wesnothd +{ + class server; +} + /** * An interface class to handle nick registration * To activate it put a [user_handler] section into the @@ -135,7 +141,7 @@ class user_handler virtual std::string get_uuid() = 0; virtual std::string get_tournaments() = 0; - virtual void async_get_and_send_game_history(boost::asio::io_service& io_service, server_base& s_base, socket_ptr player_socket, int player_id, int offset) =0; + virtual void async_get_and_send_game_history(boost::asio::io_service& io_service, wesnothd::server& s, wesnothd::player_iterator player, int player_id, int offset) =0; virtual void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name, int reload, int observers, int is_public, int has_password) = 0; virtual void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location) = 0; virtual void db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source, const std::string& current_user) = 0; diff --git a/src/server/wesnothd/game.cpp b/src/server/wesnothd/game.cpp index 90e5671822cd..7fa5afcc0724 100644 --- a/src/server/wesnothd/game.cpp +++ b/src/server/wesnothd/game.cpp @@ -350,7 +350,7 @@ bool game::send_taken_side(simple_wml::document& cfg, const simple_wml::node* si cfg.root().set_attr_dup("side", (*side)["side"]); // Tell the host which side the new player should take. - server.async_send_doc_queued(owner_->socket(), cfg); + server.send_to_player(owner_, cfg); return true; } @@ -602,7 +602,7 @@ void game::change_controller( // side_drop already.) if(!player_left) { response->root().child("change_controller")->set_attr("is_local", "yes"); - server.async_send_doc_queued(player->socket(), *response.get()); + server.send_to_player(player, *response.get()); } } @@ -630,7 +630,7 @@ void game::notify_new_host() cfg.root().add_child("host_transfer"); std::string message = owner_name + " has been chosen as the new host."; - server.async_send_doc_queued(owner_->socket(), cfg); + server.send_to_player(owner_, cfg); send_and_record_server_message(message); } @@ -772,7 +772,7 @@ void game::unmute_observer(const simple_wml::node& unmute, player_iterator unmut void game::send_leave_game(player_iterator user) const { static simple_wml::document leave_game("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED); - server.async_send_doc_queued(user->socket(), leave_game); + server.send_to_player(user, leave_game); } std::optional game::kick_member(const simple_wml::node& kick, player_iterator kicker) @@ -986,7 +986,7 @@ bool game::process_turn(simple_wml::document& data, player_iterator user) msg << "Removing illegal command '" << (*command).first_child().to_string() << "' from: " << username(user) << ". Current player is: " << username(*current_player()) << " (" << current_side_index_ + 1 << "/" << nsides_ << ")."; - LOG_GAME << msg.str() << " (socket: " << (*current_player())->socket() << ") (game id: " << id_ << ", " << db_id_ << ")\n"; + LOG_GAME << msg.str() << " (game id: " << id_ << ", " << db_id_ << ")\n"; send_and_record_server_message(msg.str()); marked.push_back(index - marked.size()); @@ -1187,7 +1187,7 @@ void game::handle_controller_choice(const simple_wml::node& req) command.set_attr("dependent", "yes"); if(sides_[side_index]) { - server.async_send_doc_queued((*sides_[side_index])->socket(), *mdata); + server.send_to_player((*sides_[side_index]), *mdata); } change_controller_wml.set_attr("is_local", "no"); @@ -1226,7 +1226,7 @@ void game::handle_choice(const simple_wml::node& data, player_iterator user) } DBG_GAME << "answering seed request " << request_id << " by player " - << user->info().name() << "(" << user->socket() << ")" << std::endl; + << user->info().name() << std::endl; last_choice_request_id_ = request_id; if(const simple_wml::node* rand = data.child("random_seed")) { @@ -1351,7 +1351,7 @@ void game::update_turn_data() bool game::add_player(player_iterator player, bool observer) { if(is_member(player)) { - ERR_GAME << "ERROR: Player is already in this game. (socket: " << player->socket() << ")\n"; + ERR_GAME << "ERROR: Player is already in this game.\n"; return false; } @@ -1393,20 +1393,19 @@ bool game::add_player(player_iterator player, bool observer) LOG_GAME << player->client_ip() << "\t" << user->info().name() << "\tjoined game:\t\"" - << name_ << "\" (" << id_ << ", " << db_id_ << ")" << (observer ? " as an observer" : "") << ". (socket: " << player->socket() - << ")\n"; + << name_ << "\" (" << id_ << ", " << db_id_ << ")" << (observer ? " as an observer" : "") << ".\n"; user->info().mark_available(id_, name_); user->info().set_status((observer) ? player::OBSERVING : player::PLAYING); DBG_GAME << debug_player_info(); // Send the user the game data. - server.async_send_doc_queued(player->socket(), level_); + server.send_to_player(player, level_); if(started_) { // Tell this player that the game has started static simple_wml::document start_game_doc("[start_game]\n[/start_game]\n", simple_wml::INIT_COMPRESSED); - server.async_send_doc_queued(player->socket(), start_game_doc); + server.send_to_player(player, start_game_doc); // Send observer join of all the observers in the game to the new player // only once the game started. The client forgets about it anyway otherwise. @@ -1435,7 +1434,7 @@ bool game::add_player(player_iterator player, bool observer) bool game::remove_player(player_iterator player, const bool disconnect, const bool destruct) { if(!is_member(player)) { - ERR_GAME << "ERROR: User is not in this game. (socket: " << player->socket() << ")\n"; + ERR_GAME << "ERROR: User is not in this game.\n"; return false; } @@ -1462,8 +1461,7 @@ bool game::remove_player(player_iterator player, const bool disconnect, const bo ? " at turn: " + lexical_cast_default(current_turn()) + " with reason: '" + termination_reason() + "'" : "") - << (observer ? " as an observer" : "") << (disconnect ? " and disconnected" : "") << ". (socket: " << user->socket() - << ")\n"; + << (observer ? " as an observer" : "") << (disconnect ? " and disconnected" : "") << ".\n"; if(game_ended && started_ && !(observer && destruct)) { send_server_message_to_all(user->info().name() + " ended the game.", player); @@ -1529,7 +1527,7 @@ bool game::remove_player(player_iterator player, const bool disconnect, const bo DBG_GAME << "*** sending side drop: \n" << drop.output() << std::endl; - server.async_send_doc_queued(owner_->socket(), drop); + server.send_to_player(owner_, drop); } if(ai_transfer) { @@ -1617,8 +1615,8 @@ void game::load_next_scenario(player_iterator user) cfg_controller.set_attr("is_local", side_user == user ? "yes" : "no"); } - server.async_send_doc_queued(user->socket(), cfg_scenario); - server.async_send_doc_queued(user->socket(), doc_controllers); + server.send_to_player(user, cfg_scenario); + server.send_to_player(user, doc_controllers); players_not_advanced_.erase(&*user); @@ -1634,7 +1632,7 @@ void game::send_to_players(simple_wml::document& data, const Container& players, { for(const auto& player : players) { if(player != exclude) { - server.async_send_doc_queued(player->socket(), data); + server.send_to_player(player, data); } } } @@ -1703,7 +1701,7 @@ void game::send_observerjoins(std::optional player) send_data(cfg, ob); } else { // Send to the (new) user. - server.async_send_doc_queued((*player)->socket(), cfg); + server.send_to_player(*player, cfg); } } } @@ -1738,7 +1736,7 @@ void game::send_history(player_iterator player) const auto doc = std::make_unique(buf.c_str(), simple_wml::INIT_STATIC); doc->compress(); - server.async_send_doc_queued(player->socket(), *doc); + server.send_to_player(player, *doc); history_.clear(); history_.push_back(std::move(doc)); @@ -1907,12 +1905,11 @@ std::string game::debug_sides_info() const result << "\t\t level, server\n"; for(const simple_wml::node* s : sides) { - result + result << "side " << (*s)["side"].to_int() << " :\t" << (*s)["controller"].to_string() << "\t, " << side_controllers_[(*s)["side"].to_int() - 1].to_cstring() - << "\t( " << (*sides_[(*s)["side"].to_int() - 1])->socket() - << ",\t" << (*s)["current_player"].to_string() << " )\n"; + << "\t( " << (*s)["current_player"].to_string() << " )\n"; } return result.str(); @@ -1971,7 +1968,7 @@ void game::send_server_message(const char* message, std::optionalsocket(), doc); + server.send_to_player(*player, doc); } } diff --git a/src/server/wesnothd/player_connection.hpp b/src/server/wesnothd/player_connection.hpp index a94cd614ace5..2a4df0a2a8f1 100644 --- a/src/server/wesnothd/player_connection.hpp +++ b/src/server/wesnothd/player_connection.hpp @@ -31,7 +31,8 @@ class game; class player_record { public: - player_record(const socket_ptr socket, const player& player) + template + player_record(const SocketPtr socket, const player& player) : socket_(socket) , player_(player) , game_() @@ -39,7 +40,7 @@ class player_record { } - const socket_ptr socket() const + const any_socket_ptr socket() const { return socket_; } @@ -70,7 +71,7 @@ class player_record void enter_lobby(); private: - const socket_ptr socket_; + const any_socket_ptr socket_; mutable player player_; std::shared_ptr game_; std::string ip_address; @@ -84,7 +85,7 @@ namespace bmi = boost::multi_index; using player_connections = bmi::multi_index_container, - bmi::const_mem_fun>, + bmi::const_mem_fun>, bmi::hashed_unique, bmi::const_mem_fun>, bmi::ordered_non_unique, diff --git a/src/server/wesnothd/server.cpp b/src/server/wesnothd/server.cpp index 3534a5818a66..e010b7c390cb 100644 --- a/src/server/wesnothd/server.cpp +++ b/src/server/wesnothd/server.cpp @@ -541,7 +541,7 @@ bool server::ip_exceeds_connection_limit(const std::string& ip) const std::size_t connections = 0; for(const auto& player : player_connections_) { - if(client_address(player.socket()) == ip) { + if(player.client_ip() == ip) { ++connections; } } @@ -609,13 +609,13 @@ void server::handle_new_client(socket_ptr socket) boost::asio::spawn(io_service_, [socket, this](boost::asio::yield_context yield) { login_client(yield, socket); }); } -void server::handle_new_client(tls_socket_ptr /*socket*/) +void server::handle_new_client(tls_socket_ptr socket) { - //boost::asio::spawn(io_service_, [socket, this](boost::asio::yield_context yield) { login_client(yield, socket); }); - throw std::runtime_error("Not implemented"); + boost::asio::spawn(io_service_, [socket, this](boost::asio::yield_context yield) { login_client(yield, socket); }); } -void server::login_client(boost::asio::yield_context yield, socket_ptr socket) +template +void server::login_client(boost::asio::yield_context yield, SocketPtr socket) { boost::system::error_code ec; @@ -744,7 +744,7 @@ void server::login_client(boost::asio::yield_context yield, socket_ptr socket) } } -bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& username, bool& registered, bool& is_moderator) +template bool server::is_login_allowed(SocketPtr socket, const simple_wml::node* const login, const std::string& username, bool& registered, bool& is_moderator) { // Check if the username is valid (all alpha-numeric plus underscore and hyphen) if(!utils::isvalid_username(username)) { @@ -864,8 +864,8 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l return true; } -bool server::authenticate( - socket_ptr socket, const std::string& username, const std::string& password, bool name_taken, bool& registered) +template bool server::authenticate( + SocketPtr socket, const std::string& username, const std::string& password, bool name_taken, bool& registered) { // Current login procedure for registered nicks is: // - Client asks to log in with a particular nick @@ -971,7 +971,7 @@ bool server::authenticate( return true; } -void server::send_password_request(socket_ptr socket, +template void server::send_password_request(SocketPtr socket, const std::string& msg, const std::string& user, const char* error_code, @@ -1012,7 +1012,7 @@ void server::send_password_request(socket_ptr socket, async_send_doc_queued(socket, doc); } -void server::handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player_data) +template void server::handle_player(boost::asio::yield_context yield, SocketPtr socket, const player& player_data) { if(lan_server_) abort_lan_server_timer(); @@ -1104,7 +1104,7 @@ void server::handle_whisper(player_iterator player, simple_wml::node& whisper) simple_wml::INIT_COMPRESSED ); - async_send_doc_queued(player->socket(), data); + send_to_player(player, data); return; } @@ -1130,7 +1130,7 @@ void server::handle_whisper(player_iterator player, simple_wml::node& whisper) const simple_wml::string_span& msg = trunc_whisper["message"]; chat_message::truncate_message(msg, trunc_whisper); - async_send_doc_queued(receiver_iter->socket(), cwhisper); + send_to_player(player_connections_.project<0>(receiver_iter), cwhisper); } void server::handle_query(player_iterator iter, simple_wml::node& query) @@ -1269,13 +1269,13 @@ void server::handle_create_game(player_iterator player, simple_wml::node& create { if(graceful_restart) { static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED); - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "This server is shutting down. You aren't allowed to make new games. Please " "reconnect to the new server.", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } @@ -1358,16 +1358,16 @@ void server::handle_join_game(player_iterator player, simple_wml::node& join) if(!g) { WRN_SERVER << player->client_ip() << "\t" << player->info().name() << "\tattempted to join unknown game:\t" << game_id << ".\n"; - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "Attempt to join unknown game.", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } else if(!g->level_init()) { WRN_SERVER << player->client_ip() << "\t" << player->info().name() << "\tattempted to join uninitialized game:\t\"" << g->name() << "\" (" << game_id << ").\n"; - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "Attempt to join an uninitialized game.", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } else if(player->info().is_moderator()) { // Admins are always allowed to join. @@ -1375,16 +1375,16 @@ void server::handle_join_game(player_iterator player, simple_wml::node& join) DBG_SERVER << player->client_ip() << "\tReject banned player: " << player->info().name() << "\tfrom game:\t\"" << g->name() << "\" (" << game_id << ").\n"; - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "You are banned from this game.", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } else if(!g->password_matches(password)) { WRN_SERVER << player->client_ip() << "\t" << player->info().name() << "\tattempted to join game:\t\"" << g->name() << "\" (" << game_id << ") with bad password\n"; - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "Incorrect password.", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } @@ -1393,13 +1393,13 @@ void server::handle_join_game(player_iterator player, simple_wml::node& join) WRN_SERVER << player->client_ip() << "\t" << player->info().name() << "\tattempted to observe game:\t\"" << g->name() << "\" (" << game_id << ") which doesn't allow observers.\n"; - async_send_doc_queued(player->socket(), leave_game_doc); + send_to_player(player, leave_game_doc); send_server_message(player, "Attempt to observe a game that doesn't allow observers. (You probably joined the " "game shortly after it filled up.)", "error"); - async_send_doc_queued(player->socket(), games_and_users_list_); + send_to_player(player, games_and_users_list_); return; } @@ -1531,7 +1531,7 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data // Everything below should only be processed if the game is already initialized. } else if(!g.level_init()) { WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name() - << " (socket:" << p->socket() << ") while the scenario wasn't yet initialized.\n" + << " while the scenario wasn't yet initialized.\n" << data.output(); return; // If the host is sending the next scenario data. @@ -1686,7 +1686,7 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data } // Send the player who has quit the gamelist. - async_send_doc_queued(p->socket(), games_and_users_list_); + send_to_player(p, games_and_users_list_); } return; @@ -1761,7 +1761,7 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data send_to_lobby(gamelist_diff, p); // Send the removed user the lobby game list. - async_send_doc_queued((*user)->socket(), games_and_users_list_); + send_to_player(*user, games_and_users_list_); } return; @@ -1832,7 +1832,7 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data if(player_id != 0) { LOG_SERVER << "Querying game history requested by player `" << player.name() << "` for player id `" << player_id << "`." << std::endl; - user_handler_->async_get_and_send_game_history(io_service_, *this, p->socket(), player_id, offset); + user_handler_->async_get_and_send_game_history(io_service_, *this, p, player_id, offset); } } return; @@ -1846,12 +1846,12 @@ void server::handle_player_in_game(player_iterator p, simple_wml::document& data return; } - WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name() << " (socket:" << p->socket() - << ") in game: \"" << g.name() << "\" (" << g.id() << ", " << g.db_id() << ")\n" + WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name() + << " in game: \"" << g.name() << "\" (" << g.id() << ", " << g.db_id() << ")\n" << data.output(); } -void server::send_server_message(socket_ptr socket, const std::string& message, const std::string& type) +template void server::send_server_message(SocketPtr socket, const std::string& message, const std::string& type) { simple_wml::document server_message; simple_wml::node& msg = server_message.root().add_child("message"); @@ -1864,7 +1864,8 @@ void server::send_server_message(socket_ptr socket, const std::string& message, void server::disconnect_player(player_iterator player) { - player->socket()->shutdown(boost::asio::ip::tcp::socket::shutdown_receive); + // FIXME: this is not a correct way for TLS + utils::visit([](auto&& socket) { socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_receive); }, player->socket()); } void server::remove_player(player_iterator iter) @@ -1913,7 +1914,7 @@ void server::send_to_lobby(simple_wml::document& data, std::optional().equal_range(0)) { auto player { player_connections_.iterator_to(p) }; if(player != exclude) { - async_send_doc_queued(player->socket(), data); + send_to_player(player, data); } } } @@ -2219,7 +2220,7 @@ void server::adminmsg_handler( for(const auto& player : player_connections_) { if(player.info().is_moderator()) { ++n; - async_send_doc_queued(player.socket(), data); + send_to_player(player_connections_.iterator_to(player), data); } } @@ -2274,7 +2275,7 @@ void server::pm_handler( continue; } - async_send_doc_queued(player.socket(), data); + send_to_player(player_connections_.iterator_to(player), data); *out << "Message to " << receiver << " successfully sent."; return; } @@ -2613,7 +2614,7 @@ void server::kickban_handler( for(auto user : users_to_kick) { *out << "\nKicked " << user->info().name() << " (" << user->client_ip() << ")."; - async_send_error(user->socket(), "You have been banned. Reason: " + reason); + utils::visit([this,reason](auto&& socket) { async_send_error(socket, "You have been banned. Reason: " + reason); }, user->socket()); disconnect_player(user); } } @@ -2770,7 +2771,7 @@ void server::kick_handler(const std::string& /*issuer_name*/, *out << "Kicked " << player->name() << " (" << player->client_ip() << "). '" << kick_message << "'"; - async_send_error(player->socket(), kick_message); + utils::visit([this, &kick_message](auto&& socket) { async_send_error(socket, kick_message); }, player->socket()); disconnect_player(player); } @@ -2904,7 +2905,7 @@ void server::delete_game(int gameid, const std::string& reason) if(make_change_diff(games_and_users_list_.root(), nullptr, "user", it->info().config_address(), udiff)) { send_to_lobby(udiff); } else { - ERR_SERVER << "ERROR: delete_game(): Could not find user in players_. (socket: " << it->socket() << ")\n"; + ERR_SERVER << "ERROR: delete_game(): Could not find user in players_.\n"; } } @@ -2919,14 +2920,15 @@ void server::delete_game(int gameid, const std::string& reason) static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED); for(const auto& it : range_vctor) { + player_iterator p { player_connections_.project<0>(it) }; if(reason != "") { simple_wml::document leave_game_doc_reason("[leave_game]\n[/leave_game]\n", simple_wml::INIT_STATIC); leave_game_doc_reason.child("leave_game")->set_attr_dup("reason", reason.c_str()); - async_send_doc_queued(it->socket(), leave_game_doc_reason); + send_to_player(p, leave_game_doc_reason); } else { - async_send_doc_queued(it->socket(), leave_game_doc); + send_to_player(p, leave_game_doc); } - async_send_doc_queued(it->socket(), games_and_users_list_); + send_to_player(p, games_and_users_list_); } } diff --git a/src/server/wesnothd/server.hpp b/src/server/wesnothd/server.hpp index 68e1db2f0ba8..133004b6b073 100644 --- a/src/server/wesnothd/server.hpp +++ b/src/server/wesnothd/server.hpp @@ -40,14 +40,14 @@ class server : public server_base void handle_new_client(socket_ptr socket); void handle_new_client(tls_socket_ptr socket); - void login_client(boost::asio::yield_context yield, socket_ptr socket); - bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& username, bool& registered, bool& is_moderator); - bool authenticate(socket_ptr socket, const std::string& username, const std::string& password, bool name_taken, bool& registered); - void send_password_request(socket_ptr socket, const std::string& msg, + template void login_client(boost::asio::yield_context yield, SocketPtr socket); + template bool is_login_allowed(SocketPtr socket, const simple_wml::node* const login, const std::string& username, bool& registered, bool& is_moderator); + template bool authenticate(SocketPtr socket, const std::string& username, const std::string& password, bool name_taken, bool& registered); + template void send_password_request(SocketPtr socket, const std::string& msg, const std::string& user, const char* error_code = "", bool force_confirmation = false); bool accepting_connections() const { return !graceful_restart; } - void handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player); + template void handle_player(boost::asio::yield_context yield, SocketPtr socket, const player& player); void handle_player_in_lobby(player_iterator player, simple_wml::document& doc); void handle_player_in_game(player_iterator player, simple_wml::document& doc); void handle_whisper(player_iterator player, simple_wml::node& whisper); @@ -60,11 +60,21 @@ class server : public server_base void disconnect_player(player_iterator player); void remove_player(player_iterator player); - void send_server_message(socket_ptr socket, const std::string& message, const std::string& type); +public: + template void send_server_message(SocketPtr socket, const std::string& message, const std::string& type); void send_server_message(player_iterator player, const std::string& message, const std::string& type) { - send_server_message(player->socket(), message, type); + utils::visit( + [this, &message, &type](auto&& socket) { send_server_message(socket, message, type); }, + player->socket() + ); } void send_to_lobby(simple_wml::document& data, std::optional exclude = {}); + void send_to_player(player_iterator player, simple_wml::document& data) { + utils::visit( + [this, &data](auto&& socket) { async_send_doc_queued(socket, data); }, + player->socket() + ); + } void send_server_message_to_lobby(const std::string& message, std::optional exclude = {}); void send_server_message_to_all(const std::string& message, std::optional exclude = {}); @@ -72,6 +82,7 @@ class server : public server_base return player->get_game() != nullptr; } +private: wesnothd::ban_manager ban_manager_; struct connection_log @@ -104,7 +115,7 @@ class server : public server_base std::deque failed_logins_; std::unique_ptr user_handler_; - std::map seeds_; + std::map seeds_; std::mt19937 die_;