Make wesnothd use iterators instead of socket_ptrs to keep track of players (#5413)

This commit is contained in:
Sergey Popov 2021-01-21 04:51:26 +03:00 committed by GitHub
parent 51511e582e
commit 8058daba84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 499 additions and 581 deletions

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,8 @@
#include "server/common/simple_wml.hpp" #include "server/common/simple_wml.hpp"
#include "utils/make_enum.hpp" #include "utils/make_enum.hpp"
#include "utils/optional_fwd.hpp"
#include <map> #include <map>
#include <vector> #include <vector>
@ -27,8 +29,8 @@
namespace wesnothd namespace wesnothd
{ {
typedef std::vector<socket_ptr> user_vector; typedef std::vector<player_iterator> user_vector;
typedef std::vector<socket_ptr> side_vector; typedef std::vector<utils::optional<player_iterator>> side_vector;
class server; class server;
class game class game
@ -41,7 +43,7 @@ public:
); );
game(wesnothd::server& server, player_connections& player_connections, game(wesnothd::server& server, player_connections& player_connections,
const socket_ptr& host, player_iterator host,
const std::string& name = "", const std::string& name = "",
bool save_replays = false, bool save_replays = false,
const std::string& replay_save_path = ""); const std::string& replay_save_path = "");
@ -68,25 +70,25 @@ public:
return name_; return name_;
} }
bool is_owner(const socket_ptr& player) const bool is_owner(player_iterator player) const
{ {
return (player == owner_); return (player == owner_);
} }
bool is_member(const socket_ptr& player) const bool is_member(player_iterator player) const
{ {
return is_player(player) || is_observer(player); return is_player(player) || is_observer(player);
} }
bool allow_observers() const; bool allow_observers() const;
bool is_observer(const socket_ptr& player) const; bool is_observer(player_iterator player) const;
bool is_player(const socket_ptr& player) const; bool is_player(player_iterator player) const;
/** Checks whether the connection's ip address or username is banned. */ /** Checks whether the connection's ip address or username is banned. */
bool player_is_banned(const socket_ptr& player, const std::string& name) const; bool player_is_banned(player_iterator player, const std::string& name) const;
/** when the host sends the new scenario of a mp campaign */ /** when the host sends the new scenario of a mp campaign */
void new_scenario(const socket_ptr& player); void new_scenario(player_iterator player);
bool level_init() const bool level_init() const
{ {
@ -153,36 +155,36 @@ public:
* Mute an observer or give a message of all currently muted observers if no * Mute an observer or give a message of all currently muted observers if no
* name is given. * name is given.
*/ */
void mute_observer(const simple_wml::node& mute, const socket_ptr& muter); void mute_observer(const simple_wml::node& mute, player_iterator muter);
void unmute_observer(const simple_wml::node& unmute, const socket_ptr& unmuter); void unmute_observer(const simple_wml::node& unmute, player_iterator unmuter);
/** /**
* Kick a member by name. * Kick a member by name.
* *
* @return The network handle of the removed member if * @return The iterator to the removed member if
* successful, null pointer otherwise. * successful, empty optional otherwise.
*/ */
socket_ptr kick_member(const simple_wml::node& kick, const socket_ptr& kicker); utils::optional<player_iterator> kick_member(const simple_wml::node& kick, player_iterator kicker);
/** /**
* Ban and kick a user by name. * Ban and kick a user by name.
* *
* The user does not need to be in this game but logged in. * The user does not need to be in this game but logged in.
* *
* @return The network handle of the banned player if he * @return The iterator to the banned player if he
* was in this game, null pointer otherwise. * was in this game, empty optional otherwise.
*/ */
socket_ptr ban_user(const simple_wml::node& ban, const socket_ptr& banner); utils::optional<player_iterator> ban_user(const simple_wml::node& ban, player_iterator banner);
void unban_user(const simple_wml::node& unban, const socket_ptr& unbanner); void unban_user(const simple_wml::node& unban, player_iterator unbanner);
/** /**
* Add a user to the game. * Add a user to the game.
* *
* @return True iff the user successfully joined the game. * @return True iff the user successfully joined the game.
*/ */
bool add_player(const socket_ptr& player, bool observer = false); bool add_player(player_iterator player, bool observer = false);
/** /**
* Removes a user from the game. * Removes a user from the game.
@ -191,12 +193,12 @@ public:
* no more players or the host left on a not yet * no more players or the host left on a not yet
* started game. * started game.
*/ */
bool remove_player(const socket_ptr& player, const bool disconnect = false, const bool destruct = false); bool remove_player(player_iterator player, const bool disconnect = false, const bool destruct = false);
/** Adds players and observers into one vector and returns that. */ /** Adds players and observers into one vector and returns that. */
const user_vector all_game_users() const; const user_vector all_game_users() const;
void start_game(const socket_ptr& starter); void start_game(player_iterator starter);
// this is performed just before starting and before [start_game] signal // this is performed just before starting and before [start_game] signal
// send scenario_diff's specific to each client so that they locally // send scenario_diff's specific to each client so that they locally
@ -206,7 +208,7 @@ public:
void update_game(); void update_game();
/** A user (player only?) asks for the next scenario to advance to. */ /** A user (player only?) asks for the next scenario to advance to. */
void load_next_scenario(const socket_ptr& user); // const void load_next_scenario(player_iterator user); // const
// iceiceice: I unmarked this const because I want to send and record server messages when I fail to tweak sides // iceiceice: I unmarked this const because I want to send and record server messages when I fail to tweak sides
// properly // properly
@ -215,9 +217,9 @@ public:
void update_side_data(); void update_side_data();
/** Let's a player owning a side give it to another player or observer. */ /** Let's a player owning a side give it to another player or observer. */
void transfer_side_control(const socket_ptr& sock, const simple_wml::node& cfg); void transfer_side_control(player_iterator player, const simple_wml::node& cfg);
void process_message(simple_wml::document& data, const socket_ptr& user); void process_message(simple_wml::document& data, player_iterator);
/** /**
* Handles [end_turn], repackages [commands] with private [speak]s in them * Handles [end_turn], repackages [commands] with private [speak]s in them
@ -228,12 +230,12 @@ public:
* *
* @returns True if the turn ended. * @returns True if the turn ended.
*/ */
bool process_turn(simple_wml::document& data, const socket_ptr& user); bool process_turn(simple_wml::document& data, player_iterator user);
/** Handles incoming [whiteboard] data. */ /** Handles incoming [whiteboard] data. */
void process_whiteboard(simple_wml::document& data, const socket_ptr& user); void process_whiteboard(simple_wml::document& data, player_iterator user);
/** Handles incoming [change_turns_wml] data. */ /** Handles incoming [change_turns_wml] data. */
void process_change_turns_wml(simple_wml::document& data, const socket_ptr& user); void process_change_turns_wml(simple_wml::document& data, player_iterator user);
/** /**
* Set the description to the number of available slots. * Set the description to the number of available slots.
@ -242,30 +244,30 @@ public:
*/ */
bool describe_slots(); bool describe_slots();
void send_server_message_to_all(const char* message, const socket_ptr& exclude = socket_ptr()); void send_server_message_to_all(const char* message, utils::optional<player_iterator> exclude = {});
void send_server_message_to_all(const std::string& message, const socket_ptr& exclude = socket_ptr()) void send_server_message_to_all(const std::string& message, utils::optional<player_iterator> exclude = {})
{ {
send_server_message_to_all(message.c_str(), exclude); send_server_message_to_all(message.c_str(), exclude);
} }
void send_server_message( void send_server_message(
const char* message, const socket_ptr& sock = socket_ptr(), simple_wml::document* doc = nullptr) const; const char* message, utils::optional<player_iterator> player = {}, simple_wml::document* doc = nullptr) const;
void send_server_message( void send_server_message(
const std::string& message, const socket_ptr& sock = socket_ptr(), simple_wml::document* doc = nullptr) const const std::string& message, utils::optional<player_iterator> player = {}, simple_wml::document* doc = nullptr) const
{ {
send_server_message(message.c_str(), sock, doc); send_server_message(message.c_str(), player, doc);
} }
/** Send data to all players in this game except 'exclude'. */ /** Send data to all players in this game except 'exclude'. */
void send_and_record_server_message(const char* message, const socket_ptr& exclude = socket_ptr()); void send_and_record_server_message(const char* message, utils::optional<player_iterator> exclude = {});
void send_and_record_server_message(const std::string& message, const socket_ptr& exclude = socket_ptr()) void send_and_record_server_message(const std::string& message, utils::optional<player_iterator> exclude = {})
{ {
send_and_record_server_message(message.c_str(), exclude); send_and_record_server_message(message.c_str(), exclude);
} }
template<typename Container> template<typename Container>
void send_to_players(simple_wml::document& data, const Container& players, socket_ptr exclude = socket_ptr()); void send_to_players(simple_wml::document& data, const Container& players, utils::optional<player_iterator> exclude = {});
void send_data(simple_wml::document& data, const socket_ptr& exclude = socket_ptr(), std::string packet_type = ""); void send_data(simple_wml::document& data, utils::optional<player_iterator> exclude = {}, std::string packet_type = "");
void clear_history(); void clear_history();
void record_data(std::unique_ptr<simple_wml::document> data); void record_data(std::unique_ptr<simple_wml::document> data);
@ -318,7 +320,7 @@ public:
void set_termination_reason(const std::string& reason); void set_termination_reason(const std::string& reason);
void handle_choice(const simple_wml::node& data, const socket_ptr& user); void handle_choice(const simple_wml::node& data, player_iterator user);
void handle_random_choice(const simple_wml::node& data); void handle_random_choice(const simple_wml::node& data);
@ -334,7 +336,7 @@ public:
/** /**
* Function which returns true iff 'player' controls any of the sides spcified in 'sides'. * Function which returns true iff 'player' controls any of the sides spcified in 'sides'.
*/ */
bool controls_side(const std::vector<int>& sides, const socket_ptr& player) const; bool controls_side(const std::vector<int>& sides, player_iterator player) const;
bool is_reload() const; bool is_reload() const;
@ -348,23 +350,23 @@ private:
return nsides_ ? (current_side_index_ % nsides_) : 0; return nsides_ ? (current_side_index_ % nsides_) : 0;
} }
const socket_ptr current_player() const utils::optional<player_iterator> current_player() const
{ {
return (nsides_ ? sides_[current_side()] : socket_ptr()); return sides_[current_side()];
} }
bool is_current_player(const socket_ptr& player) const bool is_current_player(player_iterator player) const
{ {
return (current_player() == player); return (current_player() == player);
} }
bool is_muted_observer(const socket_ptr& player) const; bool is_muted_observer(player_iterator player) const;
bool all_observers_muted() const bool all_observers_muted() const
{ {
return all_observers_muted_; return all_observers_muted_;
} }
void send_muted_observers(const socket_ptr& user) const; void send_muted_observers(player_iterator user) const;
bool send_taken_side(simple_wml::document& cfg, const simple_wml::node* side) const; bool send_taken_side(simple_wml::document& cfg, const simple_wml::node* side) const;
@ -377,21 +379,21 @@ private:
* First we look for a side where save_id= or current_player= matches the * First we look for a side where save_id= or current_player= matches the
* new user's name then we search for the first controller="network" side. * new user's name then we search for the first controller="network" side.
*/ */
bool take_side(const socket_ptr& user); bool take_side(player_iterator user);
/** /**
* Send [change_controller] message to tell all clients the new controller's name * Send [change_controller] message to tell all clients the new controller's name
* or controller type (human or ai). * or controller type (human or ai).
*/ */
void change_controller(const std::size_t side_num, void change_controller(const std::size_t side_num,
const socket_ptr& sock, player_iterator sock,
const std::string& player_name, const std::string& player_name,
const bool player_left = true); const bool player_left = true);
std::unique_ptr<simple_wml::document> change_controller_type(const std::size_t side_num, std::unique_ptr<simple_wml::document> change_controller_type(const std::size_t side_num,
const socket_ptr& sock, player_iterator player,
const std::string& player_name); const std::string& player_name);
void transfer_ai_sides(const socket_ptr& player); void transfer_ai_sides(player_iterator player);
void send_leave_game(const socket_ptr& user) const; void send_leave_game(player_iterator user) const;
/** /**
* @param data the data to be sent to the sides. * @param data the data to be sent to the sides.
@ -400,24 +402,24 @@ private:
*/ */
void send_data_sides(simple_wml::document& data, void send_data_sides(simple_wml::document& data,
const simple_wml::string_span& sides, const simple_wml::string_span& sides,
const socket_ptr& exclude = socket_ptr()); utils::optional<player_iterator> exclude = {});
void send_data_observers( void send_data_observers(
simple_wml::document& data, const socket_ptr& exclude = socket_ptr(), std::string packet_type = "") const; simple_wml::document& data, utils::optional<player_iterator> exclude = {}, std::string packet_type = "") const;
/** /**
* Send [observer] tags of all the observers in the game to the user or * Send [observer] tags of all the observers in the game to the user or
* everyone if none given. * everyone if none given.
*/ */
void send_observerjoins(const socket_ptr& sock = socket_ptr()); void send_observerjoins(utils::optional<player_iterator> player = {});
void send_observerquit(const socket_ptr& observer); void send_observerquit(player_iterator observer);
void send_history(const socket_ptr& sock) const; void send_history(player_iterator sock) const;
/** In case of a host transfer, notify the new host about its status. */ /** In case of a host transfer, notify the new host about its status. */
void notify_new_host(); void notify_new_host();
/** Shortcut to a convenience function for finding a user by name. */ /** Shortcut to a convenience function for finding a user by name. */
socket_ptr find_user(const simple_wml::string_span& name); utils::optional<player_iterator> find_user(const simple_wml::string_span& name);
bool observers_can_label() const bool observers_can_label() const
{ {
@ -429,13 +431,13 @@ private:
return true; return true;
} }
bool is_legal_command(const simple_wml::node& command, const socket_ptr& user); bool is_legal_command(const simple_wml::node& command, player_iterator user);
/** /**
* Checks whether a user has the same IP as any other members of this game. * Checks whether a user has the same IP as any other members of this game.
* @return A comma separated string of members with matching IPs. * @return A comma separated string of members with matching IPs.
*/ */
std::string has_same_ip(const socket_ptr& user) const; std::string has_same_ip(player_iterator user) const;
/** /**
* Function which should be called every time a player ends their turn * Function which should be called every time a player ends their turn
@ -451,16 +453,13 @@ private:
* *
* Only sends data if the game is initialized but not yet started. * Only sends data if the game is initialized but not yet started.
*/ */
void send_user_list(const socket_ptr& exclude = socket_ptr()); void send_user_list(utils::optional<player_iterator> exclude = {});
/** Returns the name of the user or "(unfound)". */ /** Returns the name of the user or "(unfound)". */
std::string username(const socket_ptr& pl) const; std::string username(player_iterator pl) const;
/** Returns a comma separated list of user names. */ /** Returns a comma separated list of user names. */
std::string list_users(user_vector users, const std::string& func) const; std::string list_users(user_vector users) const;
/** Function to log when we don't find a connection in player_info_. */
void missing_user(socket_ptr socket, const std::string& func) const;
/** calculates the initial value for sides_, side_controllerds_, nsides_*/ /** calculates the initial value for sides_, side_controllerds_, nsides_*/
void reset_sides(); void reset_sides();
@ -489,7 +488,7 @@ private:
std::string password_; std::string password_;
/** The game host or later owner (if the host left). */ /** The game host or later owner (if the host left). */
socket_ptr owner_; player_iterator owner_;
/** A vector of players (members owning a side). */ /** A vector of players (members owning a side). */
user_vector players_; user_vector players_;
@ -544,7 +543,7 @@ private:
* keep track of those players because processing certain * keep track of those players because processing certain
* input from those side wil lead to error (oos) * input from those side wil lead to error (oos)
*/ */
std::set<socket_ptr> players_not_advanced_; std::set<const player_record*> players_not_advanced_;
std::string termination_; std::string termination_;

