Optimized handling of friends/ignores lists.

These are now backed by a set, instead of doing string operations on
preferences. This resolves the problem of very high CPU usage
in multiplayer lobby when friend/ignore lists are long.
This commit is contained in:
Karol Nowak 2009-02-04 21:48:16 +00:00
parent a77dbecae0
commit 39240dba88
10 changed files with 124 additions and 63 deletions

View File

@ -476,6 +476,7 @@ SET(test_SRC
tests/test_policy.cpp
tests/test_team.cpp
tests/test_util.cpp
tests/test_serialization.cpp
tests/test_version.cpp
tests/gui/test_save_dialog.cpp
tests/gui/test_drop_target.cpp

View File

@ -289,6 +289,7 @@ test_SOURCES = \
tests/test_policy.cpp \
tests/test_team.cpp \
tests/test_util.cpp \
tests/test_serialization.cpp \
tests/test_version.cpp \
tests/gui/test_save_dialog.cpp \
tests/gui/test_drop_target.cpp \

View File

@ -371,6 +371,7 @@ test_sources = Split("""
tests/test_policy.cpp
tests/test_team.cpp
tests/test_util.cpp
tests/test_serialization.cpp
tests/test_version.cpp
tests/gui/test_drop_target.cpp
tests/gui/test_save_dialog.cpp

View File

@ -39,19 +39,10 @@ std::set<t_translation::t_terrain> encountered_terrains_set;
std::map<std::string, std::vector<std::string> > history_map;
const unsigned max_history_saved = 50;
/** Add a nick to the specified relation setting. */
void add_relation(const std::string& nick, const std::string& relation) {
std::vector<std::string> r = utils::split(preferences::get(relation));
r.push_back(nick);
preferences::set(relation, utils::join(r));
}
/** Remove a nick from the specified relation setting. */
void remove_relation(const std::string& nick, const std::string& relation) {
std::vector<std::string> r = utils::split(preferences::get(relation));
r.erase(std::remove(r.begin(), r.end(), nick), r.end());
preferences::set(relation, utils::join(r));
}
std::set<std::string> friends;
std::set<std::string> ignores;
bool friends_initialized = false;
bool ignores_initialized = false;
} // anon namespace
@ -149,32 +140,58 @@ manager::~manager()
set_ping_timeout(network::ping_timeout);
}
std::string get_friends() {
return preferences::get("friends");
const std::set<std::string> & get_friends() {
if(!friends_initialized) {
std::vector<std::string> names = utils::split(preferences::get("friends"));
std::set<std::string> tmp(names.begin(), names.end());
friends.swap(tmp);
friends_initialized = true;
}
return friends;
}
std::string get_ignores() {
return preferences::get("ignores");
const std::set<std::string> & get_ignores() {
if(!ignores_initialized) {
std::vector<std::string> names = utils::split(preferences::get("ignores"));
std::set<std::string> tmp(names.begin(), names.end());
ignores.swap(tmp);
ignores_initialized = true;
}
return ignores;
}
bool add_friend(const std::string& nick) {
if (!utils::isvalid_wildcard(nick)) return false;
add_relation(nick, "friends");
friends.insert(nick);
preferences::set("friends", utils::join(friends));
return true;
}
bool add_ignore(const std::string& nick) {
if (!utils::isvalid_wildcard(nick)) return false;
add_relation(nick, "ignores");
ignores.insert(nick);
preferences::set("ignores", utils::join(ignores));
return true;
}
void remove_friend(const std::string& nick) {
remove_relation(nick, "friends");
std::set<std::string>::iterator i = friends.find(nick);
if(i != friends.end()) {
friends.erase(i);
preferences::set("friends", utils::join(friends));
}
}
void remove_ignore(const std::string& nick) {
remove_relation(nick, "ignores");
std::set<std::string>::iterator i = ignores.find(nick);
if(i != ignores.end()) {
ignores.erase(i);
preferences::set("ignores", utils::join(ignores));
}
}
void clear_friends() {
@ -186,21 +203,11 @@ void clear_ignores() {
}
bool is_friend(const std::string& nick) {
const std::vector<std::string>& friends = utils::split(get_friends());
foreach(const std::string& var, friends) {
if(utils::wildcard_string_match(nick, var))
return true;
}
return false;
return friends.find(nick) != friends.end();
}
bool is_ignored(const std::string& nick) {
const std::vector<std::string>& ignores = utils::split(get_ignores());
foreach(const std::string& var, ignores) {
if(utils::wildcard_string_match(nick, var))
return true;
}
return false;
return ignores.find(nick) != ignores.end();
}
bool show_lobby_join(const std::string& sender, const std::string& message) {

View File

@ -42,8 +42,8 @@ namespace preferences {
void _set_lobby_joins(int show);
enum { SHOW_NONE, SHOW_FRIENDS, SHOW_ALL };
std::string get_friends();
std::string get_ignores();
const std::set<std::string> & get_friends();
const std::set<std::string> & get_ignores();
bool add_friend(const std::string& nick);
bool add_ignore(const std::string& nick);
void remove_friend(const std::string& nick);

View File

@ -993,12 +993,14 @@ void preferences_dialog::set_advanced_menu()
void preferences_dialog::set_friends_menu()
{
const std::vector<std::string>& friends = utils::split(preferences::get_friends());
const std::vector<std::string>& ignores = utils::split(preferences::get_ignores());
const std::set<std::string>& friends = preferences::get_friends();
const std::set<std::string>& ignores = preferences::get_ignores();
std::vector<std::string> friends_items;
std::vector<std::string> friends_names;
std::string const imgpre = IMAGE_PREFIX + std::string("misc/status-");
std::vector<std::string>::const_iterator i;
std::set<std::string>::const_iterator i;
for (i = friends.begin(); i != friends.end(); ++i)
{
friends_items.push_back(imgpre + "friend.png" + COLUMN_SEPARATOR

View File

@ -2449,14 +2449,17 @@ private:
chat_command_handler cch(*this, allies_only);
cch.dispatch(cmd);
}
void chat_command_handler::do_emote()
{
chat_handler_.send_chat_message("/me " + get_data(), allies_only_);
}
void chat_command_handler::do_network_send()
{
chat_handler_.send_command(get_cmd(), get_data());
}
void chat_command_handler::do_whisper()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
@ -2471,6 +2474,7 @@ private:
cwhisper["message"], game_display::MESSAGE_PRIVATE);
network::send_data(data, 0, true);
}
void chat_command_handler::do_log()
{
chat_handler_.change_logging(get_data());
@ -2479,10 +2483,10 @@ private:
void chat_command_handler::do_ignore()
{
if (get_arg(1).empty()) {
const std::string& tmp = preferences::get_ignores();
print("ignores list", tmp.empty() ? "(empty)" : tmp);
const std::set<std::string>& tmp = preferences::get_ignores();
print("ignores list", tmp.empty() ? "(empty)" : utils::join(tmp));
} else {
for(int i = 1;!get_arg(i).empty();i++){
for(int i = 1; !get_arg(i).empty(); i++){
if (preferences::add_ignore(get_arg(i))) {
print("ignore", _("Added to ignore list: ") + get_arg(i));
} else {
@ -2491,11 +2495,12 @@ private:
}
}
}
void chat_command_handler::do_friend()
{
if (get_arg(1).empty()) {
const std::string& tmp = preferences::get_friends();
print("friends list", tmp.empty() ? "(empty)" : tmp);
const std::set<std::string>& tmp = preferences::get_friends();
print("friends list", tmp.empty() ? "(empty)" : utils::join(tmp));
} else {
for(int i = 1;!get_arg(i).empty();i++){
if (preferences::add_friend(get_arg(i))) {
@ -2506,6 +2511,7 @@ private:
}
}
}
void chat_command_handler::do_remove()
{
for(int i = 1;!get_arg(i).empty();i++){
@ -2514,19 +2520,25 @@ private:
print("list", _("Removed from list: ") + get_arg(i));
}
}
void chat_command_handler::do_display()
{
const std::string& text_friend = preferences::get_friends();
const std::string& text_ignore = preferences::get_ignores();
if (!text_friend.empty()) {
print("friends list", text_friend);
const std::set<std::string> & friends = preferences::get_friends();
const std::set<std::string> & ignores = preferences::get_ignores();
if (!friends.empty()) {
print("friends list", utils::join(friends));
}
if (!text_ignore.empty()) {
print("ignores list", text_ignore);
} else if (text_friend.empty()) {
if (!ignores.empty()) {
print("ignores list", utils::join(ignores));
}
if (friends.empty() && ignores.empty()) {
print("list", _("There are no players on your friends or ignore list."));
}
}
void chat_command_handler::do_version() {
print("version", game_config::revision);
}

View File

@ -390,18 +390,6 @@ bool wildcard_string_match(const std::string& str, const std::string& match) {
return matches;
}
std::string join(std::vector< std::string > const &v, char c)
{
std::stringstream str;
for(std::vector< std::string >::const_iterator i = v.begin(); i != v.end(); ++i) {
str << *i;
if (i + 1 != v.end())
str << c;
}
return str.str();
}
std::vector< std::string > quoted_split(std::string const &val, char c, int flags, char quote)
{
std::vector<std::string> res;

View File

@ -20,8 +20,10 @@
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#include <boost/utility.hpp>
#include "../tstring.hpp"
#include "../util.hpp"
@ -77,7 +79,18 @@ std::vector< std::string > paranthetical_split(std::string const &val,
const char separator = 0 , std::string const &left="(",
std::string const &right=")",int flags = REMOVE_EMPTY | STRIP_SPACES);
std::string join(std::vector< std::string > const &v, char c = ',');
template <typename T>
std::string join(T const &v, char c = ',')
{
std::stringstream str;
for(typename T::const_iterator i = v.begin(); i != v.end(); ++i) {
str << *i;
if (boost::next(i) != v.end())
str << c;
}
return str.str();
}
/**
* This function is identical to split(), except it does not split

View File

@ -0,0 +1,36 @@
/* $Id$ */
/*
Copyright (C) 2009 by Karol Nowak <grywacz@gmail.com>
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.
*/
#include <vector>
#include <string>
#include "serialization/string_utils.hpp"
#include <boost/test/auto_unit_test.hpp>
BOOST_AUTO_TEST_CASE( utils_join_test )
{
BOOST_MESSAGE( "Starting utils::join test!" );
std::vector<std::string> fruit;
BOOST_CHECK( utils::join(fruit) == "" );
fruit.push_back("apples");
BOOST_CHECK( utils::join(fruit) == "apples" );
fruit.push_back("oranges");
fruit.push_back("lemons");
BOOST_CHECK( utils::join(fruit) == "apples,oranges,lemons" );
}