Replace MAKE_ENUM of CONTROLLER with something easier to use.

CONTROLLER is now defined in a single small header that can be easily read without needing to look through a bunch of Boost macros to figure out how it all works. Using the string values of the enum is now also much simpler since they're just constants of the side_controller class.
This commit is contained in:
Pentarctagon 2022-01-05 09:31:29 -06:00 committed by Pentarctagon
parent e359635bb2
commit 263e9d7d4f
24 changed files with 231 additions and 144 deletions

View file

@ -528,7 +528,7 @@ config map_context::to_config()
side["side"] = side_num;
side["hidden"] = t->hidden();
side["controller"] = t->controller();
side["controller"] = side_controller::get_string(t->controller());
side["no_leader"] = t->no_leader();
side["team_name"] = t->team_name();

View file

@ -22,11 +22,12 @@
#include "mp_game_settings.hpp"
#include "overlay.hpp"
#include "sound_music_track.hpp"
#include "string_enums/side_controller.hpp"
#include "team.hpp"
#include "tod_manager.hpp"
#include "units/map.hpp"
#include <optional>
#include <optional>
#include <vector>
class game_config_view;
@ -45,7 +46,7 @@ struct editor_team_info {
bool fog;
bool shroud;
team::SHARE_VISION share_vision;
team::CONTROLLER controller;
side_controller::type controller;
bool no_leader;
bool hidden;
};

View file

