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 "utils/make_enum.hpp"
#include "utils/optional_fwd.hpp"
#include <map>
#include <vector>
@ -27,8 +29,8 @@
namespace wesnothd
{
typedef std::vector<socket_ptr> user_vector;
typedef std::vector<socket_ptr> side_vector;
typedef std::vector<player_iterator> user_vector;
typedef std::vector<utils::optional<player_iterator>> side_vector;
class server;
class game
@ -41,7 +43,7 @@ public:
);
game(wesnothd::server& server, player_connections& player_connections,
const socket_ptr& host,
player_iterator host,
const std::string& name = "",
bool save_replays = false,
const std::string& replay_save_path = "");
@ -68,25 +70,25 @@ public:
return name_;
}
bool is_owner(const socket_ptr& player) const
bool is_owner(player_iterator player) const
{
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);
}
bool allow_observers() const;
bool is_observer(const socket_ptr& player) const;
bool is_player(const socket_ptr& player) const;
bool is_observer(player_iterator player) const;
bool is_player(player_iterator player) const;
/** 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 */
void new_scenario(const socket_ptr& player);
void new_scenario(player_iterator player);
bool level_init() const
{
@ -153,36 +155,36 @@ public:
* Mute an observer or give a message of all currently muted observers if no
* 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.
*
* @return The network handle of the removed member if
* successful, null pointer otherwise.
* @return The iterator to the removed member if
* 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.
*
* The user does not need to be in this game but logged in.
*
* @return The network handle of the banned player if he
* was in this game, null pointer otherwise.
* @return The iterator to the banned player if he
* 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.
*
* @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.
@ -191,12 +193,12 @@ public:
* no more players or the host left on a not yet
* 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. */
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
// send scenario_diff's specific to each client so that they locally
@ -206,7 +208,7 @@ public:
void update_game();
/** 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
// properly
@ -215,9 +217,9 @@ public:
void update_side_data();
/** 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
@ -228,12 +230,12 @@ public:
*
* @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. */
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. */
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.
@ -242,30 +244,30 @@ public:
*/
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 std::string& 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, utils::optional<player_iterator> exclude = {})
{
send_server_message_to_all(message.c_str(), exclude);
}
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(
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'. */
void send_and_record_server_message(const char* message, const socket_ptr& exclude = socket_ptr());
void send_and_record_server_message(const std::string& 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, utils::optional<player_iterator> exclude = {})
{
send_and_record_server_message(message.c_str(), exclude);
}
template<typename Container>
void send_to_players(simple_wml::document& data, const Container& players, socket_ptr exclude = socket_ptr());
void send_data(simple_wml::document& data, const socket_ptr& exclude = socket_ptr(), std::string packet_type = "");
void send_to_players(simple_wml::document& data, const Container& players, utils::optional<player_iterator> exclude = {});
void send_data(simple_wml::document& data, utils::optional<player_iterator> exclude = {}, std::string packet_type = "");
void clear_history();
void record_data(std::unique_ptr<simple_wml::document> data);
@ -318,7 +320,7 @@ public:
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);
@ -334,7 +336,7 @@ public:
/**
* 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;
@ -348,23 +350,23 @@ private:
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);
}
bool is_muted_observer(const socket_ptr& player) const;
bool is_muted_observer(player_iterator player) const;
bool all_observers_muted() const
{
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;
@ -377,21 +379,21 @@ private:
* 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.
*/
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
* or controller type (human or ai).
*/
void change_controller(const std::size_t side_num,
const socket_ptr& sock,
player_iterator sock,
const std::string& player_name,
const bool player_left = true);
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);
void transfer_ai_sides(const socket_ptr& player);
void send_leave_game(const socket_ptr& user) const;
void transfer_ai_sides(player_iterator player);
void send_leave_game(player_iterator user) const;
/**
* @param data the data to be sent to the sides.
@ -400,24 +402,24 @@ private:
*/
void send_data_sides(simple_wml::document& data,
const simple_wml::string_span& sides,
const socket_ptr& exclude = socket_ptr());
utils::optional<player_iterator> exclude = {});
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
* everyone if none given.
*/
void send_observerjoins(const socket_ptr& sock = socket_ptr());
void send_observerquit(const socket_ptr& observer);
void send_history(const socket_ptr& sock) const;
void send_observerjoins(utils::optional<player_iterator> player = {});
void send_observerquit(player_iterator observer);
void send_history(player_iterator sock) const;
/** In case of a host transfer, notify the new host about its status. */
void notify_new_host();
/** 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
{
@ -429,13 +431,13 @@ private:
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.
* @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
@ -451,16 +453,13 @@ private:
*
* 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)". */
std::string username(const socket_ptr& pl) const;
std::string username(player_iterator pl) const;
/** Returns a comma separated list of user names. */
std::string list_users(user_vector users, const std::string& func) 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;
std::string list_users(user_vector users) const;
/** calculates the initial value for sides_, side_controllerds_, nsides_*/
void reset_sides();
@ -489,7 +488,7 @@ private:
std::string password_;
/** The game host or later owner (if the host left). */
socket_ptr owner_;
player_iterator owner_;
/** A vector of players (members owning a side). */
user_vector players_;
@ -544,7 +543,7 @@ private:
* keep track of those players because processing certain
* 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_;

