refactor mp and menu_events code

This adds play_controller::is_networked_mp() and
play_controller::send_to_wesnothd() which are now used to comminucate
with wesnothd during a game instead of using network::send_data and
network::nconnections() directly.

The main intention is to make future changes to this code easier
specially:
1) to have more than one connection during a game, for example to check
for updates
2) to replace the clientsided network code with boost asio.

In order to do that i needed a little change to the chat_handler class
which is used both ingame and in the mp lobby. And to do that i
had to cleanup up the menu_events.cpp file and move huge helper
classes into their own files.
This commit is contained in:
gfgtdf 2016-06-01 18:35:25 +02:00
parent 50a2357461
commit 52f0e3ecd6
30 changed files with 1052 additions and 928 deletions

View file

@ -697,6 +697,8 @@ set(wesnoth-main_SRC
attack_prediction_display.cpp
build_info.cpp
carryover.cpp
chat_command_handler.cpp
chat_events.cpp
commandline_options.cpp
config_cache.cpp
game_initialization/configure_engine.cpp

View file

@ -247,6 +247,8 @@ wesnoth_sources = Split("""
attack_prediction.cpp
build_info.cpp
carryover.cpp
chat_command_handler.cpp
chat_events.cpp
commandline_options.cpp
config_cache.cpp
controller_base.cpp

View file

@ -1409,7 +1409,7 @@ namespace
int res = 0;
team t = (*resources::teams)[side_num_ - 1];
//i wonder how this got included here ?
bool is_mp = network::nconnections() != 0;
bool is_mp = resources::controller->is_networked_mp();
bool is_current_side = resources::controller->current_side() == side_num_;
//note, that the advancements for networked sides are also determined on the current playing side.

View file

@ -0,0 +1,226 @@
#include "map_command_handler.hpp"
#include "chat_command_handler.hpp"
#include "chat_events.hpp"
#include "game_preferences.hpp"
#include "preferences_display.hpp"
#include "video.hpp"
#include "game_config_manager.hpp"
namespace events {
bool chat_command_handler::is_enabled(const map_command_handler<chat_command_handler>::command& c) const
{
return !(c.has_flag('A') && !preferences::is_authenticated());
}
void chat_command_handler::print(const std::string& title, const std::string& message)
{
chat_handler_.add_chat_message(time(nullptr), title, 0, message);
}
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_network_send_req_arg()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
do_network_send();
}
void chat_command_handler::do_room_query_noarg()
{
config data;
config& q = data.add_child("room_query");
q.add_child(get_cmd());
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_room_query()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& q = data.add_child("room_query");
q["room"] = get_arg(1);
q.add_child(get_cmd());
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_gen_room_query()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& q = data.add_child("room_query");
q["room"] = get_arg(1);
config& c = q.add_child(get_arg(2));
c["value"] = get_data(3);
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_whisper()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
chat_handler_.send_whisper(get_arg(1), get_data(2));
chat_handler_.add_whisper_sent(get_arg(1), get_data(2));
}
void chat_command_handler::do_chanmsg()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
chat_handler_.send_chat_room_message(get_arg(1), get_data(2));
chat_handler_.add_chat_room_message_sent(get_arg(1), get_data(2));
}
void chat_command_handler::do_log()
{
chat_handler_.change_logging(get_data());
}
void chat_command_handler::do_ignore()
{
if (get_arg(1).empty()) {
do_display();
}
else {
utils::string_map symbols;
symbols["nick"] = get_arg(1);
if (preferences::add_ignore(get_arg(1), get_data(2))) {
print(_("ignores list"), VGETTEXT("Added to ignore list: $nick", symbols));
chat_handler_.user_relation_changed(get_arg(1));
}
else {
command_failed(VGETTEXT("Invalid username: $nick", symbols));
}
}
}
void chat_command_handler::do_friend()
{
if (get_arg(1).empty()) {
do_display();
}
else {
utils::string_map symbols;
symbols["nick"] = get_arg(1);
if (preferences::add_friend(get_arg(1), get_data(2))) {
print(_("friends list"), VGETTEXT("Added to friends list: $nick", symbols));
chat_handler_.user_relation_changed(get_arg(1));
}
else {
command_failed(VGETTEXT("Invalid username: $nick", symbols));
}
}
}
void chat_command_handler::do_remove()
{
for (int i = 1;!get_arg(i).empty();i++) {
preferences::remove_acquaintance(get_arg(i));
chat_handler_.user_relation_changed(get_arg(i));
utils::string_map symbols;
symbols["nick"] = get_arg(i);
print(_("friends and ignores list"), VGETTEXT("Removed from list: $nick", symbols));
}
}
void chat_command_handler::do_display()
{
// TODO: add video and game config argument to chat_command_handler?
preferences::show_preferences_dialog(CVideo::get_singleton(),
game_config_manager::get()->game_config(), preferences::VIEW_FRIENDS);
}
void chat_command_handler::do_version() {
print(_("version"), game_config::revision);
}
void chat_command_handler::do_register() {
config data;
config& nickserv = data.add_child("nickserv");
if (get_data(1).empty()) return command_failed_need_arg(1);
config &reg = nickserv.add_child("register");
reg["password"] = get_arg(1);
if (!get_data(2).empty()) {
reg["mail"] = get_arg(2);
}
std::string msg;
if (get_data(2).empty()) {
msg = _("registering with password *** and no email address");
}
else {
utils::string_map symbols;
symbols["email"] = get_data(2);
msg = VGETTEXT("registering with password *** and "
"email address $email", symbols);
}
print(_("nick registration"), msg);
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_drop() {
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("drop");
print(_("nick registration"), _("dropping your username"));
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_set() {
config data;
config& nickserv = data.add_child("nickserv");
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
config &set = nickserv.add_child("set");
set["detail"] = get_arg(1);
set["value"] = get_data(2);
utils::string_map symbols;
symbols["var"] = get_arg(1);
symbols["value"] = get_arg(2);
print(_("nick registration"), VGETTEXT("setting $var to $value", symbols));
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_info() {
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("info")["name"] = get_data(1);
utils::string_map symbols;
symbols["nick"] = get_arg(1);
print(_("nick registration"), VGETTEXT("requesting information for user $nick", symbols));
chat_handler_.send_to_server(data);
}
void chat_command_handler::do_details() {
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("details");
chat_handler_.send_to_server(data);
}
}

View file

@ -0,0 +1,154 @@
#pragma once
#include "map_command_handler.hpp"
#include "gettext.hpp"
namespace events {
class chat_handler;
//command handler for chat /commands
class chat_command_handler : public map_command_handler<chat_command_handler>
{
public:
typedef map_command_handler<chat_command_handler> map;
chat_command_handler(chat_handler& chathandler, bool allies_only)
: map(), chat_handler_(chathandler), allies_only_(allies_only)
{
}
protected:
void do_emote();
void do_network_send();
void do_network_send_req_arg();
void do_room_query();
void do_room_query_noarg();
void do_gen_room_query();
void do_whisper();
void do_chanmsg();
void do_log();
void do_ignore();
void do_friend();
void do_remove();
void do_display();
void do_version();
/** Ask the server to register the currently used nick. */
void do_register();
/** Ask the server do drop the currently used (and registered) nick. */
void do_drop();
/** Update details for the currently used username. */
void do_set();
/** Request information about a user from the server. */
void do_info();
/**
* Request a list of details that can be set for a username
* as these might vary depending on the configuration of the server.
*/
void do_details();
std::string get_flags_description() const {
return _("(A) admin command");
}
std::string get_command_flags_description(
const map_command_handler<chat_command_handler>::command& c) const
{
if (c.has_flag('A')) {
return std::string(" ") + _("(admin only)");
}
else {
return "";
}
}
bool is_enabled(const map_command_handler<chat_command_handler>::command& c) const;
void print(const std::string& title, const std::string& message);
void init_map()
{
set_cmd_prefix("/");
register_command("query", &chat_command_handler::do_network_send,
_("Send a query to the server. Without arguments the server"
" should tell you the available commands."));
register_alias("query", "q");
register_command("ban", &chat_command_handler::do_network_send_req_arg,
_("Ban and kick a player or observer. If he is not in the"
" game but on the server he will only be banned."), _("<nickname>"));
register_command("unban", &chat_command_handler::do_network_send_req_arg,
_("Unban a user. He does not have to be in the game but on"
" the server."), _("<nickname>"));
register_command("kick", &chat_command_handler::do_network_send_req_arg,
_("Kick a player or observer."), _("<nickname>"));
register_command("mute", &chat_command_handler::do_network_send,
_("Mute an observer. Without an argument displays the mute status."), _("<nickname>"));
register_command("unmute", &chat_command_handler::do_network_send,
_("Unmute an observer. Without an argument unmutes everyone."), _("<nickname>"));
register_command("muteall", &chat_command_handler::do_network_send,
_("Mute/Unmute all observers. (toggles)"), "");
register_command("ping", &chat_command_handler::do_network_send,
"");
register_command("green", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("red", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("yellow", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("report", &chat_command_handler::do_network_send_req_arg,
_("Report abuse, rule violations, etc. to the server moderators. "
"Make sure to mention relevant nicknames, etc."), "");
register_alias("report", "adminmsg"); // deprecated
register_command("emote", &chat_command_handler::do_emote,
_("Send an emotion or personal action in chat."), _("<message>"));
register_alias("emote", "me");
register_command("whisper", &chat_command_handler::do_whisper,
_("Sends a private message. "
"You cannot send private messages to players in a running game you observe or play in."),
_("<nickname> <message>"));
register_alias("whisper", "msg");
register_alias("whisper", "m");
register_command("log", &chat_command_handler::do_log,
_("Change the log level of a log domain."), _("<level> <domain>"));
register_command("ignore", &chat_command_handler::do_ignore,
_("Add a nickname to your ignores list."), _("<nickname>"));
register_command("friend", &chat_command_handler::do_friend,
_("Add a nickname to your friends list."), _("<nickname>"));
register_command("remove", &chat_command_handler::do_remove,
_("Remove a nickname from your ignores or friends list."), _("<nickname>"));
register_command("version", &chat_command_handler::do_version,
_("Display version information."));
register_command("register", &chat_command_handler::do_register,
_("Register your nickname"), _("<password> <email (optional)>"));
register_command("drop", &chat_command_handler::do_drop,
_("Drop your nickname."));
register_command("set", &chat_command_handler::do_set,
_("Update details for your nickname. For possible details see '/details'."),
_("<detail> <value>"));
register_command("info", &chat_command_handler::do_info,
_("Request information about a nickname."), _("<nickname>"));
register_command("details", &chat_command_handler::do_details,
_("Request a list of details you can set for your registered nickname."));
register_command("join", &chat_command_handler::do_network_send_req_arg,
_("Join a room."), _("<room>"));
register_alias("join", "j");
register_command("part", &chat_command_handler::do_network_send_req_arg,
_("Part a room."), _("<room>"));
register_command("names", &chat_command_handler::do_room_query,
_("List room members."), _("<room>"));
register_command("rooms", &chat_command_handler::do_room_query_noarg,
_("List available rooms."));
register_command("room", &chat_command_handler::do_chanmsg,
_("Room message."), _("<room> <msg>"));
register_command("room_query", &chat_command_handler::do_gen_room_query,
_("Room query."), _("<room> <type> [value]"));
register_alias("room_query", "rq");
}
private:
chat_handler& chat_handler_;
bool allies_only_;
};
}

182
src/chat_events.cpp Normal file
View file

@ -0,0 +1,182 @@
#include "chat_events.hpp"
#include "global.hpp"
#include "config_assign.hpp"
#include "formatter.hpp"
#include "formula/string_utils.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "map_command_handler.hpp"
#include "chat_command_handler.hpp"
#include "preferences.hpp"
#include "game_preferences.hpp"
#include <boost/range/algorithm/find_if.hpp>
static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
#define LOG_NG LOG_STREAM(info, log_engine)
namespace events {
chat_handler::chat_handler()
{
}
chat_handler::~chat_handler()
{
}
/**
* Change the log level of a log domain.
*
* @param data string of the form: "@<level@> @<domain@>"
*/
void chat_handler::change_logging(const std::string& data) {
const std::string::const_iterator j =
std::find(data.begin(), data.end(), ' ');
if (j == data.end()) return;
const std::string level(data.begin(), j);
const std::string domain(j + 1, data.end());
int severity;
if (level == "error") severity = lg::err().get_severity();
else if (level == "warning") severity = lg::warn().get_severity();
else if (level == "info") severity = lg::info().get_severity();
else if (level == "debug") severity = lg::debug().get_severity();
else {
utils::string_map symbols;
symbols["level"] = level;
const std::string& msg =
vgettext("Unknown debug level: '$level'.", symbols);
ERR_NG << msg << std::endl;
add_chat_message(time(nullptr), _("error"), 0, msg);
return;
}
if (!lg::set_log_domain_severity(domain, severity)) {
utils::string_map symbols;
symbols["domain"] = domain;
const std::string& msg =
vgettext("Unknown debug domain: '$domain'.", symbols);
ERR_NG << msg << std::endl;
add_chat_message(time(nullptr), _("error"), 0, msg);
return;
}
else {
utils::string_map symbols;
symbols["level"] = level;
symbols["domain"] = domain;
const std::string& msg =
vgettext("Switched domain: '$domain' to level: '$level'.", symbols);
LOG_NG << msg << "\n";
add_chat_message(time(nullptr), "log", 0, msg);
}
}
void chat_handler::send_command(const std::string& cmd, const std::string& args /* = "" */) {
config data;
if (cmd == "muteall") {
data.add_child(cmd);
}
else if (cmd == "query") {
data.add_child(cmd)["type"] = args;
}
else if (cmd == "ban" || cmd == "unban" || cmd == "kick"
|| cmd == "mute" || cmd == "unmute") {
data.add_child(cmd)["username"] = args;
}
else if (cmd == "ping") {
data[cmd] = std::to_string(time(nullptr));
}
else if (cmd == "green") {
data.add_child("query")["type"] = "lobbymsg @" + args;
}
else if (cmd == "red") {
data.add_child("query")["type"] = "lobbymsg #" + args;
}
else if (cmd == "yellow") {
data.add_child("query")["type"] = "lobbymsg <255,255,0>" + args;
}
else if (cmd == "report") {
data.add_child("query")["type"] = "report " + args;
}
else if (cmd == "join") {
data.add_child("room_join")["room"] = args;
}
else if (cmd == "part") {
data.add_child("room_part")["room"] = args;
}
send_to_server(data);
}
void chat_handler::do_speak(const std::string& message, bool allies_only)
{
if (message == "" || message == "/") {
return;
}
bool is_command = (message[0] == '/');
bool quoted_command = (is_command && message[1] == ' ');
if (!is_command) {
send_chat_message(message, allies_only);
return;
}
else if (quoted_command) {
send_chat_message(std::string(message.begin() + 2, message.end()), allies_only);
return;
}
std::string cmd(message.begin() + 1, message.end());
chat_command_handler cch(*this, allies_only);
cch.dispatch(cmd);
}
void chat_handler::user_relation_changed(const std::string& /*name*/)
{
}
void chat_handler::send_whisper(const std::string& receiver, const std::string& message)
{
config cwhisper, data;
cwhisper["receiver"] = receiver;
cwhisper["message"] = message;
cwhisper["sender"] = preferences::login();
data.add_child("whisper", cwhisper);
send_to_server(data);
}
void chat_handler::add_whisper_sent(const std::string& receiver, const std::string& message)
{
utils::string_map symbols;
symbols["receiver"] = receiver;
add_chat_message(time(nullptr), VGETTEXT("whisper to $receiver", symbols), 0, message);
}
void chat_handler::add_whisper_received(const std::string& sender, const std::string& message)
{
utils::string_map symbols;
symbols["sender"] = sender;
add_chat_message(time(nullptr), VGETTEXT("whisper: $sender", symbols), 0, message);
}
void chat_handler::send_chat_room_message(const std::string& room,
const std::string& message)
{
config cmsg, data;
cmsg["room"] = room;
cmsg["message"] = message;
cmsg["sender"] = preferences::login();
data.add_child("message", cmsg);
send_to_server(data);
}
void chat_handler::add_chat_room_message_sent(const std::string &room, const std::string &message)
{
add_chat_room_message_received(room, preferences::login(), message);
}
void chat_handler::add_chat_room_message_received(const std::string &room,
const std::string &speaker, const std::string &message)
{
add_chat_message(time(nullptr), room + ": " + speaker, 0, message, events::chat_handler::MESSAGE_PRIVATE);
}
}

View file

@ -17,7 +17,7 @@
#define CHAT_EVENTS_H_INCLUDED
#include "global.hpp"
class config;
#include <ctime>
#include <string>
@ -32,6 +32,8 @@ public:
enum MESSAGE_TYPE { MESSAGE_PUBLIC, MESSAGE_PRIVATE };
void send_command(const std::string& cmd, const std::string& args="");
virtual void send_to_server(const config& cfg) = 0;
protected:
void do_speak(const std::string& message, bool allies_only=false);
@ -39,9 +41,9 @@ protected:
virtual void add_chat_message(const time_t& time,
const std::string& speaker, int side, const std::string& message,
MESSAGE_TYPE type=MESSAGE_PRIVATE)=0;
virtual void send_chat_message(const std::string& message, bool allies_only=false)=0;
//Why are these virtual?
virtual void send_whisper(const std::string& receiver, const std::string& message);
virtual void add_whisper_sent(const std::string& receiver, const std::string& message);

View file

@ -793,4 +793,9 @@ plugins_context * ui::get_plugins_context() {
return plugins_context_.get();
}
void ui::send_to_server(const config& cfg)
{
network::send_data(cfg, 0);
}
}// namespace mp

View file

@ -104,6 +104,7 @@ public:
void set_location(const SDL_Rect& rect);
using widget::set_location;
const std::vector<std::string>& user_list() const { return user_list_; }
void send_to_server(const config& cfg) override;
protected:
int xscale(int x) const;
int yscale(int y) const;

View file

@ -1765,5 +1765,9 @@ void tlobby_main::skip_replay_changed_callback(twidget& w)
ttoggle_button& tb = dynamic_cast<ttoggle_button&>(w);
preferences::set_skip_mp_replay(tb.get_value_bool());
}
void tlobby_main::send_to_server(const config& cfg)
{
network::send_data(cfg, 0);
}
} // namespace gui2

View file

@ -95,6 +95,8 @@ public:
void update_gamelist();
void send_to_server(const config& cfg) override;
protected:
void update_gamelist_header();

View file

@ -24,7 +24,6 @@
#include "game_state.hpp"
#include "hotkey/hotkey_command.hpp"
#include "hotkey/hotkey_item.hpp"
#include "network.hpp" //for nconnections (to determine if we are in a networked game)
#include "play_controller.hpp"
#include "preferences_display.hpp"
#include "savegame.hpp"
@ -337,7 +336,7 @@ bool play_controller::hotkey_handler::can_execute_command(const hotkey::hotkey_c
return !linger() && play_controller_.enemies_visible();
case hotkey::HOTKEY_LOAD_GAME:
return network::nconnections() == 0; // Can only load games if not in a network game
return !play_controller_.is_networked_mp(); // Can only load games if not in a network game
case hotkey::HOTKEY_CHAT_LOG:
return true;

View file

@ -16,7 +16,6 @@
#include "hotkey/hotkey_command.hpp"
#include "hotkey/hotkey_item.hpp"
#include "network.hpp" //for nconnections (to determine if we are in a networked game)
#include "playsingle_controller.hpp"
#include "playmp_controller.hpp"
@ -66,7 +65,7 @@ bool playmp_controller::hotkey_handler::can_execute_command(const hotkey::hotkey
case hotkey::HOTKEY_SPEAK:
case hotkey::HOTKEY_SPEAK_ALLY:
case hotkey::HOTKEY_SPEAK_ALL:
res = network::nconnections() > 0;
res = playmp_controller_.is_networked_mp();
break;
case hotkey::HOTKEY_START_NETWORK:
case hotkey::HOTKEY_STOP_NETWORK:

View file

@ -23,7 +23,6 @@
#include "whiteboard/manager.hpp"
#include "game_events/menu_item.hpp"
#include "game_events/wmi_container.hpp"
#include "network.hpp"
#include "save_index.hpp"
#include "gui/dialogs/message.hpp"
#include "resources.hpp"
@ -298,7 +297,7 @@ bool playsingle_controller::hotkey_handler::can_execute_command(const hotkey::ho
void playsingle_controller::hotkey_handler::load_autosave(const std::string& filename)
{
if(network::nconnections() > 0)
if(playsingle_controller_.is_networked_mp())
{
config savegame;
std::string error_log;
@ -321,7 +320,7 @@ void playsingle_controller::hotkey_handler::load_autosave(const std::string& fil
void playsingle_controller::hotkey_handler::replay_exit()
{
if(network::nconnections() == 0) {
if(!playsingle_controller_.is_networked_mp()) {
resources::recorder->delete_upcoming_commands();
}
playsingle_controller_.set_player_type_changed();

407
src/map_command_handler.hpp Normal file
View file

@ -0,0 +1,407 @@
/*
Copyright (C) 2006 - 2016 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
wesnoth playturn Copyright (C) 2003 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 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.
*/
#pragma once
#include "config.hpp"
#include "serialization/string_utils.hpp"
#include "formula/string_utils.hpp"
#include "gettext.hpp"
namespace events {
//simple command args parser, separated from command_handler for clarity.
//a word begins with a nonspace
//n-th arg is n-th word up to the next space
//n-th data is n-th word up to the end
//cmd is 0-th arg, begins at 0 always.
class cmd_arg_parser
{
public:
cmd_arg_parser() :
str_(""),
args(1, 0),
args_end(false)
{
}
explicit cmd_arg_parser(const std::string& str) :
str_(str),
args(1, 0),
args_end(false)
{
}
void parse(const std::string& str)
{
str_ = str;
args.clear();
args.push_back(0);
args_end = false;
}
const std::string& get_str() const
{
return str_;
}
std::string get_arg(unsigned n) const
{
advance_to_arg(n);
if (n < args.size()) {
return std::string(str_, args[n], str_.find(' ', args[n]) - args[n]);
}
else {
return "";
}
}
std::string get_data(unsigned n) const
{
advance_to_arg(n);
if (n < args.size()) {
std::string data(str_, args[n]);
return utils::strip(data);
}
else {
return "";
}
}
std::string get_cmd() const
{
return get_arg(0);
}
private:
cmd_arg_parser& operator=(const cmd_arg_parser&);
cmd_arg_parser(const cmd_arg_parser&);
void advance_to_arg(unsigned n) const
{
while (n < args.size() && !args_end) {
size_t first_space = str_.find_first_of(' ', args.back());
size_t next_arg_begin = str_.find_first_not_of(' ', first_space);
if (next_arg_begin != std::string::npos) {
args.push_back(next_arg_begin);
}
else {
args_end = true;
}
}
}
std::string str_;
mutable std::vector<size_t> args;
mutable bool args_end;
};
//A helper class template with a slim public interface
//This represents a map of strings to void()-member-function-of-Worker-pointers
//with all the common functionality like general help, command help and aliases
//Usage (of a derived class): Derived(specific-arguments) d; d.dispatch(command);
//Derived classes should override virtual functions where noted.
//The template parameter currently must be the dervived class itself,
//i.e. class X : public map_command_handler<X>
//To add a new command in a derived class:
// * add a new private void function() to the derived class
// * add it to the function map in init_map there, setting flags like
// "D" for debug only (checking the flag is also done in the derived class)
// * remember to add some help and/or usage information in init_map()
template <class Worker>
class map_command_handler
{
public:
typedef void (Worker::*command_handler)();
struct command
{
command_handler handler;
std::string help; //long help text
std::string usage; //only args info
std::string flags;
explicit command(command_handler h, const std::string help = "",
const std::string& usage = "", const std::string flags = "")
: handler(h), help(help), usage(usage), flags(flags)
{
}
bool has_flag(const char f) const
{
return flags.find(f) != flags.npos;
}
command& add_flag(const char f)
{
flags += f;
return *this;
}
};
typedef std::map<std::string, command> command_map;
typedef std::map<std::string, std::string> command_alias_map;
map_command_handler() : cap_("")
{
}
virtual ~map_command_handler() {}
bool empty() const
{
return command_map_.empty();
}
//actual work function
void dispatch(std::string cmd)
{
if (empty()) {
init_map_default();
init_map();
}
// We recursively resolve alias (100 max to avoid infinite recursion)
for (int i = 0; i < 100; ++i) {
parse_cmd(cmd);
std::string actual_cmd = get_actual_cmd(get_cmd());
if (actual_cmd == get_cmd())
break;
std::string data = get_data(1);
// translate the command and add space + data if any
cmd = actual_cmd + (data.empty() ? "" : " ") + data;
}
if (get_cmd().empty()) {
return;
}
if (const command* c = get_command(get_cmd())) {
if (is_enabled(*c)) {
(static_cast<Worker*>(this)->*(c->handler))();
}
else {
print(get_cmd(), _("This command is currently unavailable."));
}
}
else if (help_on_unknown_) {
utils::string_map symbols;
symbols["command"] = get_cmd();
symbols["help_command"] = cmd_prefix_ + "help";
print("help", VGETTEXT("Unknown command '$command', try $help_command "
"for a list of available commands.", symbols));
}
}
std::vector<std::string> get_commands_list() const
{
std::vector<std::string> res;
for (typename command_map::value_type i : command_map_) {
res.push_back(i.first);
}
return res;
}
//command error reporting shorthands
void command_failed(const std::string& message, bool = false)
{
print(get_cmd(), _("Error:") + std::string(" ") + message);
}
protected:
void init_map_default()
{
register_command("help", &map_command_handler<Worker>::help,
_("Available commands list and command-specific help. "
"Use \"help all\" to include currently unavailable commands."),
_("do not translate the 'all'^[all|<command>]"));
}
//derived classes initialize the map overriding this function
virtual void init_map() = 0;
//overridden in derived classes to actually print the messages somwehere
virtual void print(const std::string& title, const std::string& message) = 0;
//should be overridden in derived classes if the commands have flags
//this should return a string describing what all the flags mean
virtual std::string get_flags_description() const
{
return "";
}
//this should return a string describing the flags of the given command
virtual std::string get_command_flags_description(const command& /*c*/) const
{
return "";
}
//this should be overridden if e.g. flags are used to control command
//availability. Return false if the command should not be executed by dispatch()
virtual bool is_enabled(const command& /*c*/) const
{
return true;
}
virtual void parse_cmd(const std::string& cmd_string)
{
cap_.parse(cmd_string);
}
//safe n-th argunment getter
virtual std::string get_arg(unsigned argn) const
{
return cap_.get_arg(argn);
}
//"data" is n-th arg and everything after it
virtual std::string get_data(unsigned argn = 1) const
{
return cap_.get_data(argn);
}
virtual std::string get_cmd() const
{
return cap_.get_cmd();
}
void command_failed_need_arg(int argn)
{
utils::string_map symbols;
symbols["arg_id"] = std::to_string(argn);
command_failed(VGETTEXT("Missing argument $arg_id", symbols));
}
void print_usage()
{
help_command(get_cmd());
}
//take aliases into account
std::string get_actual_cmd(const std::string& cmd) const
{
command_alias_map::const_iterator i = command_alias_map_.find(cmd);
return i != command_alias_map_.end() ? i->second : cmd;
}
const command* get_command(const std::string& cmd) const
{
typename command_map::const_iterator i = command_map_.find(cmd);
return i != command_map_.end() ? &i->second : 0;
}
command* get_command(const std::string& cmd)
{
typename command_map::iterator i = command_map_.find(cmd);
return i != command_map_.end() ? &i->second : 0;
}
void help()
{
//print command-specific help if available, otherwise list commands
if (help_command(get_arg(1))) {
return;
}
std::stringstream ss;
bool show_unavail = show_unavailable_ || get_arg(1) == "all";
for (typename command_map::value_type i : command_map_) {
if (show_unavail || is_enabled(i.second)) {
ss << i.first;
//if (!i.second.usage.empty()) {
// ss << " " << i.second.usage;
//}
//uncomment the above to display usage information in command list
//which might clutter it somewhat
if (!i.second.flags.empty()) {
ss << " (" << i.second.flags << ") ";
}
ss << "; ";
}
}
utils::string_map symbols;
symbols["flags_description"] = get_flags_description();
symbols["list_of_commands"] = ss.str();
symbols["help_command"] = cmd_prefix_ + "help";
print(_("help"), VGETTEXT("Available commands $flags_description:\n$list_of_commands", symbols));
print(_("help"), VGETTEXT("Type $help_command <command> for more info.", symbols));
}
//returns true if the command exists.
bool help_command(const std::string& acmd)
{
std::string cmd = get_actual_cmd(acmd);
const command* c = get_command(cmd);
if (c) {
std::stringstream ss;
ss << cmd_prefix_ << cmd;
if (c->help.empty() && c->usage.empty()) {
ss << _(" No help available.");
}
else {
ss << " - " << c->help;
}
if (!c->usage.empty()) {
ss << " " << _("Usage:") << " " << cmd_prefix_ << cmd << " " << c->usage;
}
ss << get_command_flags_description(*c);
const std::vector<std::string> l = get_aliases(cmd);
if (!l.empty()) {
ss << " (" << _("aliases:") << " " << utils::join(l, " ") << ")";
}
print(_("help"), ss.str());
}
return c != 0;
}
cmd_arg_parser cap_;
protected:
//show a "try help" message on unknown command?
static void set_help_on_unknown(bool value)
{
help_on_unknown_ = value;
}
//this is display-only
static void set_cmd_prefix(std::string value)
{
cmd_prefix_ = value;
}
virtual void register_command(const std::string& cmd,
command_handler h, const std::string& help = "",
const std::string& usage = "", const std::string& flags = "")
{
command c = command(h, help, usage, flags);
std::pair<typename command_map::iterator, bool> r;
r = command_map_.insert(typename command_map::value_type(cmd, c));
if (!r.second) { //overwrite if exists
r.first->second = c;
}
}
virtual void assert_existence(const std::string& cmd) {
assert(command_map_.count(cmd));
}
virtual void register_alias(const std::string& to_cmd,
const std::string& cmd)
{
// disable the assert to allow alias to "command + args"
// the fonction assert_existence seems unused now
//assert_existence(to_cmd);
command_alias_map_[cmd] = to_cmd;
}
//get all aliases of a command.
static const std::vector<std::string> get_aliases(const std::string& cmd)
{
std::vector<std::string> aliases;
typedef command_alias_map::value_type p;
for (p i : command_alias_map_) {
if (i.second == cmd) {
aliases.push_back(i.first);
}
}
return aliases;
}
private:
static command_map command_map_;
static command_alias_map command_alias_map_;
static bool help_on_unknown_;
static bool show_unavailable_;
static std::string cmd_prefix_;
};
//static member definitions
template <class Worker>
typename map_command_handler<Worker>::command_map map_command_handler<Worker>::command_map_;
template <class Worker>
typename map_command_handler<Worker>::command_alias_map map_command_handler<Worker>::command_alias_map_;
template <class Worker>
bool map_command_handler<Worker>::help_on_unknown_ = true;
template <class Worker>
bool map_command_handler<Worker>::show_unavailable_ = false;
template <class Worker>
std::string map_command_handler<Worker>::cmd_prefix_;
}

View file

@ -27,6 +27,7 @@
#include "actions/undo.hpp"
#include "actions/vision.hpp"
#include "ai/manager.hpp"
#include "chat_command_handler.hpp"
#include "config_assign.hpp"
#include "dialogs.hpp"
#include "display_chat_manager.hpp"
@ -58,6 +59,7 @@
#include "log.hpp"
#include "map/map.hpp"
#include "map/label.hpp"
#include "map_command_handler.hpp"
#include "marked-up_text.hpp"
#include "menu_events.hpp"
#include "mouse_events.hpp"
@ -1248,533 +1250,6 @@ void menu_handler::add_chat_message(const time_t& time,
("whisper", type == events::chat_handler::MESSAGE_PRIVATE));
}
//simple command args parser, separated from command_handler for clarity.
//a word begins with a nonspace
//n-th arg is n-th word up to the next space
//n-th data is n-th word up to the end
//cmd is 0-th arg, begins at 0 always.
class cmd_arg_parser
{
public:
cmd_arg_parser() :
str_(""),
args(1, 0),
args_end(false)
{
}
explicit cmd_arg_parser(const std::string& str) :
str_(str),
args(1, 0),
args_end(false)
{
}
void parse(const std::string& str)
{
str_ = str;
args.clear();
args.push_back(0);
args_end = false;
}
const std::string& get_str() const
{
return str_;
}
std::string get_arg(unsigned n) const
{
advance_to_arg(n);
if (n < args.size()) {
return std::string(str_, args[n], str_.find(' ', args[n]) - args[n]);
} else {
return "";
}
}
std::string get_data(unsigned n) const
{
advance_to_arg(n);
if (n < args.size()) {
std::string data(str_, args[n]);
return utils::strip(data);
} else {
return "";
}
}
std::string get_cmd() const
{
return get_arg(0);
}
private:
cmd_arg_parser& operator=(const cmd_arg_parser&);
cmd_arg_parser(const cmd_arg_parser&);
void advance_to_arg(unsigned n) const
{
while (n < args.size() && !args_end) {
size_t first_space = str_.find_first_of(' ', args.back());
size_t next_arg_begin = str_.find_first_not_of(' ', first_space);
if (next_arg_begin != std::string::npos) {
args.push_back(next_arg_begin);
} else {
args_end = true;
}
}
}
std::string str_;
mutable std::vector<size_t> args;
mutable bool args_end;
};
//A helper class template with a slim public interface
//This represents a map of strings to void()-member-function-of-Worker-pointers
//with all the common functionality like general help, command help and aliases
//Usage (of a derived class): Derived(specific-arguments) d; d.dispatch(command);
//Derived classes should override virtual functions where noted.
//The template parameter currently must be the dervived class itself,
//i.e. class X : public map_command_handler<X>
//To add a new command in a derived class:
// * add a new private void function() to the derived class
// * add it to the function map in init_map there, setting flags like
// "D" for debug only (checking the flag is also done in the derived class)
// * remember to add some help and/or usage information in init_map()
template <class Worker>
class map_command_handler
{
public:
typedef void (Worker::*command_handler)();
struct command
{
command_handler handler;
std::string help; //long help text
std::string usage; //only args info
std::string flags;
explicit command(command_handler h, const std::string help="",
const std::string& usage="", const std::string flags="")
: handler(h), help(help), usage(usage), flags(flags)
{
}
bool has_flag(const char f) const
{
return flags.find(f) != flags.npos;
}
command& add_flag(const char f)
{
flags += f;
return *this;
}
};
typedef std::map<std::string, command> command_map;
typedef std::map<std::string, std::string> command_alias_map;
map_command_handler() : cap_("")
{
}
virtual ~map_command_handler() {}
bool empty() const
{
return command_map_.empty();
}
//actual work function
void dispatch(std::string cmd)
{
if (empty()) {
init_map_default();
init_map();
}
// We recursively resolve alias (100 max to avoid infinite recursion)
for (int i=0; i < 100; ++i) {
parse_cmd(cmd);
std::string actual_cmd = get_actual_cmd(get_cmd());
if (actual_cmd == get_cmd())
break;
std::string data = get_data(1);
// translate the command and add space + data if any
cmd = actual_cmd + (data.empty() ? "" : " ") + data;
}
if (get_cmd().empty()) {
return;
}
if (const command* c = get_command(get_cmd())) {
if (is_enabled(*c)) {
(static_cast<Worker*>(this)->*(c->handler))();
} else {
print(get_cmd(), _("This command is currently unavailable."));
}
} else if (help_on_unknown_) {
utils::string_map symbols;
symbols["command"] = get_cmd();
symbols["help_command"] = cmd_prefix_ + "help";
print("help", VGETTEXT("Unknown command '$command', try $help_command "
"for a list of available commands.", symbols));
}
}
std::vector<std::string> get_commands_list() const
{
std::vector<std::string> res;
for(typename command_map::value_type i : command_map_) {
res.push_back(i.first);
}
return res;
}
//command error reporting shorthands
void command_failed(const std::string& message, bool = false)
{
print(get_cmd(), _("Error:") + std::string(" ") + message);
}
protected:
void init_map_default()
{
register_command("help", &map_command_handler<Worker>::help,
_("Available commands list and command-specific help. "
"Use \"help all\" to include currently unavailable commands."),
_("do not translate the 'all'^[all|<command>]"));
}
//derived classes initialize the map overriding this function
virtual void init_map() = 0;
//overridden in derived classes to actually print the messages somwehere
virtual void print(const std::string& title, const std::string& message) = 0;
//should be overridden in derived classes if the commands have flags
//this should return a string describing what all the flags mean
virtual std::string get_flags_description() const
{
return "";
}
//this should return a string describing the flags of the given command
virtual std::string get_command_flags_description(const command& /*c*/) const
{
return "";
}
//this should be overridden if e.g. flags are used to control command
//availability. Return false if the command should not be executed by dispatch()
virtual bool is_enabled(const command& /*c*/) const
{
return true;
}
virtual void parse_cmd(const std::string& cmd_string)
{
cap_.parse(cmd_string);
}
//safe n-th argunment getter
virtual std::string get_arg(unsigned argn) const
{
return cap_.get_arg(argn);
}
//"data" is n-th arg and everything after it
virtual std::string get_data(unsigned argn = 1) const
{
return cap_.get_data(argn);
}
virtual std::string get_cmd() const
{
return cap_.get_cmd();
}
void command_failed_need_arg(int argn)
{
utils::string_map symbols;
symbols["arg_id"] = std::to_string(argn);
command_failed(VGETTEXT("Missing argument $arg_id", symbols));
}
void print_usage()
{
help_command(get_cmd());
}
//take aliases into account
std::string get_actual_cmd(const std::string& cmd) const
{
command_alias_map::const_iterator i = command_alias_map_.find(cmd);
return i != command_alias_map_.end() ? i->second : cmd;
}
const command* get_command(const std::string& cmd) const
{
typename command_map::const_iterator i = command_map_.find(cmd);
return i != command_map_.end() ? &i->second : 0;
}
command* get_command(const std::string& cmd)
{
typename command_map::iterator i = command_map_.find(cmd);
return i != command_map_.end() ? &i->second : 0;
}
void help()
{
//print command-specific help if available, otherwise list commands
if (help_command(get_arg(1))) {
return;
}
std::stringstream ss;
bool show_unavail = show_unavailable_ || get_arg(1) == "all";
for(typename command_map::value_type i : command_map_) {
if (show_unavail || is_enabled(i.second)) {
ss << i.first;
//if (!i.second.usage.empty()) {
// ss << " " << i.second.usage;
//}
//uncomment the above to display usage information in command list
//which might clutter it somewhat
if (!i.second.flags.empty()) {
ss << " (" << i.second.flags << ") ";
}
ss << "; ";
}
}
utils::string_map symbols;
symbols["flags_description"] = get_flags_description();
symbols["list_of_commands"] = ss.str();
symbols["help_command"] = cmd_prefix_ + "help";
print(_("help"), VGETTEXT("Available commands $flags_description:\n$list_of_commands", symbols));
print(_("help"), VGETTEXT("Type $help_command <command> for more info.", symbols));
}
//returns true if the command exists.
bool help_command(const std::string& acmd)
{
std::string cmd = get_actual_cmd(acmd);
const command* c = get_command(cmd);
if (c) {
std::stringstream ss;
ss << cmd_prefix_ << cmd;
if (c->help.empty() && c->usage.empty()) {
ss << _(" No help available.");
} else {
ss << " - " << c->help;
}
if (!c->usage.empty()) {
ss << " " << _("Usage:") << " " << cmd_prefix_ << cmd << " " << c->usage;
}
ss << get_command_flags_description(*c);
const std::vector<std::string> l = get_aliases(cmd);
if (!l.empty()) {
ss << " (" << _("aliases:") << " " << utils::join(l," ") << ")";
}
print(_("help"), ss.str());
}
return c != 0;
}
cmd_arg_parser cap_;
protected:
//show a "try help" message on unknown command?
static void set_help_on_unknown(bool value)
{
help_on_unknown_ = value;
}
//this is display-only
static void set_cmd_prefix(std::string value)
{
cmd_prefix_ = value;
}
virtual void register_command(const std::string& cmd,
command_handler h, const std::string& help="",
const std::string& usage="", const std::string& flags="")
{
command c = command(h, help, usage, flags);
std::pair<typename command_map::iterator, bool> r;
r = command_map_.insert(typename command_map::value_type(cmd, c));
if (!r.second) { //overwrite if exists
r.first->second = c;
}
}
virtual void assert_existence(const std::string& cmd) {
assert(command_map_.count(cmd));
}
virtual void register_alias(const std::string& to_cmd,
const std::string& cmd)
{
// disable the assert to allow alias to "command + args"
// the fonction assert_existence seems unused now
//assert_existence(to_cmd);
command_alias_map_[cmd] = to_cmd;
}
//get all aliases of a command.
static const std::vector<std::string> get_aliases(const std::string& cmd)
{
std::vector<std::string> aliases;
typedef command_alias_map::value_type p;
for(p i : command_alias_map_) {
if (i.second == cmd) {
aliases.push_back(i.first);
}
}
return aliases;
}
private:
static command_map command_map_;
static command_alias_map command_alias_map_;
static bool help_on_unknown_;
static bool show_unavailable_;
static std::string cmd_prefix_;
};
//static member definitions
template <class Worker>
typename map_command_handler<Worker>::command_map map_command_handler<Worker>::command_map_;
template <class Worker>
typename map_command_handler<Worker>::command_alias_map map_command_handler<Worker>::command_alias_map_;
template <class Worker>
bool map_command_handler<Worker>::help_on_unknown_ = true;
template <class Worker>
bool map_command_handler<Worker>::show_unavailable_ = false;
template <class Worker>
std::string map_command_handler<Worker>::cmd_prefix_;
//command handler for chat /commands
class chat_command_handler : public map_command_handler<chat_command_handler>
{
public:
typedef map_command_handler<chat_command_handler> map;
chat_command_handler(chat_handler& chathandler, bool allies_only)
: map(), chat_handler_(chathandler), allies_only_(allies_only)
{
}
protected:
void do_emote();
void do_network_send();
void do_network_send_req_arg();
void do_room_query();
void do_room_query_noarg();
void do_gen_room_query();
void do_whisper();
void do_chanmsg();
void do_log();
void do_ignore();
void do_friend();
void do_remove();
void do_display();
void do_version();
/** Ask the server to register the currently used nick. */
void do_register();
/** Ask the server do drop the currently used (and registered) nick. */
void do_drop();
/** Update details for the currently used username. */
void do_set();
/** Request information about a user from the server. */
void do_info();
/**
* Request a list of details that can be set for a username
* as these might vary depending on the configuration of the server.
*/
void do_details();
std::string get_flags_description() const {
return _("(A) admin command");
}
std::string get_command_flags_description(
const map_command_handler<chat_command_handler>::command& c) const
{
if (c.has_flag('A')) {
return std::string(" ") + _("(admin only)");
} else {
return "";
}
}
bool is_enabled(
const map_command_handler<chat_command_handler>::command& c) const
{
return !(c.has_flag('A') && !preferences::is_authenticated());
}
void print(const std::string& title, const std::string& message)
{
chat_handler_.add_chat_message(time(nullptr), title, 0, message);
}
void init_map()
{
set_cmd_prefix("/");
register_command("query", &chat_command_handler::do_network_send,
_("Send a query to the server. Without arguments the server"
" should tell you the available commands."));
register_alias("query", "q");
register_command("ban", &chat_command_handler::do_network_send_req_arg,
_("Ban and kick a player or observer. If he is not in the"
" game but on the server he will only be banned."), _("<nickname>"));
register_command("unban", &chat_command_handler::do_network_send_req_arg,
_("Unban a user. He does not have to be in the game but on"
" the server."), _("<nickname>"));
register_command("kick", &chat_command_handler::do_network_send_req_arg,
_("Kick a player or observer."), _("<nickname>"));
register_command("mute", &chat_command_handler::do_network_send,
_("Mute an observer. Without an argument displays the mute status."), _("<nickname>"));
register_command("unmute", &chat_command_handler::do_network_send,
_("Unmute an observer. Without an argument unmutes everyone."), _("<nickname>"));
register_command("muteall", &chat_command_handler::do_network_send,
_("Mute/Unmute all observers. (toggles)"), "");
register_command("ping", &chat_command_handler::do_network_send,
"");
register_command("green", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("red", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("yellow", &chat_command_handler::do_network_send_req_arg,
"", "", "A");
register_command("report", &chat_command_handler::do_network_send_req_arg,
_("Report abuse, rule violations, etc. to the server moderators. "
"Make sure to mention relevant nicknames, etc."), "");
register_alias("report", "adminmsg"); // deprecated
register_command("emote", &chat_command_handler::do_emote,
_("Send an emotion or personal action in chat."), _("<message>"));
register_alias("emote", "me");
register_command("whisper", &chat_command_handler::do_whisper,
_("Sends a private message. "
"You cannot send private messages to players in a running game you observe or play in."),
_("<nickname> <message>"));
register_alias("whisper", "msg");
register_alias("whisper", "m");
register_command("log", &chat_command_handler::do_log,
_("Change the log level of a log domain."), _("<level> <domain>"));
register_command("ignore", &chat_command_handler::do_ignore,
_("Add a nickname to your ignores list."), _("<nickname>"));
register_command("friend", &chat_command_handler::do_friend,
_("Add a nickname to your friends list."), _("<nickname>"));
register_command("remove", &chat_command_handler::do_remove,
_("Remove a nickname from your ignores or friends list."), _("<nickname>"));
register_command("version", &chat_command_handler::do_version,
_("Display version information."));
register_command("register", &chat_command_handler::do_register,
_("Register your nickname"), _("<password> <email (optional)>"));
register_command("drop", &chat_command_handler::do_drop,
_("Drop your nickname."));
register_command("set", &chat_command_handler::do_set,
_("Update details for your nickname. For possible details see '/details'."),
_("<detail> <value>"));
register_command("info", &chat_command_handler::do_info,
_("Request information about a nickname."), _("<nickname>"));
register_command("details", &chat_command_handler::do_details,
_("Request a list of details you can set for your registered nickname."));
register_command("join", &chat_command_handler::do_network_send_req_arg,
_("Join a room."), _("<room>"));
register_alias("join", "j");
register_command("part", &chat_command_handler::do_network_send_req_arg,
_("Part a room."), _("<room>"));
register_command("names", &chat_command_handler::do_room_query,
_("List room members."), _("<room>"));
register_command("rooms", &chat_command_handler::do_room_query_noarg,
_("List available rooms."));
register_command("room", &chat_command_handler::do_chanmsg,
_("Room message."), _("<room> <msg>"));
register_command("room_query", &chat_command_handler::do_gen_room_query,
_("Room query."), _("<room> <type> [value]"));
register_alias("room_query", "rq");
}
private:
chat_handler& chat_handler_;
bool allies_only_;
};
//command handler for user :commands. Also understands all chat commands
//via inheritance. This complicates some things a bit.
class console_handler : public map_command_handler<console_handler>, private chat_command_handler
@ -1887,7 +1362,7 @@ class console_handler : public map_command_handler<console_handler>, private cha
bool is_enabled(const chmap::command& c) const
{
return !((c.has_flag('D') && !game_config::debug)
|| (c.has_flag('N') && network::nconnections() == 0)
|| (c.has_flag('N') && menu_handler_.pc_.is_networked_mp())
|| (c.has_flag('A') && !preferences::is_authenticated())
|| (c.has_flag('S') && (synced_context::get_synced_state() != synced_context::UNSYNCED)));
}
@ -2017,355 +1492,6 @@ class console_handler : public map_command_handler<console_handler>, private cha
const unsigned int team_num_;
};
chat_handler::chat_handler()
{
}
chat_handler::~chat_handler()
{
}
/**
* Change the log level of a log domain.
*
* @param data string of the form: "@<level@> @<domain@>"
*/
void chat_handler::change_logging(const std::string& data) {
const std::string::const_iterator j =
std::find(data.begin(), data.end(), ' ');
if (j == data.end()) return;
const std::string level(data.begin(),j);
const std::string domain(j+1,data.end());
int severity;
if (level == "error") severity = lg::err().get_severity();
else if (level == "warning") severity = lg::warn().get_severity();
else if (level == "info") severity = lg::info().get_severity();
else if (level == "debug") severity = lg::debug().get_severity();
else {
utils::string_map symbols;
symbols["level"] = level;
const std::string& msg =
vgettext("Unknown debug level: '$level'.", symbols);
ERR_NG << msg << std::endl;
add_chat_message(time(nullptr), _("error"), 0, msg);
return;
}
if (!lg::set_log_domain_severity(domain, severity)) {
utils::string_map symbols;
symbols["domain"] = domain;
const std::string& msg =
vgettext("Unknown debug domain: '$domain'.", symbols);
ERR_NG << msg << std::endl;
add_chat_message(time(nullptr), _("error"), 0, msg);
return;
} else {
utils::string_map symbols;
symbols["level"] = level;
symbols["domain"] = domain;
const std::string& msg =
vgettext("Switched domain: '$domain' to level: '$level'.", symbols);
LOG_NG << msg << "\n";
add_chat_message(time(nullptr), "log", 0, msg);
}
}
void chat_handler::send_command(const std::string& cmd, const std::string& args /* = "" */) {
config data;
if (cmd == "muteall") {
data.add_child(cmd);
} else if (cmd == "query") {
data.add_child(cmd)["type"] = args;
} else if (cmd == "ban" || cmd == "unban" || cmd == "kick"
|| cmd == "mute" || cmd == "unmute") {
data.add_child(cmd)["username"] = args;
} else if (cmd == "ping") {
data[cmd] = std::to_string(time(nullptr));
} else if (cmd == "green") {
data.add_child("query")["type"] = "lobbymsg @" + args;
} else if (cmd == "red") {
data.add_child("query")["type"] = "lobbymsg #" + args;
} else if (cmd == "yellow") {
data.add_child("query")["type"] = "lobbymsg <255,255,0>" + args;
} else if (cmd == "report") {
data.add_child("query")["type"] = "report " + args;
} else if (cmd == "join") {
data.add_child("room_join")["room"] = args;
} else if (cmd == "part") {
data.add_child("room_part")["room"] = args;
}
network::send_data(data, 0);
}
void chat_handler::do_speak(const std::string& message, bool allies_only)
{
if(message == "" || message == "/") {
return;
}
bool is_command = (message[0] == '/');
bool quoted_command = (is_command && message[1] == ' ');
if(!is_command) {
send_chat_message(message, allies_only);
return;
} else if (quoted_command) {
send_chat_message(std::string(message.begin() + 2, message.end()), allies_only);
return;
}
std::string cmd(message.begin() + 1, message.end());
chat_command_handler cch(*this, allies_only);
cch.dispatch(cmd);
}
void chat_handler::user_relation_changed(const std::string& /*name*/)
{
}
void chat_handler::send_whisper(const std::string& receiver, const std::string& message)
{
config cwhisper, data;
cwhisper["receiver"] = receiver;
cwhisper["message"] = message;
cwhisper["sender"] = preferences::login();
data.add_child("whisper", cwhisper);
network::send_data(data, 0);
}
void chat_handler::add_whisper_sent(const std::string& receiver, const std::string& message)
{
utils::string_map symbols;
symbols["receiver"] = receiver;
add_chat_message(time(nullptr), VGETTEXT("whisper to $receiver", symbols), 0, message);
}
void chat_handler::add_whisper_received(const std::string& sender, const std::string& message)
{
utils::string_map symbols;
symbols["sender"] = sender;
add_chat_message(time(nullptr), VGETTEXT("whisper: $sender", symbols), 0, message);
}
void chat_handler::send_chat_room_message(const std::string& room,
const std::string& message)
{
config cmsg, data;
cmsg["room"] = room;
cmsg["message"] = message;
cmsg["sender"] = preferences::login();
data.add_child("message", cmsg);
network::send_data(data, 0);
}
void chat_handler::add_chat_room_message_sent(const std::string &room, const std::string &message)
{
add_chat_room_message_received(room, preferences::login(), message);
}
void chat_handler::add_chat_room_message_received(const std::string &room,
const std::string &speaker, const std::string &message)
{
add_chat_message(time(nullptr), room + ": " + speaker, 0, message, events::chat_handler::MESSAGE_PRIVATE);
}
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_network_send_req_arg()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
do_network_send();
}
void chat_command_handler::do_room_query_noarg()
{
config data;
config& q = data.add_child("room_query");
q.add_child(get_cmd());
network::send_data(data, 0);
}
void chat_command_handler::do_room_query()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& q = data.add_child("room_query");
q["room"] = get_arg(1);
q.add_child(get_cmd());
network::send_data(data, 0);
}
void chat_command_handler::do_gen_room_query()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& q = data.add_child("room_query");
q["room"] = get_arg(1);
config& c = q.add_child(get_arg(2));
c["value"] = get_data(3);
network::send_data(data, 0);
}
void chat_command_handler::do_whisper()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
chat_handler_.send_whisper(get_arg(1), get_data(2));
chat_handler_.add_whisper_sent(get_arg(1), get_data(2));
}
void chat_command_handler::do_chanmsg()
{
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
chat_handler_.send_chat_room_message(get_arg(1), get_data(2));
chat_handler_.add_chat_room_message_sent(get_arg(1), get_data(2));
}
void chat_command_handler::do_log()
{
chat_handler_.change_logging(get_data());
}
void chat_command_handler::do_ignore()
{
if (get_arg(1).empty()) {
do_display();
} else {
utils::string_map symbols;
symbols["nick"] = get_arg(1);
if (preferences::add_ignore(get_arg(1), get_data(2))) {
print(_("ignores list"), VGETTEXT("Added to ignore list: $nick", symbols));
chat_handler_.user_relation_changed(get_arg(1));
} else {
command_failed(VGETTEXT("Invalid username: $nick", symbols));
}
}
}
void chat_command_handler::do_friend()
{
if (get_arg(1).empty()) {
do_display();
} else {
utils::string_map symbols;
symbols["nick"] = get_arg(1);
if (preferences::add_friend(get_arg(1), get_data(2))) {
print(_("friends list"), VGETTEXT("Added to friends list: $nick", symbols));
chat_handler_.user_relation_changed(get_arg(1));
} else {
command_failed(VGETTEXT("Invalid username: $nick", symbols));
}
}
}
void chat_command_handler::do_remove()
{
for(int i = 1;!get_arg(i).empty();i++){
preferences::remove_acquaintance(get_arg(i));
chat_handler_.user_relation_changed(get_arg(i));
utils::string_map symbols;
symbols["nick"] = get_arg(i);
print(_("friends and ignores list"), VGETTEXT("Removed from list: $nick", symbols));
}
}
void chat_command_handler::do_display()
{
// TODO: add video and game config argument to chat_command_handler?
preferences::show_preferences_dialog(CVideo::get_singleton(),
game_config_manager::get()->game_config(), preferences::VIEW_FRIENDS);
}
void chat_command_handler::do_version() {
print(_("version"), game_config::revision);
}
void chat_command_handler::do_register() {
config data;
config& nickserv = data.add_child("nickserv");
if (get_data(1).empty()) return command_failed_need_arg(1);
config &reg = nickserv.add_child("register");
reg["password"] = get_arg(1);
if(!get_data(2).empty()) {
reg["mail"] = get_arg(2);
}
std::string msg;
if (get_data(2).empty()) {
msg = _("registering with password *** and no email address");
} else {
utils::string_map symbols;
symbols["email"] = get_data(2);
msg = VGETTEXT("registering with password *** and "
"email address $email", symbols);
}
print(_("nick registration"), msg);
network::send_data(data, 0);
}
void chat_command_handler::do_drop() {
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("drop");
print(_("nick registration"), _("dropping your username"));
network::send_data(data, 0);
}
void chat_command_handler::do_set() {
config data;
config& nickserv = data.add_child("nickserv");
if (get_data(1).empty()) return command_failed_need_arg(1);
if (get_data(2).empty()) return command_failed_need_arg(2);
config &set = nickserv.add_child("set");
set["detail"] = get_arg(1);
set["value"] = get_data(2);
utils::string_map symbols;
symbols["var"] = get_arg(1);
symbols["value"] = get_arg(2);
print(_("nick registration"), VGETTEXT("setting $var to $value", symbols));
network::send_data(data, 0);
}
void chat_command_handler::do_info() {
if (get_data(1).empty()) return command_failed_need_arg(1);
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("info")["name"] = get_data(1);
utils::string_map symbols;
symbols["nick"] = get_arg(1);
print(_("nick registration"), VGETTEXT("requesting information for user $nick", symbols));
network::send_data(data, 0);
}
void chat_command_handler::do_details() {
config data;
config& nickserv = data.add_child("nickserv");
nickserv.add_child("details");
network::send_data(data, 0);
}
void menu_handler::send_chat_message(const std::string& message, bool allies_only)
{
config cfg;
@ -2593,7 +1719,7 @@ struct save_id_matches
void console_handler::do_control() {
// :control <side> <nick>
if (network::nconnections() == 0) return;
if (!menu_handler_.pc_.is_networked_mp()) return;
const std::string side = get_arg(1);
const std::string player = get_arg(2);
if(player.empty())
@ -2858,7 +1984,7 @@ void console_handler::do_turn_limit()
}
void console_handler::do_debug() {
if (network::nconnections() == 0 || game_config::mp_debug) {
if (!menu_handler_.pc_.is_networked_mp() || game_config::mp_debug) {
print(get_cmd(), _("Debug mode activated!"));
game_config::debug = true;
} else {
@ -3075,7 +2201,13 @@ void menu_handler::request_control_change ( int side_num, const std::string& pla
} else {
//The server will (or won't because we aren't allowed to change the controller)
//send us a [change_controller] back, which we then handle in playturn.cpp
change_side_controller(side,player);
pc_.send_to_wesnothd(config_of
("change_controller", config_of
("side", side)
("player", player)
)
);
}
}
@ -3090,8 +2222,8 @@ void menu_handler::custom_command()
void menu_handler::ai_formula()
{
if (network::nconnections() == 0) {
textbox_info_.show(gui::TEXTBOX_AI, translation::sgettext("prompt^Command:"), "", false, *gui_);
if (!pc_.is_networked_mp()) {
textbox_info_.show(gui::TEXTBOX_AI, translation::sgettext("prompt^Command:"), "", false, *gui_);
}
}
@ -3100,13 +2232,9 @@ void menu_handler::clear_messages()
gui_->get_chat_manager().clear_chat_messages(); // also clear debug-messages and WML-error-messages
}
void menu_handler::change_side_controller(const std::string& side, const std::string& player)
void menu_handler::send_to_server(const config& cfg)
{
config cfg;
config& change = cfg.add_child("change_controller");
change["side"] = side;
change["player"] = player;
network::send_data(cfg, 0);
pc_.send_to_wesnothd(cfg);
}
} // end namespace events

View file

@ -103,6 +103,7 @@ public:
void do_search(const std::string& new_search);
void do_command(const std::string &str);
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler);
void send_to_server(const config& cfg) override;
protected:
void add_chat_message(const time_t& time, const std::string& speaker,
int side, const std::string& message,
@ -116,7 +117,6 @@ private:
//void do_speak(const std::string& message, bool allies_only);
// std::vector<std::string> create_unit_table(const statistics::stats::str_int_map& m,unsigned int team);
bool has_friends() const;
static void change_side_controller(const std::string& side, const std::string& player);
void scenario_settings_table(int selected=0);
game_display* gui_;

View file

@ -16,7 +16,6 @@
#include "game_data.hpp"
#include "log.hpp"
#include "network.hpp"
#include "persist_context.hpp"
#include "persist_manager.hpp"
#include "persist_var.hpp"
@ -137,7 +136,7 @@ void verify_and_get_global_variable(const vconfig &pcfg)
ERR_PERSIST << "[get_global_variable] missing attribute \"namespace\"";
valid = false;
}
if (network::nconnections() != 0) {
if (resources::controller->is_networked_mp()) {
DBG_PERSIST << "verify_and_get_global_variable with from_global=" << pcfg["from_global"] << " from side " << pcfg["side"] << "\n";
config::attribute_value pcfg_side = pcfg["side"];
int side = (pcfg_side.str() == "global" || pcfg_side.empty()) ? resources::controller->current_side() : pcfg_side.to_int();
@ -172,7 +171,7 @@ void verify_and_set_global_variable(const vconfig &pcfg)
ERR_PERSIST << "[set_global_variable] missing attribute \"namespace\" and no global namespace provided.";
valid = false;
}
if (network::nconnections() != 0) {
if (resources::controller->is_networked_mp()) {
config::attribute_value pcfg_side = pcfg["side"];
int side = pcfg_side;
//Check side matching only if the side is not "global" or empty.
@ -211,7 +210,7 @@ void verify_and_clear_global_variable(const vconfig &pcfg)
ERR_PERSIST << "[clear_global_variable] missing attribute \"namespace\" and no global namespace provided.";
valid = false;
}
if (network::nconnections() != 0) {
if (resources::controller->is_networked_mp()) {
config::attribute_value pcfg_side = pcfg["side"];
const int side = pcfg_side.to_int();
//Check side matching only if the side is not "global" or empty.

View file

@ -265,6 +265,9 @@ public:
* Changes the UI for this client to the passed side index.
*/
void update_gui_to_player(const int team_index, const bool observe = false);
virtual bool is_networked_mp() const { return false; }
virtual void send_to_wesnothd(const config&, const std::string& = "unknown") const { }
protected:
struct scoped_savegame_snapshot
{

View file

@ -359,7 +359,7 @@ void playmp_controller::process_oos(const std::string& err_msg) const {
config& info = cfg.add_child("info");
info["type"] = "termination";
info["condition"] = "out of sync";
network::send_data(cfg, 0);
send_to_wesnothd(cfg);
std::stringstream temp_buf;
std::vector<std::string> err_lines = utils::split(err_msg,'\n');
@ -477,3 +477,12 @@ void playmp_controller::process_network_data()
replay::process_error("Received unexpected next_scenario during the game");
}
}
bool playmp_controller::is_networked_mp() const
{
return network::nconnections() != 0;
}
void playmp_controller::send_to_wesnothd(const config& cfg, const std::string& packet_type) const
{
network::send_data(cfg, 0, packet_type);
}

View file

@ -37,6 +37,9 @@ public:
void send_user_choice();
class hotkey_handler;
bool is_networked_mp() const override;
void send_to_wesnothd(const config& cfg, const std::string& packet_type = "unknown") const override;
protected:
virtual void handle_generic_event(const std::string& name);

View file

@ -138,7 +138,7 @@ void playsingle_controller::play_scenario_init()
saved_game_.replay_start() = to_config();
}
start_game();
if( saved_game_.classification().random_mode != "" && (network::nconnections() != 0)) {
if( saved_game_.classification().random_mode != "" && is_networked_mp()) {
// This won't cause errors later but we should notify the user about it in case he didn't knew it.
gui2::show_transient_message(
gui_->video(),
@ -299,7 +299,7 @@ LEVEL_RESULT playsingle_controller::play_scenario(const config& level)
}
// If we're a player, and the result is victory/defeat, then send
// a message to notify the server of the reason for the game ending.
network::send_data(config_of
send_to_wesnothd(config_of
("info", config_of
("type", "termination")
("condition", "game over")

View file

@ -379,7 +379,7 @@ void turn_info::change_side_controller(int side, const std::string& player)
config& change = cfg.add_child("change_controller");
change["side"] = side;
change["player"] = player;
network::send_data(cfg, 0);
resources::controller->send_to_wesnothd(cfg);
}
turn_info::PROCESS_DATA_RESULT turn_info::replay_to_process_data_result(REPLAY_RETURN replayreturn)

View file

@ -882,27 +882,27 @@ replay_network_sender::~replay_network_sender()
void replay_network_sender::sync_non_undoable()
{
if(network::nconnections() > 0) {
if(resources::controller->is_networked_mp()) {
resources::whiteboard->send_network_data();
config cfg;
const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands(),replay::NON_UNDO_DATA));
if(data.empty() == false) {
network::send_data(cfg, 0);
resources::controller->send_to_wesnothd(cfg);
}
}
}
void replay_network_sender::commit_and_sync()
{
if(network::nconnections() > 0) {
if(resources::controller->is_networked_mp()) {
resources::whiteboard->send_network_data();
config cfg;
const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands()));
if(data.empty() == false) {
network::send_data(cfg, 0);
resources::controller->send_to_wesnothd(data);
}
upto_ = obj_.ncommands();

View file

@ -25,7 +25,8 @@
#include "variable.hpp"
#include "team.hpp"
#include "serialization/string_utils.hpp"
#include "network.hpp"
#include "play_controller.hpp"
#include "resources.hpp"
#include "synced_context.hpp"
#include "units/unit.hpp"
#include "units/filter.hpp"
@ -225,7 +226,7 @@ bool side_filter::match_internal(const team &t) const
const config::attribute_value cfg_controller = cfg_["controller"];
if (!cfg_controller.blank())
{
if (network::nconnections() > 0 && synced_context::is_synced()) {
if (resources::controller->is_networked_mp() && synced_context::is_synced()) {
ERR_NG << "ignoring controller= in SSF due to danger of OOS errors" << std::endl;
}
else {

View file

@ -292,10 +292,12 @@ boost::shared_ptr<random_new::rng> synced_context::get_rng_for_action()
void synced_context::server_choice::send_request() const
{
network::send_data(config_of("request_choice", config_of
("request_id", resources::controller->get_server_request_number())
(name(), request())
));
resources::controller->send_to_wesnothd(config_of
("request_choice", config_of
("request_id", resources::controller->get_server_request_number())
(name(), request())
)
);
}
@ -305,7 +307,7 @@ config synced_context::ask_server_choice(const server_choice& sch)
set_is_simultaneously();
resources::controller->increase_server_request_number();
assert(is_synced());
const bool is_mp_game = network::nconnections() != 0;
const bool is_mp_game = resources::controller->is_networked_mp();
bool did_require = false;
DBG_REPLAY << "ask_server for random_seed\n";

View file

@ -20,7 +20,6 @@
#include "game_display.hpp"
#include "game_data.hpp"
#include "log.hpp"
#include "network.hpp"
#include "play_controller.hpp"
#include "synced_context.hpp"
#include "replay.hpp"
@ -151,7 +150,7 @@ config mp_sync::get_user_choice(const std::string &name, const mp_sync::user_cho
{
const bool is_too_early = resources::gamedata->phase() != game_data::START && resources::gamedata->phase() != game_data::PLAY;
const bool is_synced = synced_context::is_synced();
const bool is_mp_game = network::nconnections() != 0;//Only used in debugging output below
const bool is_mp_game = resources::controller->is_networked_mp();//Only used in debugging output below
const int max_side = static_cast<int>(resources::teams->size());
bool is_side_null_controlled;

View file

@ -32,7 +32,6 @@
#include "units/types.hpp"
#include "synced_context.hpp"
#include "whiteboard/side_actions.hpp"
#include "network.hpp"
#include "config_assign.hpp"
#include "serialization/string_utils.hpp"

View file

@ -26,7 +26,6 @@
#include "units/abilities.hpp"
#include "wml_exception.hpp"
#include "resources.hpp"
#include "network.hpp"
#include "config_assign.hpp"
#include <boost/range/adaptors.hpp>
@ -403,12 +402,11 @@ void tod_manager::update_server_information() const
//NOTE: The current implementation does not guarnateee that the server gets informed
// about those changes in 100% of cases. But that is ok because the information is only
// used to display the turn limit in the lobby (as opposed to things that cause OOS).
network::send_data(config_of
resources::controller->send_to_wesnothd(config_of
("change_turns_wml", config_of
("current", turn_)
("max", num_turns_)
),
0
)
);
}
}

View file

@ -41,7 +41,6 @@
#include "gettext.hpp"
#include "gui/dialogs/simple_item_selector.hpp"
#include "key.hpp"
#include "network.hpp"
#include "pathfind/pathfind.hpp"
#include "play_controller.hpp"
#include "resources.hpp"
@ -613,7 +612,7 @@ void manager::send_network_data()
buf_cfg = config();
network::send_data(packet,0,"whiteboard");
resources::controller->send_to_wesnothd(packet, "whiteboard");
size_t count = wb_cfg.child_count("net_cmd");
LOG_WB << "Side " << (team_index+1) << " sent wb data (" << count << " cmds).\n";