View file

@ -44,7 +44,7 @@ public:
return socket_; return socket_;
} }
std::string saved_client_ip() const std::string client_ip() const
{ {
return ip_address; return ip_address;
} }
@ -91,4 +91,6 @@ using player_connections = bmi::multi_index_container<player_record, bmi::indexe
bmi::const_mem_fun<player_record, int, &player_record::game_id>> bmi::const_mem_fun<player_record, int, &player_record::game_id>>
>>; >>;
typedef player_connections::const_iterator player_iterator;
} // namespace wesnothd } // namespace wesnothd

View file

@ -189,7 +189,7 @@ static bool make_change_diff(const simple_wml::node& src,
static std::string player_status(const wesnothd::player_record& player) static std::string player_status(const wesnothd::player_record& player)
{ {
std::ostringstream out; std::ostringstream out;
out << "'" << player.name() << "' @ " << client_address(player.socket()); out << "'" << player.name() << "' @ " << player.client_ip();
return out.str(); return out.str();
} }
@ -996,37 +996,38 @@ void server::send_password_request(socket_ptr socket,
async_send_doc_queued(socket, doc); async_send_doc_queued(socket, doc);
} }
void server::handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player) void server::handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player_data)
{ {
if(lan_server_) if(lan_server_)
abort_lan_server_timer(); abort_lan_server_timer();
bool inserted; bool inserted;
std::tie(std::ignore, inserted) = player_connections_.insert(player_connections::value_type(socket, player)); player_iterator player;
std::tie(player, inserted) = player_connections_.insert(player_connections::value_type(socket, player_data));
assert(inserted); assert(inserted);
BOOST_SCOPE_EXIT_ALL(this, &socket) { BOOST_SCOPE_EXIT_ALL(this, &player) {
remove_player(socket); remove_player(player);
}; };
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(socket, games_and_users_list_);
if(!motd_.empty()) { if(!motd_.empty()) {
send_server_message(socket, motd_+'\n'+announcements_+tournaments_, "motd"); send_server_message(player, motd_+'\n'+announcements_+tournaments_, "motd");
} }
send_server_message(socket, information_, "server_info"); send_server_message(player, information_, "server_info");
send_server_message(socket, announcements_+tournaments_, "announcements"); send_server_message(player, announcements_+tournaments_, "announcements");
if(version_info(player.version()) < secure_version ){ if(version_info(player_data.version()) < secure_version ){
send_server_message(socket, "You are using version " + player.version() + " which has known security issues that can be used to compromise your computer. We strongly recommend updating to a Wesnoth version " + secure_version.str() + " or newer!", "alert"); send_server_message(player, "You are using version " + player_data.version() + " which has known security issues that can be used to compromise your computer. We strongly recommend updating to a Wesnoth version " + secure_version.str() + " or newer!", "alert");
} }
if(version_info(player.version()) < version_info(recommended_version_)) { if(version_info(player_data.version()) < version_info(recommended_version_)) {
send_server_message(socket, "A newer Wesnoth version, " + recommended_version_ + ", is out!", "alert"); send_server_message(player, "A newer Wesnoth version, " + recommended_version_ + ", is out!", "alert");
} }
// Send other players in the lobby the update that the player has joined // Send other players in the lobby the update that the player has joined
simple_wml::document diff; simple_wml::document diff;
make_add_diff(games_and_users_list_.root(), nullptr, "user", diff); make_add_diff(games_and_users_list_.root(), nullptr, "user", diff);
send_to_lobby(diff, socket); send_to_lobby(diff, player);
while(true) { while(true) {
boost::system::error_code ec; boost::system::error_code ec;
@ -1039,44 +1040,44 @@ void server::handle_player(boost::asio::yield_context yield, socket_ptr socket,
} }
if(simple_wml::node* whisper = doc->child("whisper")) { if(simple_wml::node* whisper = doc->child("whisper")) {
handle_whisper(socket, *whisper); handle_whisper(player, *whisper);
} }
if(simple_wml::node* query = doc->child("query")) { if(simple_wml::node* query = doc->child("query")) {
handle_query(socket, *query); handle_query(player, *query);
} }
if(simple_wml::node* nickserv = doc->child("nickserv")) { if(simple_wml::node* nickserv = doc->child("nickserv")) {
handle_nickserv(socket, *nickserv); handle_nickserv(player, *nickserv);
} }
if(!player_is_in_game(socket)) { if(!player_is_in_game(player)) {
handle_player_in_lobby(socket, *doc); handle_player_in_lobby(player, *doc);
} else { } else {
handle_player_in_game(socket, *doc); handle_player_in_game(player, *doc);
} }
} }
} }
void server::handle_player_in_lobby(socket_ptr socket, simple_wml::document& data) void server::handle_player_in_lobby(player_iterator player, simple_wml::document& data)
{ {
if(simple_wml::node* message = data.child("message")) { if(simple_wml::node* message = data.child("message")) {
handle_message(socket, *message); handle_message(player, *message);
return; return;
} }
if(simple_wml::node* create_game = data.child("create_game")) { if(simple_wml::node* create_game = data.child("create_game")) {
handle_create_game(socket, *create_game); handle_create_game(player, *create_game);
return; return;
} }
if(simple_wml::node* join = data.child("join")) { if(simple_wml::node* join = data.child("join")) {
handle_join_game(socket, *join); handle_join_game(player, *join);
return; return;
} }
} }
void server::handle_whisper(socket_ptr socket, simple_wml::node& whisper) void server::handle_whisper(player_iterator player, simple_wml::node& whisper)
{ {
if((whisper["receiver"].empty()) || (whisper["message"].empty())) { if((whisper["receiver"].empty()) || (whisper["message"].empty())) {
static simple_wml::document data( static simple_wml::document data(
@ -1087,21 +1088,21 @@ void server::handle_whisper(socket_ptr socket, simple_wml::node& whisper)
simple_wml::INIT_COMPRESSED simple_wml::INIT_COMPRESSED
); );
async_send_doc_queued(socket, data); async_send_doc_queued(player->socket(), data);
return; return;
} }
whisper.set_attr_dup("sender", player_connections_.find(socket)->name().c_str()); whisper.set_attr_dup("sender", player->name().c_str());
auto receiver_iter = player_connections_.get<name_t>().find(whisper["receiver"].to_string()); auto receiver_iter = player_connections_.get<name_t>().find(whisper["receiver"].to_string());
if(receiver_iter == player_connections_.get<name_t>().end()) { if(receiver_iter == player_connections_.get<name_t>().end()) {
send_server_message(socket, "Can't find '" + whisper["receiver"].to_string() + "'.", "error"); send_server_message(player, "Can't find '" + whisper["receiver"].to_string() + "'.", "error");
return; return;
} }
auto g = player_connections_.find(socket)->get_game(); auto g = player->get_game();
if(g && g->started() && g->is_player(receiver_iter->socket())) { if(g && g->started() && g->is_player(player_connections_.project<0>(receiver_iter))) {
send_server_message(socket, "You cannot send private messages to players in a running game you observe.", "error"); send_server_message(player, "You cannot send private messages to players in a running game you observe.", "error");
return; return;
} }
@ -1116,13 +1117,8 @@ void server::handle_whisper(socket_ptr socket, simple_wml::node& whisper)
async_send_doc_queued(receiver_iter->socket(), cwhisper); async_send_doc_queued(receiver_iter->socket(), cwhisper);
} }
void server::handle_query(socket_ptr socket, simple_wml::node& query) void server::handle_query(player_iterator iter, simple_wml::node& query)
{ {
auto iter = player_connections_.find(socket);
if(iter == player_connections_.end()) {
return;
}
wesnothd::player& player = iter->info(); wesnothd::player& player = iter->info();
const std::string command(query["type"].to_string()); const std::string command(query["type"].to_string());
@ -1152,7 +1148,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
response << process_command(command, player.name()); response << process_command(command, player.name());
} else if(player.is_moderator()) { } else if(player.is_moderator()) {
if(command == "signout") { if(command == "signout") {
LOG_SERVER << "Admin signed out: IP: " << client_address(socket) << "\tnick: " << player.name() LOG_SERVER << "Admin signed out: IP: " << iter->client_ip() << "\tnick: " << player.name()
<< std::endl; << std::endl;
player.set_moderator(false); player.set_moderator(false);
// This string is parsed by the client! // This string is parsed by the client!
@ -1161,7 +1157,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
user_handler_->set_is_moderator(player.name(), false); user_handler_->set_is_moderator(player.name(), false);
} }
} else { } else {
LOG_SERVER << "Admin Command: type: " << command << "\tIP: " << client_address(socket) LOG_SERVER << "Admin Command: type: " << command << "\tIP: " << iter->client_ip()
<< "\tnick: " << player.name() << std::endl; << "\tnick: " << player.name() << std::endl;
response << process_command(command, player.name()); response << process_command(command, player.name());
LOG_SERVER << response.str() << std::endl; LOG_SERVER << response.str() << std::endl;
@ -1170,7 +1166,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
response << query_help_msg; response << query_help_msg;
} else if(command == "admin" || command.compare(0, 6, "admin ") == 0) { } else if(command == "admin" || command.compare(0, 6, "admin ") == 0) {
if(admin_passwd_.empty()) { if(admin_passwd_.empty()) {
send_server_message(socket, "No password set.", "error"); send_server_message(iter, "No password set.", "error");
return; return;
} }
@ -1180,7 +1176,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
} }
if(passwd == admin_passwd_) { if(passwd == admin_passwd_) {
LOG_SERVER << "New Admin recognized: IP: " << client_address(socket) << "\tnick: " << player.name() LOG_SERVER << "New Admin recognized: IP: " << iter->client_ip() << "\tnick: " << player.name()
<< std::endl; << std::endl;
player.set_moderator(true); player.set_moderator(true);
// This string is parsed by the client! // This string is parsed by the client!
@ -1190,7 +1186,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
user_handler_->set_is_moderator(player.name(), true); user_handler_->set_is_moderator(player.name(), true);
} }
} else { } else {
WRN_SERVER << "FAILED Admin attempt with password: '" << passwd << "'\tIP: " << client_address(socket) WRN_SERVER << "FAILED Admin attempt with password: '" << passwd << "'\tIP: " << iter->client_ip()
<< "\tnick: " << player.name() << std::endl; << "\tnick: " << player.name() << std::endl;
response << "Error: wrong password"; response << "Error: wrong password";
} }
@ -1198,14 +1194,14 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
response << "Error: unrecognized query: '" << command << "'\n" << query_help_msg; response << "Error: unrecognized query: '" << command << "'\n" << query_help_msg;
} }
send_server_message(socket, response.str(), "info"); send_server_message(iter, response.str(), "info");
} }
void server::handle_nickserv(socket_ptr socket, simple_wml::node& nickserv) void server::handle_nickserv(player_iterator player, simple_wml::node& nickserv)
{ {
// Check if this server allows nick registration at all // Check if this server allows nick registration at all
if(!user_handler_) { if(!user_handler_) {
send_server_message(socket, "This server does not allow username registration.", "error"); send_server_message(player, "This server does not allow username registration.", "error");
return; return;
} }
@ -1213,9 +1209,9 @@ void server::handle_nickserv(socket_ptr socket, simple_wml::node& nickserv)
if(nickserv.child("info")) { if(nickserv.child("info")) {
try { try {
std::string res = user_handler_->user_info((*nickserv.child("info"))["name"].to_string()); std::string res = user_handler_->user_info((*nickserv.child("info"))["name"].to_string());
send_server_message(socket, res, "info"); send_server_message(player, res, "info");
} catch(const user_handler::error& e) { } catch(const user_handler::error& e) {
send_server_message(socket, send_server_message(player,
"There was an error looking up the details of the user '" "There was an error looking up the details of the user '"
+ (*nickserv.child("info"))["name"].to_string() + "'. " + (*nickserv.child("info"))["name"].to_string() + "'. "
+ " The error message was: " + e.message, "error" + " The error message was: " + e.message, "error"
@ -1226,11 +1222,10 @@ void server::handle_nickserv(socket_ptr socket, simple_wml::node& nickserv)
} }
} }
void server::handle_message(socket_ptr socket, simple_wml::node& message) void server::handle_message(player_iterator user, simple_wml::node& message)
{ {
auto user = player_connections_.find(socket);
if(user->info().is_message_flooding()) { if(user->info().is_message_flooding()) {
send_server_message(socket, send_server_message(user,
"Warning: you are sending too many messages too fast. Your message has not been relayed.", "error"); "Warning: you are sending too many messages too fast. Your message has not been relayed.", "error");
return; return;
} }
@ -1245,52 +1240,46 @@ void server::handle_message(socket_ptr socket, simple_wml::node& message)
chat_message::truncate_message(msg, trunc_message); chat_message::truncate_message(msg, trunc_message);
if(msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") { if(msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") {
LOG_SERVER << client_address(socket) << "\t<" << user->name() LOG_SERVER << user->client_ip() << "\t<" << user->name()
<< simple_wml::string_span(msg.begin() + 3, msg.size() - 3) << ">\n"; << simple_wml::string_span(msg.begin() + 3, msg.size() - 3) << ">\n";
} else { } else {
LOG_SERVER << client_address(socket) << "\t<" << user->name() << "> " << msg << "\n"; LOG_SERVER << user->client_ip() << "\t<" << user->name() << "> " << msg << "\n";
} }
send_to_lobby(relay_message, socket); send_to_lobby(relay_message, user);
} }
void server::handle_create_game(socket_ptr socket, simple_wml::node& create_game) void server::handle_create_game(player_iterator player, simple_wml::node& create_game)
{ {
if(graceful_restart) { if(graceful_restart) {
static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED); static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, send_server_message(player,
"This server is shutting down. You aren't allowed to make new games. Please " "This server is shutting down. You aren't allowed to make new games. Please "
"reconnect to the new server.", "error"); "reconnect to the new server.", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} }
player_connections_.modify(
player_connections_.find(socket), std::bind(&server::create_game, this, std::placeholders::_1, std::ref(create_game)));
return;
}
void server::create_game(player_record& host_record, simple_wml::node& create_game)
{
const std::string game_name = create_game["name"].to_string(); const std::string game_name = create_game["name"].to_string();
const std::string game_password = create_game["password"].to_string(); const std::string game_password = create_game["password"].to_string();
const std::string initial_bans = create_game["ignored"].to_string(); const std::string initial_bans = create_game["ignored"].to_string();
DBG_SERVER << client_address(host_record.socket()) << "\t" << host_record.info().name() DBG_SERVER << player->client_ip() << "\t" << player->info().name()
<< "\tcreates a new game: \"" << game_name << "\".\n"; << "\tcreates a new game: \"" << game_name << "\".\n";
// Create the new game, remove the player from the lobby // Create the new game, remove the player from the lobby
// and set the player as the host/owner. // and set the player as the host/owner.
player_connections_.modify(player, [this, player, &game_name](player_record& host_record) {
host_record.get_game().reset( host_record.get_game().reset(
new wesnothd::game(*this, player_connections_, host_record.socket(), game_name, save_replays_, replay_save_path_), new wesnothd::game(*this, player_connections_, player, game_name, save_replays_, replay_save_path_),
std::bind(&server::cleanup_game, this, std::placeholders::_1) std::bind(&server::cleanup_game, this, std::placeholders::_1)
); );
});
wesnothd::game& g = *host_record.get_game(); wesnothd::game& g = *player->get_game();
DBG_SERVER << "initial bans: " << initial_bans << "\n"; DBG_SERVER << "initial bans: " << initial_bans << "\n";
if(initial_bans != "") { if(initial_bans != "") {
@ -1336,7 +1325,7 @@ void server::cleanup_game(game* game_ptr)
delete game_ptr; delete game_ptr;
} }
void server::handle_join_game(socket_ptr socket, simple_wml::node& join) void server::handle_join_game(player_iterator player, simple_wml::node& join)
{ {
const bool observer = join.attr("observe").to_bool(); const bool observer = join.attr("observe").to_bool();
const std::string& password = join["password"].to_string(); const std::string& password = join["password"].to_string();
@ -1351,55 +1340,55 @@ void server::handle_join_game(socket_ptr socket, simple_wml::node& join)
static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED); static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
if(!g) { if(!g) {
WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name() WRN_SERVER << player->client_ip() << "\t" << player->info().name()
<< "\tattempted to join unknown game:\t" << game_id << ".\n"; << "\tattempted to join unknown game:\t" << game_id << ".\n";
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, "Attempt to join unknown game.", "error"); send_server_message(player, "Attempt to join unknown game.", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} else if(!g->level_init()) { } else if(!g->level_init()) {
WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name() WRN_SERVER << player->client_ip() << "\t" << player->info().name()
<< "\tattempted to join uninitialized game:\t\"" << g->name() << "\" (" << game_id << ").\n"; << "\tattempted to join uninitialized game:\t\"" << g->name() << "\" (" << game_id << ").\n";
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, "Attempt to join an uninitialized game.", "error"); send_server_message(player, "Attempt to join an uninitialized game.", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} else if(player_connections_.find(socket)->info().is_moderator()) { } else if(player->info().is_moderator()) {
// Admins are always allowed to join. // Admins are always allowed to join.
} else if(g->player_is_banned(socket, player_connections_.find(socket)->info().name())) { } else if(g->player_is_banned(player, player->info().name())) {
DBG_SERVER << client_address(socket) DBG_SERVER << player->client_ip()
<< "\tReject banned player: " << player_connections_.find(socket)->info().name() << "\tReject banned player: " << player->info().name()
<< "\tfrom game:\t\"" << g->name() << "\" (" << game_id << ").\n"; << "\tfrom game:\t\"" << g->name() << "\" (" << game_id << ").\n";
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, "You are banned from this game.", "error"); send_server_message(player, "You are banned from this game.", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} else if(!g->password_matches(password)) { } else if(!g->password_matches(password)) {
WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name() WRN_SERVER << player->client_ip() << "\t" << player->info().name()
<< "\tattempted to join game:\t\"" << g->name() << "\" (" << game_id << ") with bad password\n"; << "\tattempted to join game:\t\"" << g->name() << "\" (" << game_id << ") with bad password\n";
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, "Incorrect password.", "error"); send_server_message(player, "Incorrect password.", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} }
bool joined = g->add_player(socket, observer); bool joined = g->add_player(player, observer);
if(!joined) { if(!joined) {
WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name() WRN_SERVER << player->client_ip() << "\t" << player->info().name()
<< "\tattempted to observe game:\t\"" << g->name() << "\" (" << game_id << "\tattempted to observe game:\t\"" << g->name() << "\" (" << game_id
<< ") which doesn't allow observers.\n"; << ") which doesn't allow observers.\n";
async_send_doc_queued(socket, leave_game_doc); async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(socket, send_server_message(player,
"Attempt to observe a game that doesn't allow observers. (You probably joined the " "Attempt to observe a game that doesn't allow observers. (You probably joined the "
"game shortly after it filled up.)", "error"); "game shortly after it filled up.)", "error");
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(player->socket(), games_and_users_list_);
return; return;
} }
player_connections_.modify(player_connections_.find(socket), player_connections_.modify(player,
std::bind(&player_record::set_game, std::placeholders::_1, player_connections_.get<game_t>().find(game_id)->get_game())); std::bind(&player_record::set_game, std::placeholders::_1, g));
g->describe_slots(); g->describe_slots();
@ -1407,26 +1396,25 @@ void server::handle_join_game(socket_ptr socket, simple_wml::node& join)
simple_wml::document diff; simple_wml::document diff;
bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g->description(), diff); bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g->description(), diff);
bool diff2 = make_change_diff(games_and_users_list_.root(), nullptr, "user", bool diff2 = make_change_diff(games_and_users_list_.root(), nullptr, "user",
player_connections_.find(socket)->info().config_address(), diff); player->info().config_address(), diff);
if(diff1 || diff2) { if(diff1 || diff2) {
send_to_lobby(diff); send_to_lobby(diff);
} }
} }
void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data) void server::handle_player_in_game(player_iterator p, simple_wml::document& data)
{ {
DBG_SERVER << "in process_data_game...\n"; DBG_SERVER << "in process_data_game...\n";
auto p = player_connections_.find(socket); wesnothd::player& player { p->info() };
wesnothd::player& player = p->info();
game& g = *(p->get_game()); game& g = *(p->get_game());
std::weak_ptr<game> g_ptr{p->get_game()}; std::weak_ptr<game> g_ptr{p->get_game()};
// If this is data describing the level for a game. // If this is data describing the level for a game.
if(data.child("snapshot") || data.child("scenario")) { if(data.child("snapshot") || data.child("scenario")) {
if(!g.is_owner(socket)) { if(!g.is_owner(p)) {
return; return;
} }
@ -1437,7 +1425,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// place a pointer to that summary in the game's description. // place a pointer to that summary in the game's description.
// g.level() should then receive the full data for the game. // g.level() should then receive the full data for the game.
if(!g.level_init()) { if(!g.level_init()) {
LOG_SERVER << client_address(socket) << "\t" << player.name() << "\tcreated game:\t\"" << g.name() << "\" (" LOG_SERVER << p->client_ip() << "\t" << player.name() << "\tcreated game:\t\"" << g.name() << "\" ("
<< g.id() << ", " << g.db_id() << ").\n"; << g.id() << ", " << g.db_id() << ").\n";
// Update our config object which describes the open games, // Update our config object which describes the open games,
// and save a pointer to the description in the new game. // and save a pointer to the description in the new game.
@ -1450,13 +1438,13 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
if(const simple_wml::node* m = data.child("multiplayer")) { if(const simple_wml::node* m = data.child("multiplayer")) {
m->copy_into(desc); m->copy_into(desc);
} else { } else {
WRN_SERVER << client_address(socket) << "\t" << player.name() << "\tsent scenario data in game:\t\"" WRN_SERVER << p->client_ip() << "\t" << player.name() << "\tsent scenario data in game:\t\""
<< g.name() << "\" (" << g.id() << ", " << g.db_id() << ") without a 'multiplayer' child.\n"; << g.name() << "\" (" << g.id() << ", " << g.db_id() << ") without a 'multiplayer' child.\n";
// Set the description so it can be removed in delete_game(). // Set the description so it can be removed in delete_game().
g.set_description(&desc); g.set_description(&desc);
delete_game(g.id()); delete_game(g.id());
send_server_message(socket, send_server_message(p,
"The scenario data is missing the [multiplayer] tag which contains the " "The scenario data is missing the [multiplayer] tag which contains the "
"game settings. Game aborted.", "error"); "game settings. Game aborted.", "error");
return; return;
@ -1465,7 +1453,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
g.set_description(&desc); g.set_description(&desc);
desc.set_attr_dup("id", std::to_string(g.id()).c_str()); desc.set_attr_dup("id", std::to_string(g.id()).c_str());
} else { } else {
WRN_SERVER << client_address(socket) << "\t" << player.name() << "\tsent scenario data in game:\t\"" WRN_SERVER << p->client_ip() << "\t" << player.name() << "\tsent scenario data in game:\t\""
<< g.name() << "\" (" << g.id() << ", " << g.db_id() << ") although it's already initialized.\n"; << g.name() << "\" (" << g.id() << ", " << g.db_id() << ") although it's already initialized.\n";
return; return;
} }
@ -1518,7 +1506,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// Send the update of the game description to the lobby. // Send the update of the game description to the lobby.
simple_wml::document diff; simple_wml::document diff;
make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff); make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
make_change_diff(games_and_users_list_.root(), nullptr, "user", player_connections_.find(socket)->info().config_address(), diff); make_change_diff(games_and_users_list_.root(), nullptr, "user", p->info().config_address(), diff);
send_to_lobby(diff); send_to_lobby(diff);
@ -1526,18 +1514,18 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return; return;
// Everything below should only be processed if the game is already initialized. // Everything below should only be processed if the game is already initialized.
} else if(!g.level_init()) { } else if(!g.level_init()) {
WRN_SERVER << client_address(socket) << "\tReceived unknown data from: " << player.name() WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name()
<< " (socket:" << socket << ") while the scenario wasn't yet initialized.\n" << " (socket:" << p->socket() << ") while the scenario wasn't yet initialized.\n"
<< data.output(); << data.output();
return; return;
// If the host is sending the next scenario data. // If the host is sending the next scenario data.
} else if(const simple_wml::node* scenario = data.child("store_next_scenario")) { } else if(const simple_wml::node* scenario = data.child("store_next_scenario")) {
if(!g.is_owner(socket)) { if(!g.is_owner(p)) {
return; return;
} }
if(!g.level_init()) { if(!g.level_init()) {
WRN_SERVER << client_address(socket) << "\tWarning: " << player.name() WRN_SERVER << p->client_ip() << "\tWarning: " << player.name()
<< "\tsent [store_next_scenario] in game:\t\"" << g.name() << "\" (" << g.id() << "\tsent [store_next_scenario] in game:\t\"" << g.name() << "\" (" << g.id()
<< ", " << g.db_id() << ") while the scenario is not yet initialized."; << ", " << g.db_id() << ") while the scenario is not yet initialized.";
return; return;
@ -1548,7 +1536,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
user_handler_->db_update_game_end(uuid_, g.db_id(), g.get_replay_filename()); user_handler_->db_update_game_end(uuid_, g.db_id(), g.get_replay_filename());
} }
g.new_scenario(socket); g.new_scenario(p);
g.reset_last_synced_context_id(); g.reset_last_synced_context_id();
// Record the full scenario in g.level() // Record the full scenario in g.level()
@ -1557,7 +1545,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
g.next_db_id(); g.next_db_id();
if(g.description() == nullptr) { if(g.description() == nullptr) {
ERR_SERVER << client_address(socket) << "\tERROR: \"" << g.name() << "\" (" << g.id() ERR_SERVER << p->client_ip() << "\tERROR: \"" << g.name() << "\" (" << g.id()
<< ", " << g.db_id() << ") is initialized but has no description_.\n"; << ", " << g.db_id() << ") is initialized but has no description_.\n";
return; return;
} }
@ -1568,12 +1556,12 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
if(const simple_wml::node* m = scenario->child("multiplayer")) { if(const simple_wml::node* m = scenario->child("multiplayer")) {
m->copy_into(desc); m->copy_into(desc);
} else { } else {
WRN_SERVER << client_address(socket) << "\t" << player.name() << "\tsent scenario data in game:\t\"" WRN_SERVER << p->client_ip() << "\t" << player.name() << "\tsent scenario data in game:\t\""
<< g.name() << "\" (" << g.id() << ", " << g.db_id() << ") without a 'multiplayer' child.\n"; << g.name() << "\" (" << g.id() << ", " << g.db_id() << ") without a 'multiplayer' child.\n";
delete_game(g.id()); delete_game(g.id());
send_server_message(socket, send_server_message(p,
"The scenario data is missing the [multiplayer] tag which contains the game " "The scenario data is missing the [multiplayer] tag which contains the game "
"settings. Game aborted.", "error"); "settings. Game aborted.", "error");
return; return;
@ -1597,17 +1585,17 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// Tell everyone that the next scenario data is available. // Tell everyone that the next scenario data is available.
static simple_wml::document notify_next_scenario( static simple_wml::document notify_next_scenario(
"[notify_next_scenario]\n[/notify_next_scenario]\n", simple_wml::INIT_COMPRESSED); "[notify_next_scenario]\n[/notify_next_scenario]\n", simple_wml::INIT_COMPRESSED);
g.send_data(notify_next_scenario, socket); g.send_data(notify_next_scenario, p);
// Send the update of the game description to the lobby. // Send the update of the game description to the lobby.
update_game_in_lobby(g); update_game_in_lobby(g);
return; return;
// A mp client sends a request for the next scenario of a mp campaign. // A mp client sends a request for the next scenario of a mp campaign.
} else if(data.child("load_next_scenario")) { } else if(data.child("load_next_scenario")) {
g.load_next_scenario(socket); g.load_next_scenario(p);
return; return;
} else if(data.child("start_game")) { } else if(data.child("start_game")) {
if(!g.is_owner(socket)) { if(!g.is_owner(p)) {
return; return;
} }
@ -1617,8 +1605,8 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// Send notification of the game starting immediately. // Send notification of the game starting immediately.
// g.start_game() will send data that assumes // g.start_game() will send data that assumes
// the [start_game] message has been sent // the [start_game] message has been sent
g.send_data(data, socket); g.send_data(data, p);
g.start_game(socket); g.start_game(p);
if(user_handler_) { if(user_handler_) {
const simple_wml::node& m = *g.level().root().child("multiplayer"); const simple_wml::node& m = *g.level().root().child("multiplayer");
@ -1659,13 +1647,13 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
update_game_in_lobby(g); update_game_in_lobby(g);
return; return;
} else if(data.child("leave_game")) { } else if(data.child("leave_game")) {
if(g.remove_player(socket)) { if(g.remove_player(p)) {
delete_game(g.id()); delete_game(g.id());
} else { } else {
auto description = g.description(); auto description = g.description();
// After this line, the game object may be destroyed. Don't use `g`! // After this line, the game object may be destroyed. Don't use `g`!
player_connections_.modify(player_connections_.find(socket), std::bind(&player_record::enter_lobby, std::placeholders::_1)); player_connections_.modify(p, std::bind(&player_record::enter_lobby, std::placeholders::_1));
// Only run this if the game object is still valid // Only run this if the game object is still valid
if(auto gStrong = g_ptr.lock()) { if(auto gStrong = g_ptr.lock()) {
@ -1678,17 +1666,17 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
bool diff2 = make_change_diff(games_and_users_list_.root(), nullptr, "user", player.config_address(), diff); bool diff2 = make_change_diff(games_and_users_list_.root(), nullptr, "user", player.config_address(), diff);
if(diff1 || diff2) { if(diff1 || diff2) {
send_to_lobby(diff, socket); send_to_lobby(diff, p);
} }
// Send the player who has quit the gamelist. // Send the player who has quit the gamelist.
async_send_doc_queued(socket, games_and_users_list_); async_send_doc_queued(p->socket(), games_and_users_list_);
} }
return; return;
// If this is data describing side changes by the host. // If this is data describing side changes by the host.
} else if(const simple_wml::node* scenario_diff = data.child("scenario_diff")) { } else if(const simple_wml::node* scenario_diff = data.child("scenario_diff")) {
if(!g.is_owner(socket)) { if(!g.is_owner(p)) {
return; return;
} }
@ -1705,15 +1693,15 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
update_game_in_lobby(g); update_game_in_lobby(g);
} }
g.send_data(data, socket); g.send_data(data, p);
return; return;
// If a player changes his faction. // If a player changes his faction.
} else if(data.child("change_faction")) { } else if(data.child("change_faction")) {
g.send_data(data, socket); g.send_data(data, p);
return; return;
// If the owner of a side is changing the controller. // If the owner of a side is changing the controller.
} else if(const simple_wml::node* change = data.child("change_controller")) { } else if(const simple_wml::node* change = data.child("change_controller")) {
g.transfer_side_control(socket, *change); g.transfer_side_control(p, *change);
if(g.describe_slots()) { if(g.describe_slots()) {
update_game_in_lobby(g); update_game_in_lobby(g);
} }
@ -1721,8 +1709,8 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return; return;
// If all observers should be muted. (toggles) // If all observers should be muted. (toggles)
} else if(data.child("muteall")) { } else if(data.child("muteall")) {
if(!g.is_owner(socket)) { if(!g.is_owner(p)) {
g.send_server_message("You cannot mute: not the game host.", socket); g.send_server_message("You cannot mute: not the game host.", p);
return; return;
} }
@ -1730,21 +1718,21 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return; return;
// If an observer should be muted. // If an observer should be muted.
} else if(const simple_wml::node* mute = data.child("mute")) { } else if(const simple_wml::node* mute = data.child("mute")) {
g.mute_observer(*mute, socket); g.mute_observer(*mute, p);
return; return;
// If an observer should be unmuted. // If an observer should be unmuted.
} else if(const simple_wml::node* unmute = data.child("unmute")) { } else if(const simple_wml::node* unmute = data.child("unmute")) {
g.unmute_observer(*unmute, socket); g.unmute_observer(*unmute, p);
return; return;
// The owner is kicking/banning someone from the game. // The owner is kicking/banning someone from the game.
} else if(data.child("kick") || data.child("ban")) { } else if(data.child("kick") || data.child("ban")) {
bool ban = (data.child("ban") != nullptr); bool ban = (data.child("ban") != nullptr);
const socket_ptr user = (ban auto user { ban
? g.ban_user(*data.child("ban"), socket) ? g.ban_user(*data.child("ban"), p)
: g.kick_member(*data.child("kick"), socket)); : g.kick_member(*data.child("kick"), p)};
if(user) { if(user) {
player_connections_.modify(player_connections_.find(user), std::bind(&player_record::enter_lobby, std::placeholders::_1)); player_connections_.modify(user.value(), std::bind(&player_record::enter_lobby, std::placeholders::_1));
if(g.describe_slots()) { if(g.describe_slots()) {
update_game_in_lobby(g, user); update_game_in_lobby(g, user);
} }
@ -1752,21 +1740,21 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// Send all other players in the lobby the update to the gamelist. // Send all other players in the lobby the update to the gamelist.
simple_wml::document gamelist_diff; simple_wml::document gamelist_diff;
make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g.description(), gamelist_diff); make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g.description(), gamelist_diff);
make_change_diff(games_and_users_list_.root(), nullptr, "user", player_connections_.find(user)->info().config_address(), gamelist_diff); make_change_diff(games_and_users_list_.root(), nullptr, "user", user.value()->info().config_address(), gamelist_diff);
send_to_lobby(gamelist_diff, socket); send_to_lobby(gamelist_diff, p);
// Send the removed user the lobby game list. // Send the removed user the lobby game list.
async_send_doc_queued(user, games_and_users_list_); async_send_doc_queued(user.value()->socket(), games_and_users_list_);
} }
return; return;
} else if(const simple_wml::node* unban = data.child("unban")) { } else if(const simple_wml::node* unban = data.child("unban")) {
g.unban_user(*unban, socket); g.unban_user(*unban, p);
return; return;
// If info is being provided about the game state. // If info is being provided about the game state.
} else if(const simple_wml::node* info = data.child("info")) { } else if(const simple_wml::node* info = data.child("info")) {
if(!g.is_player(socket)) { if(!g.is_player(p)) {
return; return;
} }
@ -1785,26 +1773,26 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
// Notify the game of the commands, and if it changes // Notify the game of the commands, and if it changes
// the description, then sync the new description // the description, then sync the new description
// to players in the lobby. // to players in the lobby.
if(g.process_turn(data, socket)) { if(g.process_turn(data, p)) {
update_game_in_lobby(g); update_game_in_lobby(g);
} }
return; return;
} else if(data.child("whiteboard")) { } else if(data.child("whiteboard")) {
g.process_whiteboard(data, socket); g.process_whiteboard(data, p);
return; return;
} else if(data.child("change_turns_wml")) { } else if(data.child("change_turns_wml")) {
g.process_change_turns_wml(data, socket); g.process_change_turns_wml(data, p);
update_game_in_lobby(g); update_game_in_lobby(g);
return; return;
} else if(simple_wml::node* sch = data.child("request_choice")) { } else if(simple_wml::node* sch = data.child("request_choice")) {
g.handle_choice(*sch, socket); g.handle_choice(*sch, p);
return; return;
} else if(data.child("message")) { } else if(data.child("message")) {
g.process_message(data, socket); g.process_message(data, p);
return; return;
} else if(data.child("stop_updates")) { } else if(data.child("stop_updates")) {
g.send_data(data, socket); g.send_data(data, p);
return; return;
} else if(simple_wml::node* request = data.child("game_history_request")) { } else if(simple_wml::node* request = data.child("game_history_request")) {
if(user_handler_) { if(user_handler_) {
@ -1842,7 +1830,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return; return;
} }
WRN_SERVER << client_address(socket) << "\tReceived unknown data from: " << player.name() << " (socket:" << socket WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name() << " (socket:" << p->socket()
<< ") in game: \"" << g.name() << "\" (" << g.id() << ", " << g.db_id() << ")\n" << ") in game: \"" << g.name() << "\" (" << g.id() << ", " << g.db_id() << ")\n"
<< data.output(); << data.output();
} }
@ -1858,23 +1846,19 @@ void server::send_server_message(socket_ptr socket, const std::string& message,
async_send_doc_queued(socket, server_message); async_send_doc_queued(socket, server_message);
} }
void server::remove_player(socket_ptr socket) void server::disconnect_player(player_iterator player)
{ {
std::string ip; player->socket()->shutdown(boost::asio::ip::tcp::socket::shutdown_receive);
auto iter = player_connections_.find(socket);
if(iter == player_connections_.end()) {
return;
} else {
// client_address() is very likely to return <unknown address> at this point
// so we remember ip in player_connections_
ip = iter->saved_client_ip();
} }
void server::remove_player(player_iterator iter)
{
std::string ip = iter->client_ip();
const std::shared_ptr<game> g = iter->get_game(); const std::shared_ptr<game> g = iter->get_game();
bool game_ended = false; bool game_ended = false;
if(g) { if(g) {
game_ended = g->remove_player(socket, true, false); game_ended = g->remove_player(iter, true, false);
} }
const simple_wml::node::child_list& users = games_and_users_list_.root().children("user"); const simple_wml::node::child_list& users = games_and_users_list_.root().children("user");
@ -1884,7 +1868,7 @@ void server::remove_player(socket_ptr socket)
// Notify other players in lobby // Notify other players in lobby
simple_wml::document diff; simple_wml::document diff;
if(make_delete_diff(games_and_users_list_.root(), nullptr, "user", iter->info().config_address(), diff)) { if(make_delete_diff(games_and_users_list_.root(), nullptr, "user", iter->info().config_address(), diff)) {
send_to_lobby(diff, socket); send_to_lobby(diff, iter);
} }
games_and_users_list_.root().remove_child("user", index); games_and_users_list_.root().remove_child("user", index);
@ -1902,39 +1886,37 @@ void server::remove_player(socket_ptr socket)
player_connections_.erase(iter); player_connections_.erase(iter);
if(socket->is_open()) {
socket->close();
}
if(lan_server_ && player_connections_.size() == 0) if(lan_server_ && player_connections_.size() == 0)
start_lan_server_timer(); start_lan_server_timer();
if(game_ended) delete_game(g->id()); if(game_ended) delete_game(g->id());
} }
void server::send_to_lobby(simple_wml::document& data, socket_ptr exclude) void server::send_to_lobby(simple_wml::document& data, utils::optional<player_iterator> exclude)
{ {
for(const auto& player : player_connections_.get<game_t>().equal_range(0)) { for(const auto& p : player_connections_.get<game_t>().equal_range(0)) {
if(player.socket() != exclude) { auto player { player_connections_.iterator_to(p) };
async_send_doc_queued(player.socket(), data); if(player != exclude) {
async_send_doc_queued(player->socket(), data);
} }
} }
} }
void server::send_server_message_to_lobby(const std::string& message, socket_ptr exclude) void server::send_server_message_to_lobby(const std::string& message, utils::optional<player_iterator> exclude)
{ {
for(const auto& player : player_connections_.get<game_t>().equal_range(0)) { for(const auto& p : player_connections_.get<game_t>().equal_range(0)) {
if(player.socket() != exclude) { auto player { player_connections_.iterator_to(p) };
send_server_message(player.socket(), message, "alert"); if(player != exclude) {
send_server_message(player, message, "alert");
} }
} }
} }
void server::send_server_message_to_all(const std::string& message, socket_ptr exclude) void server::send_server_message_to_all(const std::string& message, utils::optional<player_iterator> exclude)
{ {
for(const auto& player : player_connections_) { for(auto player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(player.socket() != exclude) { if(player != exclude) {
send_server_message(player.socket(), message, "alert"); send_server_message(player, message, "alert");
} }
} }
} }
@ -2172,7 +2154,7 @@ void server::roll_handler(const std::string& issuer_name,
auto g_ptr = player_ptr->get_game(); auto g_ptr = player_ptr->get_game();
if(g_ptr) { if(g_ptr) {
g_ptr->send_server_message_to_all(issuer_name + " rolled a die [1 - " + parameters + "] and got a " + value + ".", player_ptr->socket()); g_ptr->send_server_message_to_all(issuer_name + " rolled a die [1 - " + parameters + "] and got a " + value + ".", player_connections_.project<0>(player_ptr));
} else { } else {
*out << " (The result is shown to others only in a game.)"; *out << " (The result is shown to others only in a game.)";
} }
@ -2361,7 +2343,7 @@ void server::status_handler(
if(utils::isvalid_username(parameters)) { if(utils::isvalid_username(parameters)) {
for(const auto& player : player_connections_) { for(const auto& player : player_connections_) {
if(utf8::lowercase(parameters) == utf8::lowercase(player.info().name())) { if(utf8::lowercase(parameters) == utf8::lowercase(player.info().name())) {
parameters = client_address(player.socket()); parameters = player.client_ip();
found_something = true; found_something = true;
break; break;
} }
@ -2378,7 +2360,7 @@ void server::status_handler(
const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1); const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
for(const auto& player : player_connections_) { for(const auto& player : player_connections_) {
if(parameters.empty() || parameters == "*" || if(parameters.empty() || parameters == "*" ||
(match_ip && utils::wildcard_string_match(client_address(player.socket()), parameters)) || (match_ip && utils::wildcard_string_match(player.client_ip(), parameters)) ||
(!match_ip && utils::wildcard_string_match(utf8::lowercase(player.info().name()), utf8::lowercase(parameters))) (!match_ip && utils::wildcard_string_match(utf8::lowercase(player.info().name()), utf8::lowercase(parameters)))
) { ) {
found_something = true; found_something = true;
@ -2402,16 +2384,16 @@ void server::clones_handler(const std::string& /*issuer_name*/,
std::set<std::string> clones; std::set<std::string> clones;
for(auto it = player_connections_.begin(); it != player_connections_.end(); ++it) { for(auto it = player_connections_.begin(); it != player_connections_.end(); ++it) {
if(clones.find(client_address(it->socket())) != clones.end()) { if(clones.find(it->client_ip()) != clones.end()) {
continue; continue;
} }
bool found = false; bool found = false;
for(auto clone = std::next(it); clone != player_connections_.end(); ++clone) { for(auto clone = std::next(it); clone != player_connections_.end(); ++clone) {
if(client_address(it->socket()) == client_address(clone->socket())) { if(it->client_ip() == clone->client_ip()) {
if(!found) { if(!found) {
found = true; found = true;
clones.insert(client_address(it->socket())); clones.insert(it->client_ip());
*out << std::endl << player_status(*it); *out << std::endl << player_status(*it);
} }
@ -2502,7 +2484,7 @@ void server::ban_handler(
banned = true; banned = true;
} }
const std::string ip = client_address(player.socket()); const std::string ip = player.client_ip();
*out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target); *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
} }
} }
@ -2564,7 +2546,7 @@ void server::kickban_handler(
} }
std::string dummy_group; std::string dummy_group;
std::vector<socket_ptr> users_to_kick; std::vector<player_iterator> users_to_kick;
// if we find a '.' consider it an ip mask // if we find a '.' consider it an ip mask
/** @todo FIXME: make a proper check for valid IPs. */ /** @todo FIXME: make a proper check for valid IPs. */
@ -2573,23 +2555,23 @@ void server::kickban_handler(
*out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group); *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group);
for(const auto& player : player_connections_) { for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(utils::wildcard_string_match(client_address(player.socket()), target)) { if(utils::wildcard_string_match(player->client_ip(), target)) {
users_to_kick.push_back(player.socket()); users_to_kick.push_back(player);
} }
} }
} else { } else {
for(const auto& player : player_connections_) { for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(utils::wildcard_string_match(player.info().name(), target)) { if(utils::wildcard_string_match(player->info().name(), target)) {
if(banned) { if(banned) {
*out << "\n"; *out << "\n";
} else { } else {
banned = true; banned = true;
} }
const std::string ip = client_address(player.socket()); const std::string ip = player->client_ip();
*out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target); *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
users_to_kick.push_back(player.socket()); users_to_kick.push_back(player);
} }
} }
@ -2613,10 +2595,10 @@ void server::kickban_handler(
} }
} }
for(const auto& user : users_to_kick) { for(auto user : users_to_kick) {
*out << "\nKicked " << player_connections_.find(user)->info().name() << " (" << client_address(user) << ")."; *out << "\nKicked " << user->info().name() << " (" << user->client_ip() << ").";
async_send_error(user, "You have been banned. Reason: " + reason); async_send_error(user->socket(), "You have been banned. Reason: " + reason);
remove_player(user); disconnect_player(user);
} }
} }
@ -2674,7 +2656,7 @@ void server::gban_handler(
banned = true; banned = true;
} }
const std::string ip = client_address(player.socket()); const std::string ip = player.client_ip();
*out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, group, target); *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, group, target);
} }
} }
@ -2753,27 +2735,27 @@ void server::kick_handler(const std::string& /*issuer_name*/,
// if we find a '.' consider it an ip mask // if we find a '.' consider it an ip mask
const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(), '.') >= 1); const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(), '.') >= 1);
std::vector<socket_ptr> users_to_kick; std::vector<player_iterator> users_to_kick;
for(const auto& player : player_connections_) { for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if((match_ip && utils::wildcard_string_match(client_address(player.socket()), kick_mask)) || if((match_ip && utils::wildcard_string_match(player->client_ip(), kick_mask)) ||
(!match_ip && utils::wildcard_string_match(player.info().name(), kick_mask)) (!match_ip && utils::wildcard_string_match(player->info().name(), kick_mask))
) { ) {
users_to_kick.push_back(player.socket()); users_to_kick.push_back(player);
} }
} }
for(const auto& socket : users_to_kick) { for(const auto& player : users_to_kick) {
if(kicked) { if(kicked) {
*out << "\n"; *out << "\n";
} else { } else {
kicked = true; kicked = true;
} }
*out << "Kicked " << player_connections_.find(socket)->name() << " (" << client_address(socket) << "). '" *out << "Kicked " << player->name() << " (" << player->client_ip() << "). '"
<< kick_message << "'"; << kick_message << "'";
async_send_error(socket, kick_message); async_send_error(player->socket(), kick_message);
remove_player(socket); disconnect_player(player);
} }
if(!kicked) { if(!kicked) {
@ -2832,7 +2814,7 @@ void server::searchlog_handler(const std::string& /*issuer_name*/,
found_something = true; found_something = true;
auto player = player_connections_.get<name_t>().find(username); auto player = player_connections_.get<name_t>().find(username);
if(player != player_connections_.get<name_t>().end() && client_address(player->socket()) == ip) { if(player != player_connections_.get<name_t>().end() && player->client_ip() == ip) {
*out << std::endl << player_status(*player); *out << std::endl << player_status(*player);
} else { } else {
*out << "\n'" << username << "' @ " << ip *out << "\n'" << username << "' @ " << ip
@ -2932,7 +2914,7 @@ void server::delete_game(int gameid, const std::string& reason)
} }
} }
void server::update_game_in_lobby(const wesnothd::game& g, const socket_ptr& exclude) void server::update_game_in_lobby(const wesnothd::game& g, utils::optional<player_iterator> exclude)
{ {
simple_wml::document diff; simple_wml::document diff;
if(make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g.description(), diff)) { if(make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g.description(), diff)) {

View file

@ -23,6 +23,8 @@
#include "server/common/server_base.hpp" #include "server/common/server_base.hpp"
#include "server/wesnothd/player_connection.hpp" #include "server/wesnothd/player_connection.hpp"
#include "utils/optional_fwd.hpp"
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <random> #include <random>
@ -46,26 +48,28 @@ private:
bool accepting_connections() const { return !graceful_restart; } bool accepting_connections() const { return !graceful_restart; }
void handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player); void handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player);
void handle_player_in_lobby(socket_ptr socket, simple_wml::document& data); void handle_player_in_lobby(player_iterator player, simple_wml::document& doc);
void handle_player_in_game(socket_ptr socket, simple_wml::document& data); void handle_player_in_game(player_iterator player, simple_wml::document& doc);
void handle_whisper(socket_ptr socket, simple_wml::node& whisper); void handle_whisper(player_iterator player, simple_wml::node& whisper);
void handle_query(socket_ptr socket, simple_wml::node& query); void handle_query(player_iterator player, simple_wml::node& query);
void handle_nickserv(socket_ptr socket, simple_wml::node& nickserv); void handle_nickserv(player_iterator player, simple_wml::node& nickserv);
void handle_message(socket_ptr socket, simple_wml::node& message); void handle_message(player_iterator player, simple_wml::node& message);
void handle_create_game(socket_ptr socket, simple_wml::node& create_game); void handle_create_game(player_iterator player, simple_wml::node& create_game);
void create_game(player_record& host, simple_wml::node& create_game);
void cleanup_game(game*); // deleter for shared_ptr void cleanup_game(game*); // deleter for shared_ptr
void handle_join_game(socket_ptr socket, simple_wml::node& join); void handle_join_game(player_iterator player, simple_wml::node& join);
void remove_player(socket_ptr socket); 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); void send_server_message(socket_ptr socket, const std::string& message, const std::string& type);
void send_to_lobby(simple_wml::document& data, socket_ptr exclude = socket_ptr()); void send_server_message(player_iterator player, const std::string& message, const std::string& type) {
void send_server_message_to_lobby(const std::string& message, socket_ptr exclude = socket_ptr()); send_server_message(player->socket(), message, type);
void send_server_message_to_all(const std::string& message, socket_ptr exclude = socket_ptr()); }
void send_to_lobby(simple_wml::document& data, utils::optional<player_iterator> exclude = {});
void send_server_message_to_lobby(const std::string& message, utils::optional<player_iterator> exclude = {});
void send_server_message_to_all(const std::string& message, utils::optional<player_iterator> exclude = {});
bool player_is_in_game(socket_ptr socket) const bool player_is_in_game(player_iterator player) const {
{ return player->get_game() != nullptr;
return player_connections_.find(socket)->get_game() != nullptr;
} }
wesnothd::ban_manager ban_manager_; wesnothd::ban_manager ban_manager_;
@ -190,7 +194,7 @@ private:
void delete_game(int, const std::string& reason=""); void delete_game(int, const std::string& reason="");
void update_game_in_lobby(const wesnothd::game& g, const socket_ptr& exclude=socket_ptr()); void update_game_in_lobby(const game& g, utils::optional<player_iterator> exclude = {});
void start_new_server(); void start_new_server();