View file

@ -44,7 +44,7 @@ public:
return socket_;
}
std::string saved_client_ip() const
std::string client_ip() const
{
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>>
>>;
typedef player_connections::const_iterator player_iterator;
} // 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)
{
std::ostringstream out;
out << "'" << player.name() << "' @ " << client_address(player.socket());
out << "'" << player.name() << "' @ " << player.client_ip();
return out.str();
}
@ -996,37 +996,38 @@ 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)
void server::handle_player(boost::asio::yield_context yield, socket_ptr socket, const player& player_data)
{
if(lan_server_)
abort_lan_server_timer();
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);
BOOST_SCOPE_EXIT_ALL(this, &socket) {
remove_player(socket);
BOOST_SCOPE_EXIT_ALL(this, &player) {
remove_player(player);
};
async_send_doc_queued(socket, games_and_users_list_);
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(socket, announcements_+tournaments_, "announcements");
if(version_info(player.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, information_, "server_info");
send_server_message(player, announcements_+tournaments_, "announcements");
if(version_info(player_data.version()) < secure_version ){
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_)) {
send_server_message(socket, "A newer Wesnoth version, " + recommended_version_ + ", is out!", "alert");
if(version_info(player_data.version()) < version_info(recommended_version_)) {
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
simple_wml::document diff;
make_add_diff(games_and_users_list_.root(), nullptr, "user", diff);
send_to_lobby(diff, socket);
send_to_lobby(diff, player);
while(true) {
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")) {
handle_whisper(socket, *whisper);
handle_whisper(player, *whisper);
}
if(simple_wml::node* query = doc->child("query")) {
handle_query(socket, *query);
handle_query(player, *query);
}
if(simple_wml::node* nickserv = doc->child("nickserv")) {
handle_nickserv(socket, *nickserv);
handle_nickserv(player, *nickserv);
}
if(!player_is_in_game(socket)) {
handle_player_in_lobby(socket, *doc);
if(!player_is_in_game(player)) {
handle_player_in_lobby(player, *doc);
} 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")) {
handle_message(socket, *message);
handle_message(player, *message);
return;
}
if(simple_wml::node* create_game = data.child("create_game")) {
handle_create_game(socket, *create_game);
handle_create_game(player, *create_game);
return;
}
if(simple_wml::node* join = data.child("join")) {
handle_join_game(socket, *join);
handle_join_game(player, *join);
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())) {
static simple_wml::document data(
@ -1087,21 +1088,21 @@ void server::handle_whisper(socket_ptr socket, simple_wml::node& whisper)
simple_wml::INIT_COMPRESSED
);
async_send_doc_queued(socket, data);
async_send_doc_queued(player->socket(), data);
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());
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;
}
auto g = player_connections_.find(socket)->get_game();
if(g && g->started() && g->is_player(receiver_iter->socket())) {
send_server_message(socket, "You cannot send private messages to players in a running game you observe.", "error");
auto g = player->get_game();
if(g && g->started() && g->is_player(player_connections_.project<0>(receiver_iter))) {
send_server_message(player, "You cannot send private messages to players in a running game you observe.", "error");
return;
}
@ -1116,13 +1117,8 @@ void server::handle_whisper(socket_ptr socket, simple_wml::node& whisper)
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();
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());
} else if(player.is_moderator()) {
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;
player.set_moderator(false);
// 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);
}
} 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;
response << process_command(command, player.name());
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;
} else if(command == "admin" || command.compare(0, 6, "admin ") == 0) {
if(admin_passwd_.empty()) {
send_server_message(socket, "No password set.", "error");
send_server_message(iter, "No password set.", "error");
return;
}
@ -1180,7 +1176,7 @@ void server::handle_query(socket_ptr socket, simple_wml::node& query)
}
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;
player.set_moderator(true);
// 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);
}
} 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;
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;
}
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
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;
}
@ -1213,9 +1209,9 @@ void server::handle_nickserv(socket_ptr socket, simple_wml::node& nickserv)
if(nickserv.child("info")) {
try {
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) {
send_server_message(socket,
send_server_message(player,
"There was an error looking up the details of the user '"
+ (*nickserv.child("info"))["name"].to_string() + "'. "
+ " 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()) {
send_server_message(socket,
send_server_message(user,
"Warning: you are sending too many messages too fast. Your message has not been relayed.", "error");
return;
}
@ -1245,52 +1240,46 @@ void server::handle_message(socket_ptr socket, simple_wml::node& message)
chat_message::truncate_message(msg, trunc_message);
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";
} 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) {
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 "
"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;
}
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_password = create_game["password"].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";
// Create the new game, remove the player from the lobby
// and set the player as the host/owner.
host_record.get_game().reset(
new wesnothd::game(*this, player_connections_, host_record.socket(), game_name, save_replays_, replay_save_path_),
std::bind(&server::cleanup_game, this, std::placeholders::_1)
);
player_connections_.modify(player, [this, player, &game_name](player_record& host_record) {
host_record.get_game().reset(
new wesnothd::game(*this, player_connections_, player, game_name, save_replays_, replay_save_path_),
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";
if(initial_bans != "") {
@ -1336,7 +1325,7 @@ void server::cleanup_game(game* 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 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);
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";
async_send_doc_queued(socket, leave_game_doc);
send_server_message(socket, "Attempt to join unknown game.", "error");
async_send_doc_queued(socket, games_and_users_list_);
async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(player, "Attempt to join unknown game.", "error");
async_send_doc_queued(player->socket(), games_and_users_list_);
return;
} 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";
async_send_doc_queued(socket, leave_game_doc);
send_server_message(socket, "Attempt to join an uninitialized game.", "error");
async_send_doc_queued(socket, games_and_users_list_);
async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(player, "Attempt to join an uninitialized game.", "error");
async_send_doc_queued(player->socket(), games_and_users_list_);
return;
} else if(player_connections_.find(socket)->info().is_moderator()) {
} else if(player->info().is_moderator()) {
// Admins are always allowed to join.
} else if(g->player_is_banned(socket, player_connections_.find(socket)->info().name())) {
DBG_SERVER << client_address(socket)
<< "\tReject banned player: " << player_connections_.find(socket)->info().name()
} else if(g->player_is_banned(player, player->info().name())) {
DBG_SERVER << player->client_ip()
<< "\tReject banned player: " << player->info().name()
<< "\tfrom game:\t\"" << g->name() << "\" (" << game_id << ").\n";
async_send_doc_queued(socket, leave_game_doc);
send_server_message(socket, "You are banned from this game.", "error");
async_send_doc_queued(socket, games_and_users_list_);
async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(player, "You are banned from this game.", "error");
async_send_doc_queued(player->socket(), games_and_users_list_);
return;
} 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";
async_send_doc_queued(socket, leave_game_doc);
send_server_message(socket, "Incorrect password.", "error");
async_send_doc_queued(socket, games_and_users_list_);
async_send_doc_queued(player->socket(), leave_game_doc);
send_server_message(player, "Incorrect password.", "error");
async_send_doc_queued(player->socket(), games_and_users_list_);
return;
}
bool joined = g->add_player(socket, observer);
bool joined = g->add_player(player, observer);
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
<< ") 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 "
"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;
}
player_connections_.modify(player_connections_.find(socket),
std::bind(&player_record::set_game, std::placeholders::_1, player_connections_.get<game_t>().find(game_id)->get_game()));
player_connections_.modify(player,
std::bind(&player_record::set_game, std::placeholders::_1, g));
g->describe_slots();
@ -1407,26 +1396,25 @@ void server::handle_join_game(socket_ptr socket, simple_wml::node& join)
simple_wml::document 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",
player_connections_.find(socket)->info().config_address(), diff);
player->info().config_address(), diff);
if(diff1 || diff2) {
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";
auto p = player_connections_.find(socket);
wesnothd::player& player = p->info();
wesnothd::player& player { p->info() };
game& g = *(p->get_game());
std::weak_ptr<game> g_ptr{p->get_game()};
// If this is data describing the level for a game.
if(data.child("snapshot") || data.child("scenario")) {
if(!g.is_owner(socket)) {
if(!g.is_owner(p)) {
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.
// g.level() should then receive the full data for the game.
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";
// Update our config object which describes the open games,
// 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")) {
m->copy_into(desc);
} 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";
// Set the description so it can be removed in delete_game().
g.set_description(&desc);
delete_game(g.id());
send_server_message(socket,
send_server_message(p,
"The scenario data is missing the [multiplayer] tag which contains the "
"game settings. Game aborted.", "error");
return;
@ -1465,7 +1453,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
g.set_description(&desc);
desc.set_attr_dup("id", std::to_string(g.id()).c_str());
} 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";
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.
simple_wml::document 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);
@ -1526,18 +1514,18 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return;
// Everything below should only be processed if the game is already initialized.
} else if(!g.level_init()) {
WRN_SERVER << client_address(socket) << "\tReceived unknown data from: " << player.name()
<< " (socket:" << socket << ") while the scenario wasn't yet initialized.\n"
WRN_SERVER << p->client_ip() << "\tReceived unknown data from: " << player.name()
<< " (socket:" << p->socket() << ") while the scenario wasn't yet initialized.\n"
<< data.output();
return;
// If the host is sending the next scenario data.
} else if(const simple_wml::node* scenario = data.child("store_next_scenario")) {
if(!g.is_owner(socket)) {
if(!g.is_owner(p)) {
return;
}
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()
<< ", " << g.db_id() << ") while the scenario is not yet initialized.";
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());
}
g.new_scenario(socket);
g.new_scenario(p);
g.reset_last_synced_context_id();
// 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();
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";
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")) {
m->copy_into(desc);
} 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";
delete_game(g.id());
send_server_message(socket,
send_server_message(p,
"The scenario data is missing the [multiplayer] tag which contains the game "
"settings. Game aborted.", "error");
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.
static simple_wml::document notify_next_scenario(
"[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.
update_game_in_lobby(g);
return;
// A mp client sends a request for the next scenario of a mp campaign.
} else if(data.child("load_next_scenario")) {
g.load_next_scenario(socket);
g.load_next_scenario(p);
return;
} else if(data.child("start_game")) {
if(!g.is_owner(socket)) {
if(!g.is_owner(p)) {
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.
// g.start_game() will send data that assumes
// the [start_game] message has been sent
g.send_data(data, socket);
g.start_game(socket);
g.send_data(data, p);
g.start_game(p);
if(user_handler_) {
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);
return;
} else if(data.child("leave_game")) {
if(g.remove_player(socket)) {
if(g.remove_player(p)) {
delete_game(g.id());
} else {
auto description = g.description();
// 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
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);
if(diff1 || diff2) {
send_to_lobby(diff, socket);
send_to_lobby(diff, p);
}
// 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;
// If this is data describing side changes by the host.
} else if(const simple_wml::node* scenario_diff = data.child("scenario_diff")) {
if(!g.is_owner(socket)) {
if(!g.is_owner(p)) {
return;
}
@ -1705,15 +1693,15 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
update_game_in_lobby(g);
}
g.send_data(data, socket);
g.send_data(data, p);
return;
// If a player changes his faction.
} else if(data.child("change_faction")) {
g.send_data(data, socket);
g.send_data(data, p);
return;
// If the owner of a side is changing the 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()) {
update_game_in_lobby(g);
}
@ -1721,8 +1709,8 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return;
// If all observers should be muted. (toggles)
} else if(data.child("muteall")) {
if(!g.is_owner(socket)) {
g.send_server_message("You cannot mute: not the game host.", socket);
if(!g.is_owner(p)) {
g.send_server_message("You cannot mute: not the game host.", p);
return;
}
@ -1730,21 +1718,21 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
return;
// If an observer should be muted.
} else if(const simple_wml::node* mute = data.child("mute")) {
g.mute_observer(*mute, socket);
g.mute_observer(*mute, p);
return;
// If an observer should be unmuted.
} else if(const simple_wml::node* unmute = data.child("unmute")) {
g.unmute_observer(*unmute, socket);
g.unmute_observer(*unmute, p);
return;
// The owner is kicking/banning someone from the game.
} else if(data.child("kick") || data.child("ban")) {
bool ban = (data.child("ban") != nullptr);
const socket_ptr user = (ban
? g.ban_user(*data.child("ban"), socket)
: g.kick_member(*data.child("kick"), socket));
auto user { ban
? g.ban_user(*data.child("ban"), p)
: g.kick_member(*data.child("kick"), p)};
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()) {
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.
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_.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.
async_send_doc_queued(user, games_and_users_list_);
async_send_doc_queued(user.value()->socket(), games_and_users_list_);
}
return;
} else if(const simple_wml::node* unban = data.child("unban")) {
g.unban_user(*unban, socket);
g.unban_user(*unban, p);
return;
// If info is being provided about the game state.
} else if(const simple_wml::node* info = data.child("info")) {
if(!g.is_player(socket)) {
if(!g.is_player(p)) {
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
// the description, then sync the new description
// to players in the lobby.
if(g.process_turn(data, socket)) {
if(g.process_turn(data, p)) {
update_game_in_lobby(g);
}
return;
} else if(data.child("whiteboard")) {
g.process_whiteboard(data, socket);
g.process_whiteboard(data, p);
return;
} 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);
return;
} else if(simple_wml::node* sch = data.child("request_choice")) {
g.handle_choice(*sch, socket);
g.handle_choice(*sch, p);
return;
} else if(data.child("message")) {
g.process_message(data, socket);
g.process_message(data, p);
return;
} else if(data.child("stop_updates")) {
g.send_data(data, socket);
g.send_data(data, p);
return;
} else if(simple_wml::node* request = data.child("game_history_request")) {
if(user_handler_) {
@ -1842,7 +1830,7 @@ void server::handle_player_in_game(socket_ptr socket, simple_wml::document& data
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"
<< 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);
}
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();
bool game_ended = false;
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");
@ -1884,7 +1868,7 @@ void server::remove_player(socket_ptr socket)
// Notify other players in lobby
simple_wml::document 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);
@ -1902,39 +1886,37 @@ void server::remove_player(socket_ptr socket)
player_connections_.erase(iter);
if(socket->is_open()) {
socket->close();
}
if(lan_server_ && player_connections_.size() == 0)
start_lan_server_timer();
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)) {
if(player.socket() != exclude) {
async_send_doc_queued(player.socket(), data);
for(const auto& p : player_connections_.get<game_t>().equal_range(0)) {
auto player { player_connections_.iterator_to(p) };
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)) {
if(player.socket() != exclude) {
send_server_message(player.socket(), message, "alert");
for(const auto& p : player_connections_.get<game_t>().equal_range(0)) {
auto player { player_connections_.iterator_to(p) };
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_) {
if(player.socket() != exclude) {
send_server_message(player.socket(), message, "alert");
for(auto player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(player != exclude) {
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();
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 {
*out << " (The result is shown to others only in a game.)";
}
@ -2361,7 +2343,7 @@ void server::status_handler(
if(utils::isvalid_username(parameters)) {
for(const auto& player : player_connections_) {
if(utf8::lowercase(parameters) == utf8::lowercase(player.info().name())) {
parameters = client_address(player.socket());
parameters = player.client_ip();
found_something = true;
break;
}
@ -2378,7 +2360,7 @@ void server::status_handler(
const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
for(const auto& player : player_connections_) {
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)))
) {
found_something = true;
@ -2402,16 +2384,16 @@ void server::clones_handler(const std::string& /*issuer_name*/,
std::set<std::string> clones;
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;
}
bool found = false;
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) {
found = true;
clones.insert(client_address(it->socket()));
clones.insert(it->client_ip());
*out << std::endl << player_status(*it);
}
@ -2502,7 +2484,7 @@ void server::ban_handler(
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);
}
}
@ -2564,7 +2546,7 @@ void server::kickban_handler(
}
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
/** @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);
for(const auto& player : player_connections_) {
if(utils::wildcard_string_match(client_address(player.socket()), target)) {
users_to_kick.push_back(player.socket());
for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(utils::wildcard_string_match(player->client_ip(), target)) {
users_to_kick.push_back(player);
}
}
} else {
for(const auto& player : player_connections_) {
if(utils::wildcard_string_match(player.info().name(), target)) {
for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if(utils::wildcard_string_match(player->info().name(), target)) {
if(banned) {
*out << "\n";
} else {
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);
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) {
*out << "\nKicked " << player_connections_.find(user)->info().name() << " (" << client_address(user) << ").";
async_send_error(user, "You have been banned. Reason: " + reason);
remove_player(user);
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);
disconnect_player(user);
}
}
@ -2674,7 +2656,7 @@ void server::gban_handler(
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);
}
}
@ -2753,27 +2735,27 @@ void server::kick_handler(const std::string& /*issuer_name*/,
// if we find a '.' consider it an ip mask
const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(), '.') >= 1);
std::vector<socket_ptr> users_to_kick;
for(const auto& player : player_connections_) {
if((match_ip && utils::wildcard_string_match(client_address(player.socket()), kick_mask)) ||
(!match_ip && utils::wildcard_string_match(player.info().name(), kick_mask))
std::vector<player_iterator> users_to_kick;
for(player_iterator player = player_connections_.begin(); player != player_connections_.end(); ++player) {
if((match_ip && utils::wildcard_string_match(player->client_ip(), 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) {
*out << "\n";
} else {
kicked = true;
}
*out << "Kicked " << player_connections_.find(socket)->name() << " (" << client_address(socket) << "). '"
*out << "Kicked " << player->name() << " (" << player->client_ip() << "). '"
<< kick_message << "'";
async_send_error(socket, kick_message);
remove_player(socket);
async_send_error(player->socket(), kick_message);
disconnect_player(player);
}
if(!kicked) {
@ -2832,7 +2814,7 @@ void server::searchlog_handler(const std::string& /*issuer_name*/,
found_something = true;
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);
} else {
*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;
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/wesnothd/player_connection.hpp"
#include "utils/optional_fwd.hpp"
#include <boost/asio/steady_timer.hpp>
#include <random>
@ -46,26 +48,28 @@ private:
bool accepting_connections() const { return !graceful_restart; }
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_game(socket_ptr socket, simple_wml::document& data);
void handle_whisper(socket_ptr socket, simple_wml::node& whisper);
void handle_query(socket_ptr socket, simple_wml::node& query);
void handle_nickserv(socket_ptr socket, simple_wml::node& nickserv);
void handle_message(socket_ptr socket, simple_wml::node& message);
void handle_create_game(socket_ptr socket, simple_wml::node& create_game);
void create_game(player_record& host, simple_wml::node& create_game);
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);
void handle_query(player_iterator player, simple_wml::node& query);
void handle_nickserv(player_iterator player, simple_wml::node& nickserv);
void handle_message(player_iterator player, simple_wml::node& message);
void handle_create_game(player_iterator player, simple_wml::node& create_game);
void cleanup_game(game*); // deleter for shared_ptr
void handle_join_game(socket_ptr socket, simple_wml::node& join);
void remove_player(socket_ptr socket);
void handle_join_game(player_iterator player, simple_wml::node& join);
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_to_lobby(simple_wml::document& data, socket_ptr exclude = socket_ptr());
void send_server_message_to_lobby(const std::string& message, socket_ptr exclude = socket_ptr());
void send_server_message_to_all(const std::string& message, socket_ptr exclude = socket_ptr());
void send_server_message(player_iterator player, const std::string& message, const std::string& type) {
send_server_message(player->socket(), message, type);
}
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
{
return player_connections_.find(socket)->get_game() != nullptr;
bool player_is_in_game(player_iterator player) const {
return player->get_game() != nullptr;
}
wesnothd::ban_manager ban_manager_;
@ -190,7 +194,7 @@ private:
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();