Create a room class separate from the game class, and use it for the lobby.

There remains some unfortunate duplicate code that will be removed in
the future, not all "lobby-hacks" have yet been removed from the game
class either.
This commit is contained in:
Tomasz Śniatowski 2009-05-19 10:01:28 +01:00
parent 24536f3c8b
commit 613189f9f0
12 changed files with 312 additions and 49 deletions

View file

@ -393,6 +393,10 @@
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\src\server\room.cpp"
>
</File>
<File
RelativePath="..\..\src\server\sample_user_handler.cpp"
>
@ -570,6 +574,10 @@
RelativePath="..\..\src\server\proxy.hpp"
>
</File>
<File
RelativePath="..\..\src\server\room.hpp"
>
</File>
<File
RelativePath="..\..\src\server\sample_user_handler.hpp"
>

View file

@ -404,6 +404,7 @@ SET(wesnothd_SRC
server/player.cpp
server/player_network.cpp
server/proxy.cpp
server/room.cpp
server/server.cpp
server/simple_wml.cpp
server/user_handler.cpp

View file

@ -234,6 +234,7 @@ wesnothd_SOURCES = \
server/player.cpp \
server/player_network.cpp \
server/proxy.cpp \
server/room.cpp
server/server.cpp \
server/simple_wml.cpp \
server/user_handler.cpp \

View file

@ -348,6 +348,7 @@ wesnothd_sources = Split("""
server/player.cpp
server/player_network.cpp
server/proxy.cpp
server/room.cpp
server/sample_user_handler.cpp
server/simple_wml.cpp
server/user_handler.cpp

View file

@ -35,23 +35,6 @@ static lg::log_domain log_server("server");
#define LOG_GAME LOG_STREAM(info, log_server)
#define DBG_GAME LOG_STREAM(debug, log_server)
namespace chat_message {
const size_t max_message_length = 256;
static void truncate_message(const simple_wml::string_span& str, simple_wml::node& message) {
// testing for msg.size() is not sufficient but we're not getting false negatives
// and it's cheaper than always converting to wstring.
if(str.size() > static_cast<int>(chat_message::max_message_length)) {
std::string tmp(str.begin(), str.end());
// The string can contain utf-8 characters so truncate as wide_string otherwise
// a corrupted utf-8 string can be returned.
utils::truncate_as_wstring(tmp, max_message_length);
message.set_attr_dup("message", tmp.c_str());
}
}
} // end chat_message namespace
namespace wesnothd {
int game::id_num = 1;
@ -754,15 +737,8 @@ void game::unban_user(const simple_wml::node& unban,
}
void game::process_message(simple_wml::document& data, const player_map::iterator user) {
// Hack to handle the pseudo game lobby_.
if (owner_ != 0) {
} else if (user->second.silenced()) {
return;
} else if (user->second.is_message_flooding()) {
send_server_message(
"Warning: you are sending too many messages too fast. "
"Your message has not been relayed.", user->first);
return;
if (owner_ == 0) {
ERR_GAME << "No owner in game::process_message\n";
}
simple_wml::node* const message = data.root().child("message");
@ -772,19 +748,7 @@ void game::process_message(simple_wml::document& data, const player_map::iterato
const simple_wml::string_span& msg = (*message)["message"];
chat_message::truncate_message(msg, *message);
// Only log in the lobby_.
std::string game_prefix;
if (owner_ != 0) {
game_prefix = "game ";
} else if (msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") {
LOG_GAME << network::ip_address(user->first) << "\t<"
<< user->second.name() << simple_wml::string_span(msg.begin() + 3, msg.size() - 3) << ">\n";
} else {
LOG_GAME << network::ip_address(user->first) << "\t<"
<< user->second.name() << "> " << msg << "\n";
}
send_data(data, user->first, game_prefix + "message");
send_data(data, user->first, "game message");
}
bool game::is_legal_command(const simple_wml::node& command, bool is_player) {

View file

@ -0,0 +1,77 @@
/* $Id$ */
/*
Copyright (C) 2003 - 2009 by David White <dave@whitevine.net>
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 version 2
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_PLAYER_GROUP_HPP_INCLUDED
#define SERVER_PLAYER_GROUP_HPP_INCLUDED
#include "../network.hpp"
#include "player.hpp"
#include "simple_wml.hpp"
namespace wesnothd {
typedef std::map<network::connection,player> player_map;
typedef std::vector<network::connection> connection_vector;
/**
* A player_group represents a group of players, with convenience functions for
* sending data to all of them
*/
class player_group
{
public:
player_group();
explicit player_group(const connection_vector& v);
/**
* Check if the player is a member of this group
* @return true iif the playes is a member
*/
bool is_member(const network::connection player) const;
/**
* Add a user to the group.
* @return True iff the user successfully joined.
*/
bool add_member(const network::connection player);
const connection_vector members() const { return members_; }
/** Send data to all members except 'exclude' */
void send_and_record_server_message(const char* message, const network::connection exclude = 0);
void send_data(simple_wml::document& data, const network::connection exclude=0, std::string packet_type = "") const;
void send_to_one(simple_wml::document& data, const network::connection sock, std::string packet_type = "") const;
protected:
connection_vector members_;
};
class room
{
public:
room(const std::string& name);
private:
std::string name_;
connection_vector members_;
};
} //end namespace wesnothd
#endif

View file

@ -2,6 +2,25 @@
namespace wesnothd {
namespace chat_message {
const size_t max_message_length = 256;
void truncate_message(const simple_wml::string_span& str, simple_wml::node& message)
{
// testing for msg.size() is not sufficient but we're not getting false negatives
// and it's cheaper than always converting to wstring.
if(str.size() > static_cast<int>(chat_message::max_message_length)) {
std::string tmp(str.begin(), str.end());
// The string can contain utf-8 characters so truncate as wide_string otherwise
// a corrupted utf-8 string can be returned.
utils::truncate_as_wstring(tmp, max_message_length);
message.set_attr_dup("message", tmp.c_str());
}
}
} // end chat_message namespace
player_map::const_iterator find_user(const player_map& all_players,
const simple_wml::string_span& name)
{

View file

@ -23,6 +23,11 @@
namespace wesnothd {
namespace chat_message {
void truncate_message(const simple_wml::string_span& str,
simple_wml::node& message);
} // end chat_message namespace
typedef std::map<network::connection,player> player_map;
typedef std::vector<network::connection> connection_vector;

116
src/server/room.cpp Normal file
View file

@ -0,0 +1,116 @@
#include "game.hpp"
#include "player_network.hpp"
#include "room.hpp"
#include "../foreach.hpp"
#include "../log.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()
: members_()
{
}
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::add_players(const wesnothd::game& game)
{
foreach (network::connection player, game.all_game_users()) {
add_player(player);
}
}
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 == NULL) {
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)
{
if (user->second.silenced()) {
return;
} else if (user->second.is_message_flooding()) {
send_server_message(
"Warning: you are sending too many messages too fast. "
"Your message has not been relayed.", user->first);
return;
}
simple_wml::node* const message = data.root().child("message");
assert(message);
message->set_attr_dup("sender", user->second.name().c_str());
const simple_wml::string_span& msg = (*message)["message"];
chat_message::truncate_message(msg, *message);
if (msg.size() >= 3 && simple_wml::string_span(msg.begin(), 4) == "/me ") {
LOG_ROOM << network::ip_address(user->first)
<< "\t<" << user->second.name()
<< simple_wml::string_span(msg.begin() + 3, msg.size() - 3)
<< ">\n";
} else {
LOG_ROOM << network::ip_address(user->first) << "\t<"
<< user->second.name() << "> " << msg << "\n";
}
send_data(data, user->first, "message");
}
} //end namespace wesnothd

70
src/server/room.hpp Normal file
View file

@ -0,0 +1,70 @@
/* $Id$ */
/*
Copyright (C) 2003 - 2009 by David White <dave@whitevine.net>
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 version 2
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<network::connection> connection_vector;
class game;
class room {
public:
room();
/**
* Return the number of players in this room
*/
size_t size() const {
return members_.size();
}
/**
* Return the members of this room
*/
const std::vector<network::connection>& members() const {
return members_;
}
bool is_member(network::connection player) const {
return std::find(members_.begin(), members_.end(), player) != members_.end();
}
bool add_player(network::connection player);
void add_players(const game& game);
void remove_player(network::connection player);
void process_message(simple_wml::document& data, const player_map::iterator user);
void send_data(simple_wml::document& data, const network::connection exclude=0, std::string packet_type = "") const;
void send_server_message_to_all(const char* message, network::connection exclude=0) const;
void send_server_message(const char* message, network::connection sock, simple_wml::document* docptr = NULL) const;
private:
connection_vector members_;
};
} //end namespace wesnothd
#endif

View file

@ -297,7 +297,7 @@ server::server(int port, const std::string& config_file, size_t min_threads,
ghost_players_(),
games_(),
not_logged_in_(players_),
lobby_(players_),
lobby_(),
input_(),
config_file_(config_file),
cfg_(read_config()),
@ -530,7 +530,7 @@ void server::dump_stats(const time_t& now) {
<< "\tnumber_of_games = " << games_.size()
<< "\tnumber_of_users = " << players_.size()
<< "\tnumber_of_ghost_users = " << ghost_players_.size()
<< "\tlobby_users = " << lobby_.nobservers() << "\n";
<< "\tlobby_users = " << lobby_.size() << "\n";
}
void server::clean_user_handler(const time_t& now) {
@ -838,7 +838,7 @@ void server::process_data(const network::connection sock,
process_nickserv(sock, *nickserv);
} else if (simple_wml::node* whisper = root.child("whisper")) {
process_whisper(sock, *whisper);
} else if (lobby_.is_observer(sock)) {
} else if (lobby_.is_member(sock)) {
process_data_lobby(sock, data);
} else {
process_data_game(sock, data);
@ -1070,7 +1070,7 @@ void server::process_login(const network::connection sock,
ghost_players_.insert(sock) ;
not_logged_in_.remove_player(sock);
lobby_.add_player(sock, true);
lobby_.add_player(sock);
// Send the new player the entire list of games and players
send_doc(games_and_users_list_, sock);
@ -1250,7 +1250,7 @@ std::string server::process_command(const std::string& query, const std::string&
out << "Number of games = " << games_.size()
<< "\nTotal number of users = " << players_.size()
<< "\nNumber of ghost users = " << ghost_players_.size()
<< "\nNumber of users in the lobby = " << lobby_.nobservers();
<< "\nNumber of users in the lobby = " << lobby_.size();
return out.str();
} else if (command == "metrics") {
out << metrics_;
@ -2113,7 +2113,7 @@ void server::process_data_game(const network::connection sock,
delete_game(itor);
} else {
g->remove_player(sock);
lobby_.add_player(sock, true);
lobby_.add_player(sock);
g->describe_slots();
// Send all other players in the lobby the update to the gamelist.
@ -2178,7 +2178,7 @@ void server::process_data_game(const network::connection sock,
(ban ? g->ban_user(*data.child("ban"), pl)
: g->kick_member(*data.child("kick"), pl));
if (user) {
lobby_.add_player(user, true);
lobby_.add_player(user);
if (g->describe_slots()) {
update_game_in_lobby(g, user);
}
@ -2285,7 +2285,7 @@ void server::delete_game(std::vector<wesnothd::game*>::iterator game_it) {
static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
(*game_it)->send_data(leave_game_doc);
// Put the remaining users back in the lobby.
lobby_.add_players(**game_it, true);
lobby_.add_players(**game_it);
(*game_it)->send_data(games_and_users_list_);

View file

@ -4,6 +4,7 @@
#include "../network.hpp"
#include "ban.hpp"
#include "player.hpp"
#include "room.hpp"
#include "simple_wml.hpp"
#include "user_handler.hpp"
#include <boost/scoped_ptr.hpp>
@ -39,8 +40,8 @@ private:
std::vector<wesnothd::game*> games_;
wesnothd::game not_logged_in_;
/** The lobby is implemented as a game. */
wesnothd::game lobby_;
/** The lobby is implemented as a room. */
wesnothd::room lobby_;
/** server socket/fifo. */
boost::scoped_ptr<input_stream> input_;