@ -218,7 +218,7 @@ unit* game_board::get_visible_unit(const map_location& loc, const team& current_
return &*ui;
}
void game_board::side_drop_to(int side_num, team::CONTROLLER ctrl, team::PROXY_CONTROLLER proxy)
void game_board::side_drop_to(int side_num, side_controller::type ctrl, team::PROXY_CONTROLLER proxy)
{
team& tm = get_team(side_num);
@ -226,11 +226,11 @@ void game_board::side_drop_to(int side_num, team::CONTROLLER ctrl, team::PROXY_C
tm.change_proxy(proxy);
tm.set_local(true);
tm.set_current_player(ctrl.to_string() + std::to_string(side_num));
tm.set_current_player(side_controller::get_string(ctrl) + std::to_string(side_num));
unit_map::iterator leader = units_.find_leader(side_num);
if(leader.valid()) {
leader->rename(ctrl.to_string() + std::to_string(side_num));
leader->rename(side_controller::get_string(ctrl) + std::to_string(side_num));
}
}
@ -242,10 +242,10 @@ void game_board::side_change_controller(
tm.set_local(is_local);
// only changing the type of controller
if(controller_type == team::CONTROLLER::enum_to_string(team::CONTROLLER::AI) && !tm.is_ai()) {
if(controller_type == side_controller::ai && !tm.is_ai()) {
tm.make_ai();
return;
} else if(controller_type == team::CONTROLLER::enum_to_string(team::CONTROLLER::HUMAN) && !tm.is_human()) {
} else if(controller_type == side_controller::human && !tm.is_human()) {
tm.make_human();
return;
}

View file

@ -21,8 +21,9 @@
#include "terrain/type_data.hpp"
#include "units/map.hpp"
#include "units/id.hpp"
#include <optional>
#include "string_enums/side_controller.hpp"
#include <optional>
#include <set>
#include <vector>
@ -152,7 +153,7 @@ public:
// Manipulator from playturn
void side_drop_to (int side_num, team::CONTROLLER ctrl, team::PROXY_CONTROLLER proxy = team::PROXY_CONTROLLER::PROXY_HUMAN);
void side_drop_to (int side_num, side_controller::type ctrl, team::PROXY_CONTROLLER proxy = team::PROXY_CONTROLLER::PROXY_HUMAN);
void side_change_controller (int side_num, bool is_local, const std::string& pname, const std::string& controller_type);
// Manipulator from actionwml

View file

@ -29,6 +29,7 @@
#include "tod_manager.hpp"
#include "team.hpp"
#include "wesnothd_connection.hpp"
#include "string_enums/side_controller.hpp"
#include <array>
#include <cstdlib>
@ -50,11 +51,11 @@ static lg::log_domain log_network("network");
namespace
{
const std::array<std::string, 5> controller_names {{
"human",
"human",
"ai",
"null",
"reserved"
side_controller::human,
side_controller::human,
side_controller::ai,
side_controller::none,
side_controller::reserved
}};
const std::set<std::string> children_to_swap {
@ -877,7 +878,7 @@ side_engine::side_engine(const config& cfg, connect_engine& parent_engine, const
ERR_MP << "controller=<number> is deperecated\n";
}
if(cfg_["controller"] != "human" && cfg_["controller"] != "ai" && cfg_["controller"] != "null") {
if(cfg_["controller"] != side_controller::human && cfg_["controller"] != side_controller::ai && cfg_["controller"] != side_controller::none) {
//an invalid controller type was specified. Remove it to prevent asertion failures later.
cfg_.remove_attribute("controller");
}
@ -886,12 +887,12 @@ side_engine::side_engine(const config& cfg, connect_engine& parent_engine, const
// Tweak the controllers.
if(parent_.state_.classification().is_scenario() && cfg_["controller"].blank()) {
cfg_["controller"] = "ai";
cfg_["controller"] = side_controller::ai;
}
if(cfg_["controller"] == "null") {
if(cfg_["controller"] == side_controller::none) {
set_controller(CNTR_EMPTY);
} else if(cfg_["controller"] == "ai") {
} else if(cfg_["controller"] == side_controller::ai) {
set_controller(CNTR_COMPUTER);
} else if(parent_.default_controller_ == CNTR_NETWORK && !reserved_for_.empty()) {
// Reserve a side for "current_player", unless the side
@ -1249,20 +1250,20 @@ void side_engine::update_controller_options()
// Default options.
if(parent_.mp_metadata_) {
add_controller_option(CNTR_NETWORK, _("Network Player"), "human");
add_controller_option(CNTR_NETWORK, _("Network Player"), side_controller::human);
}
add_controller_option(CNTR_LOCAL, _("Local Player"), "human");
add_controller_option(CNTR_COMPUTER, _("Computer Player"), "ai");
add_controller_option(CNTR_EMPTY, _("Nobody"), "null");
add_controller_option(CNTR_LOCAL, _("Local Player"), side_controller::human);
add_controller_option(CNTR_COMPUTER, _("Computer Player"), side_controller::ai);
add_controller_option(CNTR_EMPTY, _("Nobody"), side_controller::none);
if(!reserved_for_.empty()) {
add_controller_option(CNTR_RESERVED, _("Reserved"), "human");
add_controller_option(CNTR_RESERVED, _("Reserved"), side_controller::human);
}
// Connected users.
for(const std::string& user : parent_.connected_users()) {
add_controller_option(parent_.default_controller_, user, "human");
add_controller_option(parent_.default_controller_, user, side_controller::human);
}
update_current_controller_index();
@ -1322,11 +1323,11 @@ void side_engine::set_controller_commandline(const std::string& controller_name)
{
set_controller(CNTR_LOCAL);
if(controller_name == "ai") {
if(controller_name == side_controller::ai) {
set_controller(CNTR_COMPUTER);
}
if(controller_name == "null") {
if(controller_name == side_controller::none) {
set_controller(CNTR_EMPTY);
}

View file

@ -27,6 +27,7 @@
#include "minimap.hpp"
#include "saved_game.hpp"
#include "wml_exception.hpp"
#include "string_enums/side_controller.hpp"
#include "serialization/preprocessor.hpp"
#include "serialization/parser.hpp"
@ -110,7 +111,7 @@ void scenario::set_sides()
side["side"] = pos + 1;
side["team_name"] = "Team " + std::to_string(pos + 1);
side["canrecruit"] = true;
side["controller"] = "human";
side["controller"] = side_controller::human;
}
}

View file

@ -33,6 +33,7 @@
#include "units/unit.hpp"
#include "whiteboard/manager.hpp"
#include "gui/dialogs/loading_screen.hpp"
#include "string_enums/side_controller.hpp"
#include <functional>
#include <SDL2/SDL_timer.h>
@ -210,7 +211,7 @@ void game_state::init(const config& level, play_controller & pc)
{
if (first_human_team_ == -1) {
const std::string &controller = side["controller"];
if (controller == "human" && side["is_local"].to_bool(true)) {
if (controller == side_controller::human && side["is_local"].to_bool(true)) {
first_human_team_ = team_num;
}
}
@ -459,7 +460,7 @@ void game_state::add_side_wml(config cfg)
{
cfg["side"] = board_.teams().size() + 1;
//if we want to also allow setting the controller we must update the server code.
cfg["controller"] = "null";
cfg["controller"] = side_controller::none;
//TODO: is this it? are there caches which must be cleared?
board_.teams().emplace_back();
board_.teams().back().build(cfg, board_.map(), cfg["gold"].to_int());

View file

@ -52,9 +52,9 @@ editor_edit_side::editor_edit_side(editor::editor_team_info& info)
void editor_edit_side::pre_show(window& window)
{
controller_group.add_member(find_widget<toggle_button>(&window, "controller_human", false, true), team::CONTROLLER::HUMAN);
controller_group.add_member(find_widget<toggle_button>(&window, "controller_ai", false, true), team::CONTROLLER::AI);
controller_group.add_member(find_widget<toggle_button>(&window, "controller_null", false, true), team::CONTROLLER::EMPTY);
controller_group.add_member(find_widget<toggle_button>(&window, "controller_human", false, true), side_controller::type::HUMAN);
controller_group.add_member(find_widget<toggle_button>(&window, "controller_ai", false, true), side_controller::type::AI);
controller_group.add_member(find_widget<toggle_button>(&window, "controller_null", false, true), side_controller::type::NONE);
controller_group.set_member_states(controller_);

View file

@ -19,6 +19,7 @@
#include "gui/dialogs/modal_dialog.hpp"
#include "gui/widgets/group.hpp"
#include "team.hpp"
#include "string_enums/side_controller.hpp"
namespace gui2
{
@ -48,8 +49,8 @@ private:
virtual void pre_show(window& window) override;
virtual void post_show(window& window) override;
team::CONTROLLER& controller_;
group<team::CONTROLLER> controller_group;
side_controller::type& controller_;
group<side_controller::type> controller_group;
team::SHARE_VISION& share_vision_;
group<team::SHARE_VISION> vision_group;

View file

@ -67,8 +67,8 @@ unit_const_ptr game_stats::get_leader(const int side)
static std::string controller_name(const team& t)
{
static const std::array<t_string, 3> names {{_("controller^Human"), _("controller^AI"), _("controller^Idle")}};
return "<span color='#808080'><small>" + names[t.controller().v] + "</small></span>";
static const std::array<t_string, side_controller::size()> names {{_("controller^Idle"), _("controller^Human"), _("controller^AI"), _("controller^Reserved")}};
return "<span color='#808080'><small>" + names[static_cast<int>(t.controller())] + "</small></span>";
}
void game_stats::pre_show(window& window)

View file

@ -47,6 +47,7 @@
#include "units/types.hpp"
#include "utils/scope_exit.hpp"
#include "wesnothd_connection.hpp"
#include "string_enums/side_controller.hpp"
static lg::log_domain log_mp_connect_engine("mp/connect/engine");
#define DBG_MP LOG_STREAM(debug, log_mp_connect_engine)
@ -165,13 +166,13 @@ bool mp_join_game::fetch_game_config()
for(const config& side : get_scenario().child_range("side")) {
// TODO: it can happen that the scenario specifies that the controller
// of a side should also gain control of another side.
if(side["controller"] == "reserved" && side["current_player"] == preferences::login()) {
if(side["controller"] == side_controller::reserved && side["current_player"] == preferences::login()) {
side_choice = &side;
side_num_choice = side_num_counter;
break;
}
if(side["controller"] == "human" && side["player_id"].empty()) {
if(side["controller"] == side_controller::human && side["player_id"].empty()) {
if(!side_choice) { // Found the first empty side
side_choice = &side;
side_num_choice = side_num_counter;
@ -218,15 +219,15 @@ static std::string generate_user_description(const config& side)
const std::string reservation = side["current_player"].str();
const std::string owner = side["player_id"].str();
if(controller_type == "ai") {
if(controller_type == side_controller::ai) {
return _("Computer Player");
} else if(controller_type == "null") {
} else if(controller_type == side_controller::none) {
return _("Empty slot");
} else if(controller_type == "reserved") {
} else if(controller_type == side_controller::reserved) {
return VGETTEXT("Reserved for $playername", {{"playername", reservation}});
} else if(owner.empty()) {
return _("Vacant slot");
} else if(controller_type == "human" || controller_type == "network") {
} else if(controller_type == side_controller::human) {
return owner;
} else {
return _("empty");

View file

@ -1490,8 +1490,6 @@ void console_handler::do_droid()
const bool is_droid = menu_handler_.board().get_team(side).is_droid();
const bool is_proxy_human = menu_handler_.board().get_team(side).is_proxy_human();
const bool is_ai = menu_handler_.board().get_team(side).is_ai();
const std::string human = team::CONTROLLER::enum_to_string(team::CONTROLLER::HUMAN);
const std::string ai = team::CONTROLLER::enum_to_string(team::CONTROLLER::AI);
if(action == "on") {
if(is_ai && !is_your_turn) {
@ -1503,7 +1501,7 @@ void console_handler::do_droid()
menu_handler_.board().get_team(side).make_droid();
changed = true;
if(is_ai) {
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", human}});
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", side_controller::human}});
}
print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
} else {
@ -1519,7 +1517,7 @@ void console_handler::do_droid()
menu_handler_.board().get_team(side).make_proxy_human();
changed = true;
if(is_ai) {
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", human}});
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", side_controller::human}});
}
print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
} else {
@ -1535,7 +1533,7 @@ void console_handler::do_droid()
menu_handler_.board().get_team(side).make_droid();
changed = true;
if(is_human || is_proxy_human) {
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", ai}});
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", side_controller::ai}});
}
print(get_cmd(), VGETTEXT("Side '$side' controller is now fully controlled by: AI.", symbols));
} else {
@ -1551,7 +1549,7 @@ void console_handler::do_droid()
menu_handler_.board().get_team(side).make_proxy_human();
changed = true;
if(is_ai) {
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", human}});
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", side_controller::human}});
}
print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: human.", symbols));
} else {
@ -1559,7 +1557,7 @@ void console_handler::do_droid()
menu_handler_.board().get_team(side).make_droid();
changed = true;
if(is_ai) {
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", human}});
menu_handler_.pc_.send_to_wesnothd(config {"change_controller", config {"side", side, "player", preferences::login(), "to", side_controller::human}});
}
print(get_cmd(), VGETTEXT("Side '$side' controller is now controlled by: AI.", symbols));
}
@ -1715,7 +1713,7 @@ void console_handler::do_controller()
return;
}
std::string report = menu_handler_.board().get_team(side_num).controller().to_string();
std::string report = side_controller::get_string(menu_handler_.board().get_team(side_num).controller());
if(!menu_handler_.board().get_team(side_num).is_proxy_human()) {
report += " (" + menu_handler_.board().get_team(side_num).proxy_controller().to_string() + ")";
}

View file

@ -438,7 +438,7 @@ void playsingle_controller::play_side_impl()
}
} else {
// we should have skipped over empty controllers before so this shouldn't be possible
ERR_NG << "Found invalid side controller " << current_team().controller().to_string() << " ("
ERR_NG << "Found invalid side controller " << side_controller::get_string(current_team().controller()) << " ("
<< current_team().proxy_controller().to_string() << ") for side " << current_team().side() << "\n";
}
}

View file

@ -232,19 +232,19 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
throw ingame_wesnothd_error("");
}
team::CONTROLLER ctrl;
if(!ctrl.parse(side_drop_c["controller"])) {
auto ctrl = side_controller::get_enum(side_drop_c["controller"]);
if(!ctrl) {
ERR_NW << "unknown controller type issued from server on side drop: " << side_drop_c["controller"] << std::endl;
throw ingame_wesnothd_error("");
}
if (ctrl == team::CONTROLLER::AI) {
resources::gameboard->side_drop_to(side_drop, ctrl);
if (ctrl == side_controller::type::AI) {
resources::gameboard->side_drop_to(side_drop, *ctrl);
return restart ? PROCESS_RESTART_TURN:PROCESS_CONTINUE;
}
//null controlled side cannot be dropped because they aren't controlled by anyone.
else if (ctrl != team::CONTROLLER::HUMAN) {
ERR_NW << "unknown controller type issued from server on side drop: " << ctrl.to_cstring() << std::endl;
else if (ctrl != side_controller::type::HUMAN) {
ERR_NW << "unknown controller type issued from server on side drop: " << side_controller::get_string(*ctrl) << std::endl;
throw ingame_wesnothd_error("");
}
@ -317,7 +317,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
{
// Server thinks this side is ours now so in case of error transferring side we have to make local state to same as what server thinks it is.
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
resources::gameboard->side_drop_to(side_drop, side_controller::type::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
}
if (action < first_observer_option_idx) {
@ -336,17 +336,17 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
switch(action) {
case 0:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_AI);
resources::gameboard->side_drop_to(side_drop, side_controller::type::HUMAN, team::PROXY_CONTROLLER::PROXY_AI);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 1:
resources::controller->on_not_observer();
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_HUMAN);
resources::gameboard->side_drop_to(side_drop, side_controller::type::HUMAN, team::PROXY_CONTROLLER::PROXY_HUMAN);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
case 2:
resources::gameboard->side_drop_to(side_drop, team::CONTROLLER::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
resources::gameboard->side_drop_to(side_drop, side_controller::type::HUMAN, team::PROXY_CONTROLLER::PROXY_IDLE);
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;

View file

@ -457,7 +457,7 @@ void extract_summary_from_config(config& cfg_save, config& cfg_summary)
int gold = side["gold"];
int units = 0, recall_units = 0;
if(side["controller"] != team::CONTROLLER::enum_to_string(team::CONTROLLER::HUMAN)) {
if(side["controller"] != side_controller::human) {
continue;
}

View file

@ -76,6 +76,7 @@
#include "statistics.hpp"
#include "variable.hpp" // for config_variable_set
#include "variable_info.hpp"
#include "string_enums/side_controller.hpp"
#include <cassert>
#include <iomanip>
@ -688,7 +689,7 @@ void saved_game::cancel_orders()
// for humans "goto_x/y" is used for multi-turn-moves
// for the ai "goto_x/y" is a way for wml to order the ai to move a unit to a certain place.
// we want to cancel human order but not to break wml.
if(side["controller"] != "human" && side["controller"] != "network") {
if(side["controller"] != side_controller::human) {
continue;
}
@ -703,14 +704,6 @@ void saved_game::unify_controllers()
{
for(config& side : starting_point_.child_range("side")) {
side.remove_attribute("is_local");
//TODO: the old code below is probably not needed anymore
if(side["controller"] == "network") {
side["controller"] = "human";
}
if(side["controller"] == "network_ai") {
side["controller"] = "ai";
}
}
}

View file

@ -80,7 +80,7 @@ static int impl_side_get(lua_State *L)
return_string_attrib("faction", t.faction());
return_tstring_attrib("faction_name", t.faction_name());
return_string_attrib("color", t.color());
return_cstring_attrib("controller", t.controller().to_string().c_str());
return_string_attrib("controller", side_controller::get_string(t.controller()));
return_bool_attrib("is_local", t.is_local());
return_string_attrib("defeat_condition", t.defeat_condition().to_string());
return_string_attrib("share_vision", t.share_vision().to_string());

View file

@ -203,7 +203,7 @@ void game::perform_controller_tweaks()
for(unsigned side_index = 0; side_index < sides.size(); ++side_index) {
simple_wml::node& side = *sides[side_index];
if(side["controller"] != "null") {
if(side["controller"] != side_controller::none) {
if(!sides_[side_index]) {
sides_[side_index] = owner_;
std::stringstream msg;
@ -284,7 +284,7 @@ void game::start_game(player_iterator starter)
for(unsigned side_index = 0; side_index < sides.size(); ++side_index) {
simple_wml::node& side = *sides[side_index];
if(side["controller"] != "null") {
if(side["controller"] != side_controller::none) {
if(side_index >= sides_.size()) {
continue;
}
@ -360,7 +360,7 @@ bool game::take_side(player_iterator user)
const simple_wml::node::child_list& sides = get_sides_list();
for(const simple_wml::node* side : sides) {
if(((*side)["controller"] == "human" || (*side)["controller"] == "reserved")
if(((*side)["controller"] == side_controller::human || (*side)["controller"] == side_controller::reserved)
&& (*side)["current_player"] == user->name().c_str()) {
if(send_taken_side(cfg, side)) {
@ -371,7 +371,7 @@ bool game::take_side(player_iterator user)
// If there was no fitting side just take the first available.
for(const simple_wml::node* side : sides) {
if((*side)["controller"] == "human") {
if((*side)["controller"] == side_controller::human) {
if(send_taken_side(cfg, side)) {
return true;
}
@ -434,19 +434,24 @@ void game::update_side_data()
const simple_wml::string_span& player_id = (*side)["player_id"];
const simple_wml::string_span& controller = (*side)["controller"];
auto type = side_controller::get_enum(controller.to_string());
if(player_id == iter->info().name().c_str()) {
// We found invalid [side] data. Some message would be cool.
if(controller != "human" && controller != "ai") {
if(controller != side_controller::human && controller != side_controller::ai) {
continue;
}
side_controllers_[side_index].parse(controller);
if(type) {
side_controllers_[side_index] = *type;
}
sides_[side_index] = iter;
side_found = true;
} else if(iter == owner_ && (controller == "null")) {
} else if(iter == owner_ && controller == side_controller::none) {
// the *user == owner_ check has no effect,
// it's just an optimisation so that we only do this once.
side_controllers_[side_index].parse(controller);
if(type) {
side_controllers_[side_index] = *type;
}
}
}
@ -522,13 +527,14 @@ void game::transfer_side_control(player_iterator player, const simple_wml::node&
// if the player is unchanged and the controller type (human or ai) is also unchanged then nothing to do
// else only need to change the controller type rather than the player who controls the side
// :droid provides a valid controller_type; :control provides nothing since it's only tranferring control between players regardless of type
if(controller_type == "" || CONTROLLER::string_to_enum(controller_type) == side_controllers_[side_num - 1]) {
auto type = side_controller::get_enum(controller_type);
if(!type || type == side_controllers_[side_num - 1]) {
std::stringstream msg;
msg << "Side " << side_num << " is already controlled by " << newplayer_name << ".";
send_server_message(msg.str(), player);
return;
} else {
side_controllers_[side_num - 1] = CONTROLLER::string_to_enum(controller_type);
side_controllers_[side_num - 1] = *side_controller::get_enum(controller_type);
change_controller_type(side_num - 1, *newplayer, (*newplayer)->info().name());
return;
}
@ -572,7 +578,7 @@ void game::change_controller(
const std::string& side = lexical_cast_default<std::string, std::size_t>(side_index + 1);
sides_[side_index] = player;
if(player_left && side_controllers_[side_index] == CONTROLLER::AI) {
if(player_left && side_controllers_[side_index] == side_controller::type::AI) {
// Automatic AI side transfer.
} else {
if(started_) {
@ -605,7 +611,7 @@ std::unique_ptr<simple_wml::document> game::change_controller_type(const std::si
change.set_attr_dup("side", side.c_str());
change.set_attr_dup("player", player_name.c_str());
change.set_attr_dup("controller", side_controllers_[side_index].to_cstring());
change.set_attr_dup("controller", side_controller::get_string(side_controllers_[side_index]).c_str());
change.set_attr("is_local", "no");
send_data(response, player);
@ -634,7 +640,7 @@ bool game::describe_slots()
int i = 0;
for(const simple_wml::node* side : get_sides_list()) {
if(((*side)["allow_player"].to_bool(true) == false) || (*side)["controller"] == "null") {
if(((*side)["allow_player"].to_bool(true) == false) || (*side)["controller"] == side_controller::none) {
num_sides--;
} else if(!sides_[i]) {
++available_slots;
@ -1126,23 +1132,23 @@ void game::handle_random_choice()
void game::handle_add_side_wml()
{
++nsides_;
side_controllers_.push_back(CONTROLLER::EMPTY);
side_controllers_.push_back(side_controller::type::NONE);
sides_.emplace_back();
}
void game::handle_controller_choice(const simple_wml::node& req)
{
const std::size_t side_index = req["side"].to_int() - 1;
CONTROLLER new_controller;
CONTROLLER old_controller;
auto new_controller = side_controller::get_enum(req["new_controller"].to_string());
auto old_controller = side_controller::get_enum(req["old_controller"].to_string());
if(!new_controller.parse(req["new_controller"])) {
if(!new_controller) {
send_and_record_server_message(
"Could not handle [request_choice] [change_controller] with invalid controller '" + req["new_controller"].to_string() + "'");
return;
}
if(!old_controller.parse(req["old_controller"])) {
if(!old_controller) {
send_and_record_server_message(
"Could not handle [request_choice] [change_controller] with invalid controller '" + req["old_controller"].to_string() + "'");
return;
@ -1150,7 +1156,7 @@ void game::handle_controller_choice(const simple_wml::node& req)
if(old_controller != this->side_controllers_[side_index]) {
send_and_record_server_message(
"Found unexpected old_controller= '" + old_controller.to_string() + "' in [request_choice] [change_controller]");
"Found unexpected old_controller= '" + side_controller::get_string(*old_controller) + "' in [request_choice] [change_controller]");
}
if(side_index >= sides_.size()) {
@ -1159,8 +1165,8 @@ void game::handle_controller_choice(const simple_wml::node& req)
return;
}
const bool was_null = this->side_controllers_[side_index] == CONTROLLER::EMPTY;
const bool becomes_null = new_controller == CONTROLLER::EMPTY;
const bool was_null = this->side_controllers_[side_index] == side_controller::type::NONE;
const bool becomes_null = new_controller == side_controller::type::NONE;
if(was_null) {
assert(!sides_[side_index]);
@ -1171,14 +1177,14 @@ void game::handle_controller_choice(const simple_wml::node& req)
sides_[side_index].reset();
}
side_controllers_[side_index] = new_controller;
side_controllers_[side_index] = *new_controller;
auto mdata = std::make_unique<simple_wml::document>();
simple_wml::node& turn = mdata->root().add_child("turn");
simple_wml::node& command = turn.add_child("command");
simple_wml::node& change_controller_wml = command.add_child("change_controller_wml");
change_controller_wml.set_attr_dup("controller", new_controller.to_cstring());
change_controller_wml.set_attr_dup("controller", side_controller::get_string(*new_controller).c_str());
change_controller_wml.set_attr("is_local", "yes");
command.set_attr("from_side", "server");
@ -1304,7 +1310,7 @@ bool game::end_turn(int new_side)
}
// Skip over empty sides.
for(int i = 0; i < nsides_ && side_controllers_[current_side()] == CONTROLLER::EMPTY; ++i) {
for(int i = 0; i < nsides_ && side_controllers_[current_side()] == side_controller::type::NONE; ++i) {
++current_side_index_;
}
@ -1495,7 +1501,7 @@ bool game::remove_player(player_iterator player, const bool disconnect, const bo
continue;
}
if(side_controllers_[side_index] == CONTROLLER::AI) {
if(side_controllers_[side_index] == side_controller::type::AI) {
ai_transfer = true;
}
@ -1517,7 +1523,7 @@ bool game::remove_player(player_iterator player, const bool disconnect, const bo
auto& node_side_drop = drop.root().add_child("side_drop");
node_side_drop.set_attr_dup("side_num", side_drop.c_str());
node_side_drop.set_attr_dup("controller", side_controllers_[side_index].to_cstring());
node_side_drop.set_attr_dup("controller", side_controller::get_string(side_controllers_[side_index]).c_str());
DBG_GAME << "*** sending side drop: \n" << drop.output() << std::endl;
@ -1896,7 +1902,7 @@ std::string game::debug_sides_info() const
result
<< "side " << (*s)["side"].to_int()
<< " :\t" << (*s)["controller"].to_string()
<< "\t, " << side_controllers_[(*s)["side"].to_int() - 1].to_cstring()
<< "\t, " << side_controller::get_string(side_controllers_[(*s)["side"].to_int() - 1])
<< "\t( " << (*s)["current_player"].to_string() << " )\n";
}

View file

@ -19,7 +19,7 @@
#include "server/wesnothd/player.hpp"
#include "server/wesnothd/player_connection.hpp"
#include "server/common/simple_wml.hpp"
#include "utils/make_enum.hpp"
#include "string_enums/side_controller.hpp"
#include <map>
#include <optional>
@ -36,12 +36,6 @@ class server;
class game
{
public:
MAKE_ENUM(CONTROLLER,
(HUMAN, "human")
(AI, "ai")
(EMPTY, "null")
);
game(wesnothd::server& server, player_connections& player_connections,
player_iterator host,
const std::string& name = "",
@ -661,7 +655,7 @@ private:
* change and a subsequent update_side_data() call makes it actually
* happen.
* First we look for a side where save_id= or current_player= matches the
* new user's name then we search for the first controller="network" side.
* new user's name then we search for the first controller=human or reserved side.
*
* @param user The player taking a side.
* @return True if the side was taken, false otherwise.
@ -835,7 +829,7 @@ private:
side_vector sides_;
/** A vector containiner the controller type for each side. */
std::vector<CONTROLLER> side_controllers_;
std::vector<side_controller::type> side_controllers_;
/** Number of sides in the current scenario. */
int nsides_;

View file

@ -224,7 +224,7 @@ bool side_filter::match_internal(const team &t) const
bool found = false;
for(const std::string& controller : utils::split(cfg_controller))
{
if(t.controller().to_string() == controller) {
if(side_controller::get_string(t.controller()) == controller) {
found = true;
}
}

View file

@ -0,0 +1,66 @@
/*
Copyright (C) 2008 - 2021
Part of the Battle for Wesnoth Project https://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 <optional>
#include <string>
namespace string_enums
{
/**
* The base template for associating string values with enum values.
* Implementing classes should not set custom int values for their enum.
* The number of enum values must match the number of elements in the @a values array.
* The values the @a values array must be unique.
*/
template<typename T>
struct enum_base : public T
{
/**
* Uses the int value of the provided enum to get the associated index of the @a values array in the implementing class.
*
* @param key The enum value to get the equivalent string for.
* @return The string value associated to the enum value.
*/
static std::string get_string(typename T::type key)
{
return std::string{T::values[static_cast<int>(key)]};
}
/**
* Convert a string into its enum equivalent.
*
* @param value The string value to convert.
* @return The equivalent enum or std::nullopt.
*/
static std::optional<typename T::type> get_enum(const std::string value)
{
for(unsigned int i = 0; i < T::values.size(); i++) {
if(value == T::values[i]) {
return static_cast<typename T::type>(i);
}
}
return std::nullopt;
}
/**
* @return The size of the implementing class's @a values array.
*/
static constexpr std::size_t size()
{
return T::values.size();
}
};
} // namespace string_enums

View file

@ -0,0 +1,31 @@
/*
Copyright (C) 2008 - 2021
Part of the Battle for Wesnoth Project https://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 <array>
#include "string_enums/enum_base.hpp"
struct side_controller_defines
{
static constexpr const char* const none = "null";
static constexpr const char* const human = "human";
static constexpr const char* const ai = "ai";
static constexpr const char* const reserved = "reserved";
enum class type { NONE, HUMAN, AI, RESERVED, ENUM_MAX };
static constexpr std::array<const char*, static_cast<int>(type::ENUM_MAX)> values{ none, human, ai, reserved };
};
using side_controller = string_enums::enum_base<side_controller_defines>;

View file

@ -243,17 +243,16 @@ void team::team_info::read(const config& cfg)
support_per_village = lexical_cast_default<int>(village_support, game_config::village_support);
}
controller = team::CONTROLLER::AI;
controller.parse(cfg["controller"].str());
controller = side_controller::get_enum(cfg["controller"].str()).value_or(side_controller::type::AI);
// TODO: Why do we read disallow observers differently when controller is empty?
if(controller == CONTROLLER::EMPTY) {
if(controller == side_controller::type::NONE) {
disallow_observers = cfg["disallow_observers"].to_bool(true);
}
// override persistence flag if it is explicitly defined in the config
// by default, persistence of a team is set depending on the controller
persistent = cfg["persistent"].to_bool(this->controller == CONTROLLER::HUMAN);
persistent = cfg["persistent"].to_bool(this->controller == side_controller::type::HUMAN);
//========================================================
// END OF MESSY CODE
@ -309,7 +308,7 @@ void team::team_info::write(config& cfg) const
cfg["hidden"] = hidden;
cfg["suppress_end_turn_confirmation"] = no_turn_confirmation;
cfg["scroll_to_leader"] = scroll_to_leader;
cfg["controller"] = controller;
cfg["controller"] = side_controller::get_string(controller);
cfg["recruit"] = utils::join(can_recruit);
cfg["share_vision"] = share_vision;
@ -551,7 +550,7 @@ namespace
class controller_server_choice : public synced_context::server_choice
{
public:
controller_server_choice(team::CONTROLLER new_controller, const team& team)
controller_server_choice(side_controller::type new_controller, const team& team)
: new_controller_(new_controller)
, team_(team)
{
@ -560,14 +559,14 @@ public:
/** We are in a game with no mp server and need to do this choice locally */
virtual config local_choice() const
{
return config{"controller", new_controller_, "is_local", true};
return config{"controller", side_controller::get_string(new_controller_), "is_local", true};
}
/** The request which is sent to the mp server. */
virtual config request() const
{
return config{
"new_controller", new_controller_, "old_controller", team_.controller(), "side", team_.side(),
"new_controller", side_controller::get_string(new_controller_), "old_controller", side_controller::get_string(team_.controller()), "side", team_.side(),
};
}
@ -577,29 +576,29 @@ public:
}
private:
team::CONTROLLER new_controller_;
side_controller::type new_controller_;
const team& team_;
};
} // end anon namespace
void team::change_controller_by_wml(const std::string& new_controller_string)
{
CONTROLLER new_controller;
if(!new_controller.parse(new_controller_string)) {
auto new_controller = side_controller::get_enum(new_controller_string);
if(!new_controller) {
WRN_NG << "ignored attempt to change controller to " << new_controller_string << std::endl;
return;
}
if(new_controller == CONTROLLER::EMPTY && resources::controller->current_side() == this->side()) {
if(new_controller == side_controller::type::NONE && resources::controller->current_side() == this->side()) {
WRN_NG << "ignored attempt to change the currently playing side's controller to 'null'" << std::endl;
return;
}
config choice = synced_context::ask_server_choice(controller_server_choice(new_controller, *this));
if(!new_controller.parse(choice["controller"])) {
// TODO: this should be more than a ERR_NG message.
// GL-2016SEP02 Oh? So why was ERR_NG defined as warning level? Making the call fit the definition.
config choice = synced_context::ask_server_choice(controller_server_choice(*new_controller, *this));
if(!side_controller::get_enum(choice["controller"])) {
WRN_NG << "Received an invalid controller string from the server" << choice["controller"] << std::endl;
} else {
new_controller = side_controller::get_enum(choice["controller"]);
}
if(!resources::controller->is_replay()) {
@ -612,7 +611,7 @@ void team::change_controller_by_wml(const std::string& new_controller_string)
}
}
change_controller(new_controller);
change_controller(*new_controller);
}
void team::change_team(const std::string& name, const t_string& user_name)

View file

@ -23,6 +23,7 @@
#include "recall_list_manager.hpp"
#include "units/ptr.hpp"
#include "config.hpp"
#include "string_enums/side_controller.hpp"
#include <set>
@ -72,13 +73,6 @@ private:
class team
{
public:
MAKE_ENUM(CONTROLLER,
(HUMAN, "human")
(AI, "ai")
(EMPTY, "null")
)
MAKE_ENUM(PROXY_CONTROLLER,
(PROXY_HUMAN, "human")
(PROXY_AI, "ai")
@ -137,7 +131,7 @@ private:
* displayed to the user. */
mutable bool objectives_changed;
CONTROLLER controller;
side_controller::type controller;
bool is_local;
DEFEAT_CONDITION defeat_condition;
@ -264,17 +258,17 @@ public:
}
}
CONTROLLER controller() const { return info_.controller; }
side_controller::type controller() const { return info_.controller; }
const std::string& color() const { return info_.color; }
/** @note Call display::reinit_flag_for_side() after calling this */
void set_color(const std::string& color) { info_.color = color; }
bool is_empty() const { return info_.controller == CONTROLLER::EMPTY; }
bool is_empty() const { return info_.controller == side_controller::type::NONE; }
bool is_local() const { return !is_empty() && info_.is_local; }
bool is_network() const { return !is_empty() && !info_.is_local; }
bool is_human() const { return info_.controller == CONTROLLER::HUMAN; }
bool is_ai() const { return info_.controller == CONTROLLER::AI; }
bool is_human() const { return info_.controller == side_controller::type::HUMAN; }
bool is_ai() const { return info_.controller == side_controller::type::AI; }
bool is_local_human() const { return is_human() && is_local(); }
bool is_local_ai() const { return is_ai() && is_local(); }
@ -282,13 +276,12 @@ public:
bool is_network_ai() const { return is_ai() && is_network(); }
void set_local(bool local) { info_.is_local = local; }
void make_human() { info_.controller = CONTROLLER::HUMAN; }
void make_ai() { info_.controller = CONTROLLER::AI; }
void make_human() { info_.controller = side_controller::type::HUMAN; }
void make_ai() { info_.controller = side_controller::type::AI; }
void change_controller(const std::string& new_controller) {
info_.controller = CONTROLLER::AI;
info_.controller.parse(new_controller);
info_.controller = side_controller::get_enum(new_controller).value_or(side_controller::type::AI);
}
void change_controller(CONTROLLER controller) { info_.controller = controller; }
void change_controller(side_controller::type controller) { info_.controller = controller; }
void change_controller_by_wml(const std::string& new_controller);
PROXY_CONTROLLER proxy_controller() const { return info_.proxy_controller; }