diff --git a/src/server/input_stream.cpp b/src/server/input_stream.cpp deleted file mode 100644 index 02ce35a231a..00000000000 --- a/src/server/input_stream.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - Copyright (C) 2003 - 2016 by David White - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#include "global.hpp" - -#include "input_stream.hpp" - -#ifndef _WIN32 - -#include -#include -#include -#include -#include -#include -#include - -#endif - -input_stream::input_stream(const std::string& path) : - fd_(-1), - path_(path), - data_() -{ -#ifndef _WIN32 - if(path == "") { - return; - } - - const int res = mkfifo(path.c_str(),0660); - if(res != 0) { - std::cerr << "could not make fifo at '" << path << "' (" << errno << ")\n"; - } - - fd_ = open(path.c_str(),O_RDONLY|O_NONBLOCK); - - if(fd_ == -1) { - std::cerr << "failed to open fifo at '" << path << "' (" << errno << ")\n"; - } else { - std::cerr << "opened fifo at '" << path << "'. Server commands may be written to this file.\n"; - } -#endif -} - -input_stream::~input_stream() -{ -#ifndef _WIN32 - stop(); -#endif -} - -void input_stream::stop() -{ -#ifndef _WIN32 - if(fd_ != -1) { - close(fd_); - unlink(path_.c_str()); - fd_ = -1; - } -#endif -} - -#ifndef _WIN32 -bool input_stream::read_line(std::string& str) -{ - if(fd_ == -1) { - return false; - } - - const size_t block_size = 4096; - char block[block_size]; - - const size_t nbytes = read(fd_,block,block_size); - if (nbytes == static_cast(-1)) { - return false; - } - std::copy(block,block+nbytes,std::back_inserter(data_)); - - const std::deque::iterator itor = std::find(data_.begin(),data_.end(),'\n'); - if(itor != data_.end()) { - str.resize(itor - data_.begin()); - std::copy(data_.begin(),itor,str.begin()); - data_.erase(data_.begin(),itor+1); - return true; - } else { - return false; - } -} -#else -bool input_stream::read_line(std::string&) -{ - return false; -} -#endif diff --git a/src/server/input_stream.hpp b/src/server/input_stream.hpp deleted file mode 100644 index 9bec9566d67..00000000000 --- a/src/server/input_stream.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2003 - 2016 by David White - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#ifndef INPUT_STREAM_HPP_INCLUDED -#define INPUT_STREAM_HPP_INCLUDED - -#include -#include - -class input_stream -{ -public: - input_stream(const std::string& path); - ~input_stream(); - - bool read_line(std::string& str); - void stop(); - - const std::string& path() const - { - return path_; - } - -private: - input_stream(const input_stream&); - void operator=(const input_stream&); - - int fd_; - std::string path_; - std::deque data_; -}; - -#endif diff --git a/src/server/room.cpp b/src/server/room.cpp deleted file mode 100644 index 4034acd209c..00000000000 --- a/src/server/room.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - Copyright (C) 2009 - 2016 by Tomasz Sniatowski - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#include "config.hpp" -#include "game.hpp" -#include "player_network.hpp" -#include "room.hpp" -#include "log.hpp" -#include "serialization/string_utils.hpp" -#include "util.hpp" - -static lg::log_domain log_server("server"); -#define ERR_ROOM LOG_STREAM(err, log_server) -#define LOG_ROOM LOG_STREAM(info, log_server) -#define DBG_ROOM LOG_STREAM(debug, log_server) - -namespace wesnothd { - -room::room(const std::string& name) - : name_(name) - , members_() - , persistent_(false) - , topic_() - , logged_(false) -{ -} - -room::room(const config& wml) - : name_(wml["name"]) - , members_() - , persistent_(wml["persistent"].to_bool()) - , topic_(wml["topic"]) - , logged_(wml["logged"].to_bool()) -{ -} - -void room::write(config& cfg) const -{ - cfg["name"] = name_; - cfg["persistent"] = persistent_; - cfg["topic"] = topic_; - cfg["logged"] = logged_; -} - -const std::string& room::name() const -{ - return name_; -} - -bool room::persistent() const -{ - return persistent_; -} - -void room::set_persistent(bool v) -{ - persistent_ = v; -} - -const std::string& room::topic() const -{ - return topic_; -} - -void room::set_topic(const std::string& v) -{ - topic_ = v; -} - -bool room::logged() const -{ - return logged_; -} - -void room::set_logged(bool v) -{ - logged_ = v; -} - -bool room::add_player(network::connection player) -{ - if (is_member(player)) { - ERR_ROOM << "ERROR: Player is already in this room. (socket: " - << player << ")\n"; - return false; - } - members_.push_back(player); - return true; -} - -void room::remove_player(network::connection player) -{ - const user_vector::iterator itor = - std::find(members_.begin(), members_.end(), player); - if (itor != members_.end()) { - members_.erase(itor); - } else { - ERR_ROOM << "ERROR: Player is not in this room. (socket: " - << player << ")\n"; - } -} - - -void room::send_data(simple_wml::document& data, - const network::connection exclude, - std::string packet_type) const -{ - wesnothd::send_to_many(data, members(), exclude, packet_type); -} - -void room::send_server_message_to_all(const char* message, network::connection exclude) const -{ - simple_wml::document doc; - send_server_message(message, 0, &doc); - send_data(doc, exclude, "message"); -} - -void room::send_server_message(const char* message, network::connection sock, - simple_wml::document* docptr) const -{ - simple_wml::document docbuf; - if(docptr == nullptr) { - docptr = &docbuf; - } - simple_wml::document& doc = *docptr; - - simple_wml::node& msg = doc.root().add_child("message"); - msg.set_attr("sender", "server"); - msg.set_attr_dup("message", message); - - if(sock) { - send_to_one(doc, sock, "message"); - } -} - -void room::process_message(simple_wml::document& /*data*/, - const player_map::iterator /*user*/) -{ -} - - - -} //end namespace wesnothd diff --git a/src/server/room.hpp b/src/server/room.hpp deleted file mode 100644 index 250dd82cf6c..00000000000 --- a/src/server/room.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - Copyright (C) 2009 - 2016 by Tomasz Sniatowski - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#ifndef SERVER_ROOM_HPP_INCLUDED -#define SERVER_ROOM_HPP_INCLUDED - -#include "network.hpp" -#include "player.hpp" -#include "simple_wml.hpp" - -namespace wesnothd { - -typedef std::vector connection_vector; -typedef std::map player_map; -class game; - -/** - * A room is a group of players that can communicate via messages. - */ -class room { -public: - /** - * Construct a room with just a name and default settings - */ - room(const std::string& name); - - /** - * Construct a room from WML - */ - room(const config& cfg); - - /** - * Write room info to a config - */ - void write(config& cfg) const; - - /** - * The name of this room - */ - const std::string& name() const; - - /** - * Whether this room should be 'persistent', i.e. not deleted when there - * are no players within and stored on disk if needed. - */ - bool persistent() const; - - /** - * Set the persistent flag for this room - */ - void set_persistent(bool v); - - /** - * Whether the room is logged (and might end up in e.g. the lobby bot - */ - bool logged() const; - - /** - * Set the room's logged flag - */ - void set_logged(bool v); - - /** - * This room's topic/motd, sent to all joining players - */ - const std::string& topic() const; - - /** - * Set the topic for this room - */ - void set_topic(const std::string& v); - - /** - * Return the number of players in this room - */ - size_t size() const { - return members_.size(); - } - - /** - * Return true iif the room is empty - */ - bool empty() const { - return members_.empty(); - } - - /** - * Return the members of this room - */ - const std::vector& members() const { - return members_; - } - - /** - * Membership checker. - * @return true iif player is a member of this room - */ - bool is_member(network::connection player) const { - return std::find(members_.begin(), members_.end(), player) != members_.end(); - } - - /** - * Joining the room - * @return true if the player was successfully added - */ - bool add_player(network::connection player); - - /** - * Leaving the room - */ - void remove_player(network::connection player); - - /** - * Chat message processing - */ - void process_message(simple_wml::document& data, const player_map::iterator user); - - /** - * Convenience function for sending a wml document to all (or all except - * one) members. - * @see send_to_many - * @param data the document to send - * @param exclude if nonzero, do not send to this player - * @param packet_type the packet type, if empty the root node name is used - */ - void send_data(simple_wml::document& data, const network::connection exclude=0, std::string packet_type = "") const; - - /** - * Send a text message to all members - * @param message the message text - * @param exclude if nonzero, do not send to this player - */ - void send_server_message_to_all(const char* message, network::connection exclude=0) const; - void send_server_message_to_all(const std::string& message, network::connection exclude=0) const - { - send_server_message_to_all(message.c_str(), exclude); - } - - /** - * Prepare a text message and/or send it to a player. If a nonzero sock - * is passed, the message is sent to this player. If a non-null pointer - * to a simple_wml::document is passed, the message is stored in that - * document. - * @param message the message text - * @param sock the socket to send the message to, if nonzero - * @param docptr the wml document to store the message in, if nonnull - */ - void send_server_message(const char* message, network::connection sock, - simple_wml::document* docptr = nullptr) const; - - void send_server_message(const std::string& message, network::connection sock, - simple_wml::document* docptr = nullptr) const - { - send_server_message(message.c_str(), sock, docptr); - } - - -private: - std::string name_; - connection_vector members_; - bool persistent_; - std::string topic_; - bool logged_; -}; - -} //end namespace wesnothd - -#endif diff --git a/src/server/room_manager.cpp b/src/server/room_manager.cpp deleted file mode 100644 index 3223fe2c4de..00000000000 --- a/src/server/room_manager.cpp +++ /dev/null @@ -1,548 +0,0 @@ -/* - Copyright (C) 2009 - 2016 by Tomasz Sniatowski - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ -#include "game.hpp" -#include "player_network.hpp" -#include "room_manager.hpp" - -#include "serialization/parser.hpp" -#include "serialization/binary_or_text.hpp" -#include "serialization/string_utils.hpp" -#include "util.hpp" -#include "filesystem.hpp" -#include "log.hpp" - -static lg::log_domain log_server_lobby("server/lobby"); -#define ERR_LOBBY LOG_STREAM(err, log_server_lobby) -#define WRN_LOBBY LOG_STREAM(warn, log_server_lobby) -#define LOG_LOBBY LOG_STREAM(info, log_server_lobby) -#define DBG_LOBBY LOG_STREAM(debug, log_server_lobby) - -static lg::log_domain log_server("server"); -#define ERR_SERVER LOG_STREAM(err, log_server) -#define WRN_SERVER LOG_STREAM(warn, log_server) -#define LOG_SERVER LOG_STREAM(info, log_server) -#define DBG_SERVER LOG_STREAM(debug, log_server) - -namespace wesnothd { - -const char* const room_manager::lobby_name_ = "lobby"; - -room_manager::room_manager(player_map &all_players) - : all_players_(all_players) - , lobby_(nullptr) - , rooms_by_name_() - , rooms_by_player_() - , player_stored_rooms_() - , filename_() - , compress_stored_rooms_(true) - , new_room_policy_(PP_EVERYONE) - , dirty_(false) -{ -} - -room_manager::~room_manager() -{ - // this assumes the server is shutting down, so there's no need to - // send the actual room-quit messages to clients - write_rooms(); - for (t_rooms_by_name_::value_type i : rooms_by_name_) { - delete i.second; - } -} - -room_manager::PRIVILEGE_POLICY room_manager::pp_from_string(const std::string& str) -{ - if (str == "everyone") { - return PP_EVERYONE; - } else if (str == "registered") { - return PP_REGISTERED; - } else if (str == "admins") { - return PP_ADMINS; - } else if (str == "nobody") { - return PP_NOBODY; - } - return PP_COUNT; -} - -void room_manager::load_config(const config& cfg) -{ - filename_ = cfg["room_save_file"].str(); - compress_stored_rooms_ = cfg["compress_stored_rooms"].to_bool(true); - PRIVILEGE_POLICY pp = pp_from_string(cfg["new_room_policy"]); - if (pp != PP_COUNT) new_room_policy_ = pp; -} - -void room_manager::read_rooms() -{ - if (!filename_.empty() && filesystem::file_exists(filename_)) { - LOG_LOBBY << "Reading rooms from " << filename_ << "\n"; - config cfg; - filesystem::scoped_istream file = filesystem::istream_file(filename_); - if (compress_stored_rooms_) { - read_gz(cfg, *file); - } else { - read(cfg, *file); - } - - for (const config &c : cfg.child_range("room")) { - room* r(new room(c)); - if (room_exists(r->name())) { - ERR_LOBBY << "Duplicate room ignored in stored rooms: " - << r->name() << "\n"; - delete r; - } else { - rooms_by_name_.insert(std::make_pair(r->name(), r)); - } - } - } - lobby_ = get_room(lobby_name_); - if (lobby_ == nullptr) { - lobby_ = create_room(lobby_name_); - lobby_->set_persistent(true); - lobby_->set_logged(true); - dirty_ = true; - } -} - -void room_manager::write_rooms() -{ - if (filename_.empty()) return; - LOG_LOBBY << "Writing rooms to " << filename_ << "\n"; - config cfg; - for (const t_rooms_by_name_::value_type& v : rooms_by_name_) { - const room& r = *v.second; - if (r.persistent()) { - config& c = cfg.add_child("room"); - r.write(c); - } - } - - filesystem::scoped_ostream file = filesystem::ostream_file(filename_); - config_writer writer(*file, compress_stored_rooms_); - writer.write(cfg); - dirty_ = false; -} - - - -room* room_manager::get_room(const std::string &name) -{ - t_rooms_by_name_::iterator i = rooms_by_name_.find(name); - if (i != rooms_by_name_.end()) { - return i->second; - } else { - return nullptr; - } -} - -bool room_manager::room_exists(const std::string &name) const -{ - return rooms_by_name_.find(name) != rooms_by_name_.end(); -} - -room* room_manager::create_room(const std::string &name) -{ - if (room_exists(name)) { - DBG_LOBBY << "Requested creation of already existing room '" << name << "'\n"; - return nullptr; - } - room* r = new room(name); - rooms_by_name_.insert(std::make_pair(name, r)); - return r; -} - -room* room_manager::get_create_room(const std::string &name, network::connection player) -{ - room* r = get_room(name); - if (r == nullptr) { - bool can_create = false; - switch (new_room_policy_) { - case PP_EVERYONE: - can_create = true; - break; - case PP_REGISTERED: - { - player_map::iterator i = all_players_.find(player); - if (i != all_players_.end()) { - can_create = i->second.registered(); - } - } - break; - case PP_ADMINS: - { - player_map::iterator i = all_players_.find(player); - if (i != all_players_.end()) { - can_create = i->second.is_moderator(); - } - } - break; - default: - break; - } - if (can_create) { //TODO: check if player can create room - //TODO: filter room names for abuse? - r = create_room(name); - } else { - lobby_->send_server_message("The room does not exist", player); - return nullptr; - } - } - return r; -} - -void room_manager::enter_lobby(network::connection player) -{ - lobby_->add_player(player); - unstore_player_rooms(player); -} - -void room_manager::enter_lobby(const wesnothd::game &game) -{ - for (network::connection player : game.all_game_users()) { - enter_lobby(player); - } -} - -void room_manager::exit_lobby(network::connection player) -{ - // No messages are sent to the rooms the player is in because other members - // will receive the "player-entered-game" message (or similar) anyway, and - // will be able to deduce that he or she is no longer in any rooms - lobby_->remove_player(player); - store_player_rooms(player); - t_rooms_by_player_::iterator i = rooms_by_player_.find(player); - if (i != rooms_by_player_.end()) { - for (room* r : i->second) { - r->remove_player(player); - } - } - rooms_by_player_.erase(player); -} - -bool room_manager::in_lobby(network::connection player) const -{ - return lobby_->is_member(player); -} - -void room_manager::remove_player(network::connection player) -{ - // No messages are sent since a player-quit message is sent to everyone - // anyway. - lobby_->remove_player(player); - t_rooms_by_player_::iterator i = rooms_by_player_.find(player); - if (i != rooms_by_player_.end()) { - for (room* r : i->second) { - r->remove_player(player); - } - } - rooms_by_player_.erase(player); - player_stored_rooms_.erase(player); -} - -room* room_manager::require_room(const std::string& room_name, - const player_map::iterator user, - const char *log_string) -{ - room* r = get_room(room_name); - if (r == nullptr) { - lobby_->send_server_message("The room does not exist", user->first); - WRN_LOBBY << "Player " << user->second.name() - << " (conn " << user->first << ")" - << " attempted to " << log_string - << "a nonexistent room '" << room_name << "'\n"; - return nullptr; - } - return r; -} - -room* room_manager::require_member(const std::string& room_name, - const player_map::iterator user, - const char *log_string) -{ - room* r = require_room(room_name, user, log_string); - if (r == nullptr) return nullptr; - if (!r->is_member(user->first)) { - lobby_->send_server_message("You are not a member of this room", user->first); - WRN_LOBBY << "Player " << user->second.name() - << " (conn " << user->first << ")" - << " attempted to " << log_string - << "room '" << room_name << "', but is not a member of that room\n"; - return nullptr; - } - return r; -} - -bool room_manager::player_enters_room(network::connection player, wesnothd::room *room) -{ - if (room->is_member(player)) { - room->send_server_message("You are already in this room", player); - return false; - } - //TODO: implement per-room bans, check ban status here - room->add_player(player); - rooms_by_player_[player].insert(room); - return true; -} - -void room_manager::player_exits_room(network::connection player, wesnothd::room *room) -{ - room->remove_player(player); - rooms_by_player_[player].erase(room); -} - -void room_manager::store_player_rooms(network::connection player) -{ - t_rooms_by_player_::iterator i = rooms_by_player_.find(player); - if (i == rooms_by_player_.end()) { - return; - } - if (i->second.size() < 1) { - return; - } - t_player_stored_rooms_::iterator it = - player_stored_rooms_.insert(std::make_pair(player, std::set())).first; - std::set& store = it->second; - for (room* r : i->second) { - store.insert(r->name()); - } -} - -void room_manager::unstore_player_rooms(network::connection player) -{ - player_map::iterator i = all_players_.find(player); - if (i != all_players_.end()) { - unstore_player_rooms(i); - } -} - -void room_manager::unstore_player_rooms(const player_map::iterator user) -{ - t_player_stored_rooms_::iterator it = player_stored_rooms_.find(user->first); - if (it == player_stored_rooms_.end()) { - return; - } - simple_wml::document doc; - simple_wml::node& join_msg = doc.root().add_child("room_join"); - join_msg.set_attr_dup("player", user->second.name().c_str()); - for (const std::string& room_name : it->second) { - room* r = get_create_room(room_name, user->first); - if (r == nullptr) { - LOG_LOBBY << "Player " << user->second.name() << " unable to rejoin room " << room_name << "\n"; - continue; - } - player_enters_room(user->first, r); - join_msg.set_attr_dup("room", room_name.c_str()); - r->send_data(doc, user->first); - join_msg.remove_child("members", 0); - fill_member_list(r, join_msg); - join_msg.set_attr_dup("topic", r->topic().c_str()); - send_to_one(doc, user->first); - } -} - -void room_manager::process_message(simple_wml::document &data, const player_map::iterator user) -{ - simple_wml::node* const message = data.root().child("message"); - assert (message); - message->set_attr_dup("sender", user->second.name().c_str()); - std::string room_name = message->attr("room").to_string(); - if (room_name.empty()) room_name = lobby_name_; - room* r = require_member(room_name, user, "message"); - if (r == nullptr) { - std::stringstream ss; - ss << "You are not a member of the room '" << room_name << "'. " - << "Your message has not been relayed."; - lobby_->send_server_message(ss.str(), user->first); - return; - } - if (user->second.is_message_flooding()) { - r->send_server_message( - "Warning: you are sending too many messages too fast. " - "Your message has not been relayed.", user->first); - return; - } - const simple_wml::string_span& msg = (*message)["message"]; - chat_message::truncate_message(msg, *message); - if (r->logged()) { - if (msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") { - LOG_SERVER << network::ip_address(user->first) - << "\t<" << user->second.name() - << simple_wml::string_span(msg.begin() + 3, msg.size() - 3) - << ">\n"; - } else { - LOG_SERVER << network::ip_address(user->first) << "\t<" - << user->second.name() << "> " << msg << "\n"; - } - } - r->send_data(data, user->first, "message"); -} - -void room_manager::process_room_join(simple_wml::document &data, const player_map::iterator user) -{ - simple_wml::node* const msg = data.root().child("room_join"); - assert(msg); - std::string room_name = msg->attr("room").to_string(); - room* r = get_create_room(room_name, user->first); - if (r == nullptr) { - return; - } - if (!player_enters_room(user->first, r)) { - return; //player was unable to join room - } - // notify other members - msg->set_attr_dup("player", user->second.name().c_str()); - r->send_data(data, user->first); - // send member list to the new member - fill_member_list(r, *msg); - msg->set_attr_dup("topic", r->topic().c_str()); - send_to_one(data, user->first); -} - -void room_manager::process_room_part(simple_wml::document &data, const player_map::iterator user) -{ - simple_wml::node* const msg = data.root().child("room_part"); - assert(msg); - std::string room_name = msg->attr("room").to_string(); - if (room_name == lobby_name_) { - lobby_->send_server_message("You cannot quit the lobby", user->first); - return; - } - room* r = require_member(room_name, user, "quit"); - if (r == nullptr) return; - player_exits_room(user->first, r); - msg->set_attr_dup("player", user->second.name().c_str()); - r->send_data(data); - if (r->empty() && !r->persistent()) { - LOG_LOBBY << "Last player left room " << room_name << ". Deleting room.\n"; - rooms_by_name_.erase(room_name); - delete r; - } - send_to_one(data, user->first); -} - -void room_manager::process_room_query(simple_wml::document& data, const player_map::iterator user) -{ - simple_wml::node* const msg = data.root().child("room_query"); - assert(msg); - simple_wml::document doc; - simple_wml::node& resp = doc.root().add_child("room_query_response"); - simple_wml::node* q; - q = msg->child("rooms"); - if (q != nullptr) { - fill_room_list(resp); - send_to_one(doc, user->first); - return; - } - std::string room_name = msg->attr("room").to_string(); - if (room_name.empty()) room_name = lobby_name_; - - /* room-specific queries */ - room* r = require_room(room_name, user, "query"); - if (r == nullptr) return; - resp.set_attr_dup("room", room_name.c_str()); - q = msg->child("names"); - if (q != nullptr) { - fill_member_list(r, resp); - send_to_one(doc, user->first); - return; - } - q = msg->child("persist"); - if (q != nullptr) { - if (user->second.is_moderator()) { - WRN_LOBBY << "Attempted room set persistent by non-moderator"; - } else { - if (q->attr("value").empty()) { - if (r->persistent()) { - resp.set_attr("message", "Room is persistent."); - } else { - resp.set_attr("message", "Room is not persistent."); - } - } else if (q->attr("value").to_bool()) { - r->set_persistent(true); - resp.set_attr("message", "Room set as persistent."); - dirty_ = true; - } else { - r->set_persistent(false); - resp.set_attr("message", "Room set as not persistent."); - dirty_ = true; - } - send_to_one(doc, user->first); - } - return; - } - q = msg->child("logged"); - if (q != nullptr) { - if (user->second.is_moderator()) { - WRN_LOBBY << "Attempted room set logged by non-moderator."; - } else { - if (q->attr("value").empty()) { - if (r->persistent()) { - resp.set_attr("message", "Room is logged."); - } else { - resp.set_attr("message", "Room is not logged."); - } - } else if (q->attr("value").to_bool()) { - r->set_logged(true); - resp.set_attr("message", "Room set as logged."); - dirty_ = true; - } else { - r->set_logged(false); - resp.set_attr("message", "Room set as not logged."); - dirty_ = true; - } - send_to_one(doc, user->first); - } - return; - } - q = msg->child("topic"); - if (q != nullptr) { - if (q->attr("value").empty()) { - resp.set_attr_dup("topic", r->topic().c_str()); - send_to_one(doc, user->first); - } else { - if (user->second.is_moderator()) { - WRN_LOBBY << "Attempted room set topic by non-moderator."; - } else { - r->set_topic(q->attr("value").to_string()); - resp.set_attr("message", "Room topic changed."); - send_to_one(doc, user->first); - } - } - } - r->send_server_message("Unknown room query type", user->first); -} - -void room_manager::fill_room_list(simple_wml::node& root) -{ - simple_wml::node& rooms = root.add_child("rooms"); - for (const t_rooms_by_name_::value_type& tr : rooms_by_name_) { - const room& r = *tr.second; - simple_wml::node& room = rooms.add_child("room"); - room.set_attr_dup("name", r.name().c_str()); - room.set_attr_dup("size", lexical_cast_default(r.members().size()).c_str()); - } -} - -void room_manager::fill_member_list(const room* room, simple_wml::node& root) -{ - simple_wml::node& members = root.add_child("members"); - for (network::connection m : room->members()) { - simple_wml::node& member = members.add_child("member"); - player_map::const_iterator mi = all_players_.find(m); - if (mi != all_players_.end()) { - member.set_attr_dup("name", mi->second.name().c_str()); - } - } -} -} //namespace wesnothd diff --git a/src/server/room_manager.hpp b/src/server/room_manager.hpp deleted file mode 100644 index 9020a13c9ae..00000000000 --- a/src/server/room_manager.hpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (C) 2009 - 2016 by Tomasz Sniatowski - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#include "room.hpp" - -#include - -class config; - -#ifndef SERVER_ROOM_MANAGER_HPP_INCLUDED -#define SERVER_ROOM_MANAGER_HPP_INCLUDED - -namespace wesnothd { - -/** - * The room manager manages the lobby and other rooms in the server, and related - * client message processing. - * The lobby represents players that are on the server, but not in any game. - */ -class room_manager : private boost::noncopyable -{ -public: - /** - * Room manager constructor - */ - room_manager(player_map& all_players); - - /** - * Room manager destructor - */ - ~room_manager(); - - enum PRIVILEGE_POLICY { - PP_EVERYONE, - PP_REGISTERED, - PP_ADMINS, - PP_NOBODY, - PP_COUNT - }; - - static PRIVILEGE_POLICY pp_from_string(const std::string& str); - - /** - * Load settings from the main config file - */ - void load_config(const config& cfg); - - /** - * Reads stored rooms from a file on disk, or returns immediately - * if load_config was not called before or the storage filename is empty - */ - void read_rooms(); - - /** - * Writes rooms to the storage file or returns immediately if load_config - * was not called beforethe storage filename is empty - */ - void write_rooms(); - - /** - * Dirty flag for rooms -- true if there were changes that should be written - * to disk - */ - bool dirty() const { return dirty_; } - - /** - * Get a room by name, or nullptr if it does not exist - */ - room* get_room(const std::string& name); - - /** - * @param name the room name to check - * @return true iif the room existst - */ - bool room_exists(const std::string& name) const; - - /** - * Create room named "name" if it does not exist already. - */ - room* create_room(const std::string& name); - - /** - * Get a room by name or create that room if it does not exist and - * creating rooms is allowed. - * @return a valid pointer to a room or nullptr if the room did not exist and - * could not be created. - */ - room* get_create_room(const std::string& name, network::connection player); - - /** - * @return true iif the player is in the lobby - */ - bool in_lobby(network::connection player) const; - - /** - * Player-enters-lobby action. Will autorejoin "stored" rooms (the ones - * the player was before enetering a game, for instance) - */ - void enter_lobby(network::connection player); - - /** - * All players from a game re-enter the lobby - */ - void enter_lobby(const game& game); - - /** - * Player exits lobby. - */ - void exit_lobby(network::connection player); - - /** - * Remove info abut given player from all rooms - */ - void remove_player(network::connection player); - - /** - * Check if the room exists, log failures. - * @return non-nullptr iff the room exists and the player is a member - */ - room* require_room(const std::string& room_name, - const player_map::iterator user, const char* log_string = "use"); - - /** - * Check if the room exists and if the player is a member, log failures. - * @return non-nullptr iff the room exists and the player is a member - */ - room* require_member(const std::string& room_name, - const player_map::iterator user, const char* log_string = "use"); - - /** - * Process a message (chat message) sent to a room. Check conditions - * and resend to other players in the room. - */ - void process_message(simple_wml::document& data, const player_map::iterator user); - - /** - * Process a player's request to join a room - */ - void process_room_join(simple_wml::document& data, const player_map::iterator user); - - /** - * Process a player's request to leave a room - */ - void process_room_part(simple_wml::document& data, const player_map::iterator user); - - /** - * Process a general room query - */ - void process_room_query(simple_wml::document& data, const player_map::iterator user); - - /** - * Lobby convenience accesor - */ - const room& lobby() const { return *lobby_; } - -private: - void do_room_join(network::connection player, const std::string& room_name); - - /** - * Adds a player to a room, maintaining internal consistency - * Will send appropriate error messages to the player. - * @return true iif the operation was successful, false otherwise - */ - bool player_enters_room(network::connection player, room* room); - - /** - * Removes a player from a room, maintaining internal consistency - */ - void player_exits_room(network::connection player, room* room); - - /** - * Stores the room names (other than lobby) of the given player for future - * use (rejoin) - */ - void store_player_rooms(network::connection player); - - /** - * Unstores (rejoins) player's rooms that were previously stored. - * No action if not stored earlier or no rooms. - */ - void unstore_player_rooms(const player_map::iterator user); - - /** - * Helper function that calls the player_map::iterator version - * of unstore_player_rooms - */ - void unstore_player_rooms(network::connection player); - - /** - * Fill a wml node (message) with members of a room - */ - void fill_member_list(const room* room, simple_wml::node& root); - - /** - * Fill a wml node (message) with a room list - */ - void fill_room_list(simple_wml::node& root); - - /** Reference to the all players map */ - player_map& all_players_; - - /** The lobby-room, treated separetely */ - room* lobby_; - - /** Rooms by name */ - typedef std::map t_rooms_by_name_; - t_rooms_by_name_ rooms_by_name_; - - /** Rooms by player */ - typedef std::map > t_rooms_by_player_; - t_rooms_by_player_ rooms_by_player_; - - /** Room names stored for players that have entered a game */ - typedef std::map > t_player_stored_rooms_; - t_player_stored_rooms_ player_stored_rooms_; - - /** - * Persistent room storage filename. If empty, rooms are not stored on disk. - */ - std::string filename_; - - /** - * Flag controlling whether to compress the stored rooms or not - */ - bool compress_stored_rooms_; - - /** - * Policy regarding who can create new rooms - */ - PRIVILEGE_POLICY new_room_policy_; - - /** - * 'Dirty' flag with regards to room info that will be stored on disk - */ - bool dirty_; - - /** - * The main (lobby) room name - */ - static const char* const lobby_name_; -}; - -} //namespace wesnothd - - -#endif diff --git a/src/tests/test_network_worker.cpp b/src/tests/test_network_worker.cpp deleted file mode 100644 index 74682fcba00..00000000000 --- a/src/tests/test_network_worker.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - Copyright (C) 2008 - 2016 by Pauli Nieminen - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#define GETTEXT_DOMAIN "wesnoth-test" - -#include - - -#include "utils/auto_parameterized.hpp" -#include "utils/predicate.hpp" - -#include "network_worker.hpp" -#include "thread.hpp" -#include "filesystem.hpp" - -#include "config.hpp" -#include "game_config.hpp" - -/** - * Test networking to prevent bugs there. - * Try to create all kind of unlikely error conditions - * Some test should lock management_mutex from worker to settup stress test - * It is like that this will need some threading also :( - */ - -BOOST_AUTO_TEST_SUITE( test_network ) - -const int TEST_PORT = 15010; -const int MIN_THREADS = 1; -const int MAX_THREADS = 5; -const std::string LOCALHOST = "localhost"; - - -network::manager* wes_manager; -network::server_manager* wes_server; - -network::connection client_client1; -network::connection client_client2; - -network::connection server_client1; -network::connection server_client2; - - -BOOST_AUTO_TEST_CASE( test_connect ) -{ - int connections = network::nconnections(); - - BOOST_WARN_MESSAGE(connections == 0, "There is open "<< connections <<" connections before test!"); - BOOST_CHECK_MESSAGE(wes_manager = new network::manager(MIN_THREADS,MAX_THREADS), "network::manager failed to initialize"); - - BOOST_CHECK_MESSAGE(wes_server = new network::server_manager(TEST_PORT,network::server_manager::MUST_CREATE_SERVER), - "network::server_manager failed to initialize"); - BOOST_REQUIRE_MESSAGE(wes_server->is_running(), "Can't start server!"); - - client_client1 = network::connect(LOCALHOST, TEST_PORT); - - BOOST_CHECK_MESSAGE(client_client1 > 0, "Can't connect to server"); - - server_client1 = network::accept_connection(); - - BOOST_CHECK_MESSAGE(server_client1 > 0, "Can't accept connection"); - - -} - -template -static network::connection receive(T& cfg, int max_tries = 100) -{ - network::connection receive_con; - while ((receive_con = network::receive_data(cfg)) == network::null_connection) - { - // loop untill data is received - SDL_Delay(50); - if (--max_tries <= 0) - { - BOOST_WARN_MESSAGE(max_tries > 0,"receiving data took too long. Preventing for ever loop"); - break; - } - } - return receive_con; -} - -BOOST_AUTO_TEST_CASE( test_send_client ) -{ - config cfg_send; - config& child = cfg_send.add_child("test_client_send"); - - child["test"] = "yes!"; - cfg_send["test_running"] = true; - network::send_data(cfg_send, client_client1); - - network::connection receive_from; - config received; - - receive_from = receive(received); - - BOOST_CHECK_MESSAGE( receive_from == server_client1, "Received data is not from test client 1" ); - - BOOST_CHECK_EQUAL(cfg_send, received); - -} - -static void try_send_random_seed ( const std::string seed_str, const unsigned int random_calls) -{ - config cfg_send; - config& child = cfg_send.add_child("command"); - - child["random_seed"] = seed_str; - child["random_calls"] = random_calls; - - network::send_data(cfg_send, client_client1); - - network::connection receive_from; - config received; - - receive_from = receive(received); - - BOOST_CHECK_MESSAGE( receive_from == server_client1, "Received data is not from test client 1" ); - - BOOST_CHECK_EQUAL(cfg_send, received); - - config rec_command = received.child("command"); - - std::string rec_seed_str = rec_command["random_seed"].str(); - unsigned int rec_calls = rec_command["random_calls"]; - - BOOST_CHECK_EQUAL(seed_str, rec_seed_str); - BOOST_CHECK_EQUAL(random_calls, rec_calls); -} - -BOOST_AUTO_TEST_CASE( test_send_random_seed ) -{ - try_send_random_seed("0000badd",0); - try_send_random_seed("00001234",1); - try_send_random_seed("deadbeef",2); - try_send_random_seed("12345678",3); - try_send_random_seed("00009999",4); - try_send_random_seed("ffffaaaa",5); - try_send_random_seed("11110000",6); - try_send_random_seed("10101010",7); - try_send_random_seed("aaaa0000",8); -} - -class connect_aborter : public threading::waiter -{ -public: - connect_aborter() : start_(SDL_GetTicks()) - {} - ACTION process(); - -private: - size_t start_; -}; - -connect_aborter::ACTION connect_aborter::process() -{ - // Abort connection after 5 ms - if(SDL_GetTicks() - start_ >= 5) { - return ABORT; - } else { - return WAIT; - } -} - -#if 0 -BOOST_AUTO_TEST_CASE( test_sdl_thread_wait_crash ) -{ - - delete wes_server; - wes_server = 0; - delete wes_manager; - wes_manager = 0; - BOOST_CHECK_MESSAGE(wes_manager == 0, "network::manager nono zero after delete"); - - BOOST_CHECK_MESSAGE(wes_manager = new network::manager(MIN_THREADS,MAX_THREADS), "network::manager failed to initialize"); - BOOST_CHECK_THROW(client_client1 = network::connect(LOCALHOST, TEST_PORT), network::error); - BOOST_CHECK_MESSAGE(client_client1 > 0, "Can't connect to server"); - delete wes_manager; - wes_manager = 0; - BOOST_CHECK_MESSAGE(wes_manager = new network::manager(MIN_THREADS,MAX_THREADS), "network::manager failed to initialize"); - - connect_aborter aborter; - BOOST_CHECK_MESSAGE((client_client1 = network::connect("server.wesnoth.org", 15000, aborter)) == 0, "Connection create but not shoul"); - delete wes_manager; - - BOOST_CHECK_MESSAGE(wes_manager = new network::manager(MIN_THREADS,MAX_THREADS), "network::manager failed to initialize"); - BOOST_CHECK_MESSAGE(wes_server = new network::server_manager(TEST_PORT,network::server_manager::MUST_CREATE_SERVER), ""); - BOOST_CHECK_MESSAGE((client_client1 = network::connect(LOCALHOST, TEST_PORT, aborter)) == 0, "Connection create but not shoul"); - delete wes_manager; - - wes_manager = new network::manager(MIN_THREADS,MAX_THREADS); - client_client1 = network::connect(LOCALHOST, TEST_PORT); - BOOST_CHECK_MESSAGE(client_client1 > 0, "Can't connect to server"); - delete wes_server; - delete wes_manager; - wes_manager = new network::manager(MIN_THREADS,MAX_THREADS); - wes_server = new network::server_manager(TEST_PORT,network::server_manager::MUST_CREATE_SERVER); -} -#endif - -// Use 1kb, 500kb and 10Mb files for testing -struct sendfile_param { - sendfile_param(size_t size, bool system) : size_(size), system_(system) {} - size_t size_; - bool system_; -}; - -sendfile_param sendfile_sizes[] = {sendfile_param(1*1024,true), - sendfile_param(5*1024*1024,true), - sendfile_param(1*1024,false), - sendfile_param(5*1024*1024,false)}; - -static std::string create_random_sendfile(size_t size) -{ - char buffer[1024]; - const int buffer_size = sizeof(buffer)/sizeof(buffer[0]); - int *begin = reinterpret_cast(&buffer[0]); - int *end = begin + sizeof(buffer)/sizeof(int); - std::string filename = "sendfile.tmp"; - filesystem::scoped_ostream file = filesystem::ostream_file(filename); - std::generate(begin,end,std::rand); - while( size > 0 - && !file->bad()) - { - file->write(buffer, buffer_size); - size -= buffer_size; - } - return filename; -} - -static void delete_random_sendfile(const std::string& file) -{ - filesystem::delete_file(file); -} - -template -class auto_resetter { - T& value_to_change_; - T old_val_; - public: - auto_resetter(const T& new_value, T& value_to_change) : value_to_change_(value_to_change), old_val_(value_to_change) - { - value_to_change_ = new_value; - } - ~auto_resetter() - { - value_to_change_ = old_val_; - } -}; - -WESNOTH_PARAMETERIZED_TEST_CASE( test_multi_sendfile, sendfile_param, sendfile_sizes, size ) -{ - auto_resetter path("", game_config::path); - network::set_raw_data_only(); - std::string file = create_random_sendfile(size.size_); - network_worker_pool::set_use_system_sendfile(size.system_); - - network::connection cl_client1, se_client1; - network::connection cl_client2, se_client2; - network::connection cl_client3, se_client3; - - BOOST_CHECK_MESSAGE((cl_client1 = network::connect(LOCALHOST, TEST_PORT)) > 0, "Can't connect to server!"); - BOOST_CHECK_MESSAGE((se_client1 = network::accept_connection()) > 0, "Coulnd't accept new connection"); - BOOST_CHECK_MESSAGE((cl_client2 = network::connect(LOCALHOST, TEST_PORT)) > 0, "Can't connect to server!"); - BOOST_CHECK_MESSAGE((se_client2 = network::accept_connection()) > 0, "Coulnd't accept new connection"); - BOOST_CHECK_MESSAGE((cl_client3 = network::connect(LOCALHOST, TEST_PORT)) > 0, "Can't connect to server!"); - BOOST_CHECK_MESSAGE((se_client3 = network::accept_connection()) > 0, "Coulnd't accept new connection"); - - network::send_file(file, cl_client1); - network::send_file(file, cl_client2); - network::send_file(file, cl_client3); - - std::vector data; - - BOOST_CHECK_PREDICATE(test_utils::one_of , (receive(data,500))(3)(se_client1)(se_client2)(se_client3)); - BOOST_CHECK_EQUAL(data.size(), static_cast(filesystem::file_size(file))); - BOOST_CHECK_PREDICATE(test_utils::one_of , (receive(data,500))(3)(se_client1)(se_client2)(se_client3)); - BOOST_CHECK_EQUAL(data.size(), static_cast(filesystem::file_size(file))); - BOOST_CHECK_PREDICATE(test_utils::one_of , (receive(data,500))(3)(se_client1)(se_client2)(se_client3)); - - BOOST_CHECK_EQUAL(data.size(), static_cast(filesystem::file_size(file))); - - network::disconnect(cl_client1); - network::disconnect(cl_client2); - network::disconnect(cl_client3); - - BOOST_CHECK_THROW(receive(data),network::error); - BOOST_CHECK_THROW(receive(data),network::error); - BOOST_CHECK_THROW(receive(data),network::error); - - delete_random_sendfile(file); -} - -#if 0 -BOOST_AUTO_TEST_CASE( test_multiple_connections ) -{ -} - -BOOST_AUTO_TEST_CASE( test_cancel_transfer ) -{ -} - -BOOST_AUTO_TEST_CASE( test_detect_errors ) -{ -} - -BOOST_AUTO_TEST_CASE( test_broken_data ) -{ -} - -BOOST_AUTO_TEST_CASE( test_config_with_macros ) -{ -} - -BOOST_AUTO_TEST_CASE( test_disconnect ) -{ - network::disconnect(client_client1); - network::disconnect(server_client1); -} -#endif -BOOST_AUTO_TEST_CASE( test_shutdown ) -{ - delete wes_server; - BOOST_CHECK_MESSAGE(true,"Not true"); - delete wes_manager; -} - -BOOST_AUTO_TEST_SUITE_END() -/* vim: set ts=4 sw=4: */ diff --git a/src/tests/utils/predicate.hpp b/src/tests/utils/predicate.hpp deleted file mode 100644 index fecfca9acc1..00000000000 --- a/src/tests/utils/predicate.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (C) 2008 - 2016 by Pauli Nieminen - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#ifndef TESTS_UTILS_FAKE_DISPLAY_HPP_INCLUDED -#define TESTS_UTILS_FAKE_DISPLAY_HPP_INCLUDED - -#include - -namespace test_utils { - - /** - * Used to check if first parameter matches one of given values - * used with BOOST_CHECK_PREDICATE - **/ - template - bool one_of(const T& val, int va_number, ...) - { - T param; - va_list vl; - va_start(vl, va_number); - - bool ret = false; - - for (int i = 0; i < va_number; ++i) - { - param = va_arg(vl, T); - if (param == val) - { - ret = true; - break; - } - } - va_end(vl); - return ret; - } - - -} -#endif diff --git a/src/thread.cpp b/src/thread.cpp deleted file mode 100644 index 491071ed9e0..00000000000 --- a/src/thread.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - Copyright (C) 2003 - 2016 by David White - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#include "global.hpp" - -#include - -#include "log.hpp" -#include "thread.hpp" - -#include -#include - -#define ERR_G LOG_STREAM(err, lg::general()) - -uint32_t threading::thread::get_id() { return SDL_GetThreadID(thread_); } - -uint32_t threading::get_current_thread_id() { return SDL_ThreadID(); } - -static int run_async_operation(void* data) -{ - threading::async_operation_ptr op(*reinterpret_cast(data)); - op->run(); - - const threading::lock l(op->get_mutex()); - op->notify_finished(); //in case the operation didn't notify of finishing - - return 0; -} - -namespace { - -std::vector detached_threads; - -} - -namespace threading { - -manager::~manager() -{ - for(std::vector::iterator i = detached_threads.begin(); i != detached_threads.end(); ++i) { - SDL_WaitThread(*i,nullptr); - } -} - -thread::thread(int (*f)(void*), void* data) - : thread_(SDL_CreateThread(f, "", data)) -{ -} - -thread::~thread() -{ - join(); -} - -void thread::join() -{ - if(thread_ != nullptr) { - SDL_WaitThread(thread_,nullptr); - thread_ = nullptr; - } -} - -void thread::detach() -{ - detached_threads.push_back(thread_); - thread_ = nullptr; -} - -mutex::mutex() : m_(SDL_CreateMutex()) -{} - -mutex::~mutex() -{ - SDL_DestroyMutex(m_); -} - -lock::lock(mutex& m) : m_(m) -{ - SDL_mutexP(m_.m_); -} - -lock::~lock() -{ - SDL_mutexV(m_.m_); -} - -condition::condition() : cond_(SDL_CreateCond()) -{} - -condition::~condition() -{ - SDL_DestroyCond(cond_); -} - -bool condition::wait(const mutex& m) -{ - return SDL_CondWait(cond_,m.m_) == 0; -} - -condition::WAIT_TIMEOUT_RESULT condition::wait_timeout(const mutex& m, unsigned int timeout) -{ - const int res = SDL_CondWaitTimeout(cond_,m.m_,timeout); - switch(res) { - case 0: return WAIT_OK; - case SDL_MUTEX_TIMEDOUT: return WAIT_TIMED_OUT; - default: - ERR_G << "SDL_CondWaitTimeout: " << SDL_GetError() << std::endl; - return WAIT_ERROR; - } -} - -bool condition::notify_one() -{ - if(SDL_CondSignal(cond_) < 0) { - ERR_G << "SDL_CondSignal: " << SDL_GetError() << std::endl; - return false; - } - - return true; -} - -bool condition::notify_all() -{ - if(SDL_CondBroadcast(cond_) < 0) { - ERR_G << "SDL_CondBroadcast: " << SDL_GetError() << std::endl; - return false; - } - return true; -} - -bool async_operation::notify_finished() -{ - finishedVar_ = true; - return finished_.notify_one(); -} -active_operation_list async_operation::active_; - -async_operation::RESULT async_operation::execute(async_operation_ptr this_ptr, waiter& wait) -{ - //the thread must be created after the lock, and also destroyed after it. - //this is because during the thread's execution, we must always hold the mutex - //unless we are waiting on notification that the thread is finished, or we have - //already received that notification. - // - //we cannot hold the mutex while waiting for the thread to join though, because - //the thread needs access to the mutex before it terminates - { - const lock l(get_mutex()); - active_.push_back(this_ptr); - thread_.reset(new thread(run_async_operation,&this_ptr)); - - bool completed = false; - while(wait.process() == waiter::WAIT) { - const condition::WAIT_TIMEOUT_RESULT res = finished_.wait_timeout(get_mutex(),20); - if(res == condition::WAIT_OK || finishedVar_) { - completed = true; - break; - } else if(res == condition::WAIT_ERROR) { - break; - } - } - - if(!completed) { - aborted_ = true; - return ABORTED; - } - } - - return COMPLETED; -} - - -} diff --git a/src/thread.hpp b/src/thread.hpp deleted file mode 100644 index daa3a9749e3..00000000000 --- a/src/thread.hpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - Copyright (C) 2003 - 2016 by David White - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -#ifndef THREAD_HPP_INCLUDED -#define THREAD_HPP_INCLUDED - -#include - -#include -#include - -struct SDL_Thread; -struct SDL_mutex; -struct SDL_cond; - -// Threading primitives wrapper for SDL_Thread. -// -// This module defines primitives for wrapping C++ around SDL's threading -// interface -namespace threading -{ - -struct manager -{ - ~manager(); -}; - -// Threading object. -// -// This class defines threading objects. One such object represents a -// thread and admits killing and joining on threads. Intended to be -// used for manipulating threads instead of poking around with SDL_Thread -// calls. -class thread - : private boost::noncopyable -{ -public: - // Construct a new thread to start executing the function - // pointed to by f. The void* data will be passed to f, to - // facilitate passing of parameters to f. - // - // \param f the function at which the thread should start executing - // \param data passed to f - // - // \pre f != nullptr - explicit thread(int (*f)(void*), void* data=nullptr); - - // Destroy the thread object. This is done by waiting on the - // thread with the join() operation, thus blocking until the - // thread object has finished its operation. - ~thread(); - - // Join (wait) on the thread to finish. When the thread finishes, - // the function will return. calling wait() on an already killed - // thread is a no-op. - void join(); - - void detach(); - - uint32_t get_id(); -private: - - SDL_Thread* thread_; -}; - -uint32_t get_current_thread_id(); -// Binary mutexes. -// -// Implements an interface to binary mutexes. This class only defines the -// mutex itself. Locking is handled through the friend class lock, -// and monitor interfacing through condition variables is handled through -// the friend class condition. -class mutex - : private boost::noncopyable -{ -public: - mutex(); - ~mutex(); - - friend class lock; - friend class condition; - -private: - - SDL_mutex* const m_; -}; - -// Binary mutex locking. -// -// Implements a locking object for mutexes. The creation of a lock -// object on a mutex will lock the mutex as long as this object is -// not deleted. -class lock - : private boost::noncopyable -{ -public: - // Create a lock object on the mutex given as a parameter to - // the constructor. The lock will be held for the duration - // of the object existence. - // If the mutex is already locked, the constructor will - // block until the mutex lock can be acquired. - // - // \param m the mutex on which we should try to lock. - explicit lock(mutex& m); - // Delete the lock object, thus releasing the lock acquired - // on the mutex which the lock object was created with. - ~lock(); -private: - - mutex& m_; -}; - -// Condition variable locking. -// -// Implements condition variables for mutexes. A condition variable -// allows you to free up a lock inside a critical section -// of the code and regain it later. Condition classes only make -// sense to do operations on, if one already acquired a mutex. -class condition - : private boost::noncopyable -{ -public: - condition(); - ~condition(); - - // Wait on the condition. When the condition is met, you - // have a lock on the mutex and can do work in the critical - // section. When the condition is not met, wait blocks until - // the condition is met and atomically frees up the lock on - // the mutex. One will automatically regain the lock when the - // thread unblocks. - // - // If wait returns false we have an error. In this case one cannot - // assume that he has a lock on the mutex anymore. - // - // \param m the mutex you wish to free the lock for - // \returns true: the wait was successful, false: an error occurred - // - // \pre You have already acquired a lock on mutex m - // - bool wait(const mutex& m); - - enum WAIT_TIMEOUT_RESULT { WAIT_OK, WAIT_TIMED_OUT, WAIT_ERROR }; - - // wait on the condition with a timeout. Basically the same as the - // wait() function, but if the lock is not acquired before the - // timeout, the function returns with an error. - // - // \param m the mutex you wish free the lock for. - // \param timeout the allowed timeout in milliseconds (ms) - // \returns result based on whether condition was met, it timed out, - // or there was an error - WAIT_TIMEOUT_RESULT wait_timeout(const mutex& m, unsigned int timeout); - // signal the condition and wake up one thread waiting on the - // condition. If no thread is waiting, notify_one() is a no-op. - // Does not unlock the mutex. - bool notify_one(); - - // signal all threads waiting on the condition and let them contend - // for the lock. This is often used when varying resource amounts are - // involved and you do not know how many processes might continue. - // The function should be used with care, especially if many threads are - // waiting on the condition variable. - bool notify_all(); - -private: - - SDL_cond* const cond_; -}; - -//class which defines an interface for waiting on an asynchronous operation -class waiter { -public: - enum ACTION { WAIT, ABORT }; - - virtual ~waiter() {} - virtual ACTION process() = 0; -}; - -class async_operation; - -typedef std::shared_ptr async_operation_ptr; - -typedef std::list active_operation_list; - -//class which defines an asynchronous operation. Objects of this class are accessed from -//both the worker thread and the calling thread, and so it has 'strange' allocation semantics. -//It is allocated by the caller, and generally deleted by the caller. However, in some cases -//the asynchronous operation is aborted, and the caller abandons it. The caller cannot still -//delete the operation, since the worker thread might still access it, so in the case when the -//operation is aborted, the worker thread will delete it. -// -//The caller should hold these objects using the async_operation_holder class below, which will -//handle the delete semantics -class async_operation -{ -public: - - enum RESULT { COMPLETED, ABORTED }; - - async_operation() : - thread_(), aborted_(false), finished_(), finishedVar_(false), mutex_() - { - while (!active_.empty() && active_.front().unique()) - active_.pop_front(); - } - virtual ~async_operation() {} - - RESULT execute(async_operation_ptr this_ptr, waiter& wait); - - mutex& get_mutex() { return mutex_; } - - virtual void run() = 0; - - //notify that the operation is finished. Can be called from within the thread - //while holding the mutex and after checking is_aborted() - //if we want to be sure that if the operation is completed, the caller is notified. - //will be called in any case after the operation returns - bool notify_finished(); - - //must hold the mutex before calling this function from the worker thread - bool is_aborted() const { return aborted_; } - -private: - std::unique_ptr thread_; - bool aborted_; - condition finished_; - bool finishedVar_; - mutex mutex_; - - static active_operation_list active_; -}; - -} - -#endif