replace controller=network,network_ai with is_local=no in [side]

This has 3 advantages:
1) It makes the serversided handling of controller= attribute easier.
2) It prevents OOS causes by wrong use of the [side] controller=
attribute
3) It gived us a field to store the local/remote data for
null-controlled sides, this could be useful in cases where the
controller changes from null no non-null, Currently is_local is ignored
for null-controlled sides.
This commit is contained in:
gfgtdf 2015-12-29 23:14:23 +01:00
parent 12b72bfa90
commit 4d14482159
8 changed files with 48 additions and 93 deletions

View file

@ -212,13 +212,13 @@ void game_board::side_drop_to(int side_num, team::CONTROLLER ctrl, team::PROXY_C
if (leader.valid()) leader->rename(lexical_cast<std::string> (ctrl) + lexical_cast<std::string> (side_num));
}
void game_board::side_change_controller(int side_num, team::CONTROLLER ctrl, const std::string& pname) {
void game_board::side_change_controller(int side_num, bool is_local, const std::string& pname) {
team &tm = teams_[side_num-1];
tm.change_controller(ctrl);
tm.set_local(is_local);
if (pname.empty()) {
return ;
if (pname.empty() || !tm.is_human()) {
return;
}
tm.set_current_player(pname);

View file

@ -124,7 +124,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_change_controller (int side_num, team::CONTROLLER ctrl, const std::string& pname = "");
void side_change_controller (int side_num, bool is_local, const std::string& pname = "");
// Manipulator from actionwml

View file

@ -48,7 +48,7 @@ static lg::log_domain log_network("network");
namespace {
const std::string controller_names[] = {
"network",
"human",
"human",
"ai",
"null",
@ -1011,12 +1011,10 @@ config side_engine::new_config() const
}
res["controller"] = controller_names[controller_];
if(player_id_ == preferences::login() && res["controller"] == "network") {
// the hosts rveices the serversided controller wteaks after the start event, but
// for mp sync it's very important that the controller types are correct
// during the start/prestart event (otherwse random unit creation during prestart fails).
res["controller"] = "human";
}
// the hosts rveices the serversided controller tweaks after the start event, but
// for mp sync it's very important that the controller types are correct
// during the start/prestart event (otherwse random unit creation during prestart fails).
res["is_local"] = player_id_ == preferences::login();
std::string desc = user_description();
if(!desc.empty()) {

View file

@ -279,7 +279,7 @@ void wait::join_game(bool observe)
side_num = nb_sides;
break;
}
if (sd["controller"] == "network" && sd["player_id"].empty())
if (sd["controller"] == "human" && sd["player_id"].empty())
{
if (!side_choice) { // found the first empty side
side_choice = &sd;

View file

@ -173,30 +173,20 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
ERR_NW << "Bad [change_controller] signal from server, [change_controller] tag was empty." << std::endl;
return PROCESS_CONTINUE;
}
//don't use lexical_cast_default it's "safer" to end on error
const int side = lexical_cast<int>(change["side"]);
const int side = change["side"].to_int();
const bool is_local = change["is_local"].to_bool();
const std::string player = change["player"];
const size_t index = side - 1;
assert(resources::gameboard);
if(index >= resources::gameboard->teams().size()) {
ERR_NW << "Bad [change_controller] signal from server, side out of bounds: " << change.debug() << std::endl;
return PROCESS_CONTINUE;
}
const team & tm = resources::gameboard->teams().at(index);
const std::string &player = change["player"];
const bool was_local = tm.is_local();
team::CONTROLLER new_controller = team::CONTROLLER();
try {
new_controller = team::CONTROLLER::string_to_enum(change["controller"].str());
} catch (bad_enum_cast & e) {
ERR_NW << "Bad [change_controller] message from server:\n" << e.what() << std::endl << change.debug() << std::endl;
return PROCESS_CONTINUE;
}
resources::gameboard->side_change_controller(side, new_controller, player);
resources::gameboard->side_change_controller(side, is_local, player);
if (!was_local && tm.is_local()) {
resources::controller->on_not_observer();

View file

@ -245,12 +245,8 @@ void game::perform_controller_tweaks() {
// Therefore, if the side belongs to the host, we pass player_left = true, otherwise player_left = false.
change_controller(side_index, sides_[side_index], user_name , sides_[side_index] == owner_);
//next lines change controller types found in level_ to be what is appropriate for an observer at game start.
if ((**s)["controller"] == "ai") {
(*s)->set_attr("controller", "network_ai");
} else { //this catches "reserved" also
(*s)->set_attr("controller", "network");
}
//next line change controller types found in level_ to be what is appropriate for an observer at game start.
(*s)->set_attr("is_local", "no");
if (sides_[side_index] == 0) {
std::stringstream msg;
@ -366,10 +362,12 @@ bool game::take_side(const player_map::const_iterator user)
simple_wml::document cfg;
cfg.root().set_attr_dup("name", user->second.name().c_str());
//FIXME: It the client code (multiplayer.wait.cpp) the host code (connect_engine.cpp) and the server code (this file)
// Has this code to figure out a fitting aise for a new players, tis is clearly too much.
// Check if we can figure out a fitting side.
const simple_wml::node::child_list& sides = get_sides_list();
for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
if(((**side)["controller"] == "network" || (**side)["controller"] == "reserved")
if(((**side)["controller"] == "human" || (**side)["controller"] == "reserved")
&& (**side)["current_player"] == user->second.name().c_str())
{
if (send_taken_side(cfg, side)) return true;
@ -377,7 +375,7 @@ bool game::take_side(const player_map::const_iterator user)
}
// If there was no fitting side just take the first available.
for(simple_wml::node::child_list::const_iterator side = sides.begin(); side != sides.end(); ++side) {
if((**side)["controller"] == "network") {
if((**side)["controller"] == "human") {
if (send_taken_side(cfg, side)) return true;
}
}
@ -433,14 +431,11 @@ void game::update_side_data()
const simple_wml::string_span& player_id = (**side)["player_id"];
const simple_wml::string_span& controller = (**side)["controller"];
if ( player_id == info->second.name().c_str()) {
//if this is called before perform_controller_tweaks() we have "ai" and "human" controllers
//if its called after that we have "network" and "network_ai" controllers.
if(controller != "network" && controller != "human" && controller != "ai" && controller != "network_ai") {
if(controller != "human" && controller != "ai") {
//we found invalid [side] data. Some message would be cool.
continue;
}
//convert "network_ai" -> "ai", "network" -> "human"
side_controllers_[side_index] = controller == "network" ? "human" : controller == "network_ai" ? "ai" : controller.to_string();
side_controllers_[side_index] = controller.to_string();
sides_[side_index] = *user;
side_found = true;
}
@ -573,7 +568,8 @@ void game::change_controller(const size_t side_index,
change.set_attr("player", player_name.c_str());
// Tell everyone but the new player that this side's controller changed.
change.set_attr("controller", (side_controllers_[side_index] == "ai" ? "network_ai" : "network"));
change.set_attr("controller", (side_controllers_[side_index] == "ai" ? "ai" : "human"));
change.set_attr("is_local", "no");
send_data(response, sock);
if (started_) { //this is added instead of the if (started_) {...} below
@ -585,7 +581,7 @@ void game::change_controller(const size_t side_index,
// Just don't send it when the player left the game. (The host gets the
// side_drop already.)
if (!player_left) {
change.set_attr("controller", (side_controllers_[side_index] == "ai" ? "ai" : "human"));
change.set_attr("is_local", "yes");
wesnothd::send_to_one(response, sock);
}
}
@ -1047,18 +1043,14 @@ void game::handle_controller_choice(const simple_wml::node& req)
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.c_str());
change_controller_wml.set_attr_dup("is_local", "yes");
command.set_attr("from_side", "server");
command.set_attr("dependent", "yes");
if(sides_[side_index] != 0) {
//calling send_to_one to 0 connect causes the package to be sended to all clients.
wesnothd::send_to_one(*mdata, sides_[side_index], "game replay");
}
if(new_controller == "human") {
change_controller_wml.set_attr("controller", "network");
}
if(new_controller == "ai") {
change_controller_wml.set_attr("controller", "network_ai");
}
change_controller_wml.set_attr_dup("is_local", "no");
send_data(*mdata, sides_[side_index], "game replay");
record_data(mdata);
}
@ -1391,27 +1383,9 @@ void game::load_next_scenario(const player_map::const_iterator user) {
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
send_and_record_server_message(msg.str());
} else if (sides_[side_index] == user->first) {
if (side_controllers_[side_index] == "human") {
(*s)->set_attr("controller", "human");
} else if (side_controllers_[side_index] == "ai") {
(*s)->set_attr("controller", "ai");
} else {
std::stringstream msg;
msg << "Side " << side_index + 1 << " had unexpected side_controller = " << side_controllers_[side_index] << " on server side.";
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
send_and_record_server_message(msg.str());
}
(*s)->set_attr("is_local", "yes");
} else {
if (side_controllers_[side_index] == "human") {
(*s)->set_attr("controller", "network");
} else if (side_controllers_[side_index] == "ai") {
(*s)->set_attr("controller", "network_ai");
} else {
std::stringstream msg;
msg << "Side " << side_index + 1 << " had unexpected side_controller = " << side_controllers_[side_index] << " on server side.";
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
send_and_record_server_message(msg.str());
}
(*s)->set_attr("is_local", "no");
}
}
}

View file

@ -96,6 +96,7 @@ team::team_info::team_info() :
objectives(),
objectives_changed(false),
controller(),
is_local(true),
defeat_condition(team::DEFEAT_CONDITION::NO_LEADER),
proxy_controller(team::PROXY_CONTROLLER::PROXY_HUMAN),
share_vision(team::SHARE_VISION::ALL),
@ -146,6 +147,7 @@ void team::team_info::read(const config &cfg)
carryover_bonus = cfg["carryover_bonus"].to_double(1);
carryover_gold = cfg["carryover_gold"].to_int(0);
variables = cfg.child_or_empty("variables");
is_local = cfg["is_local"].to_bool(true);
if(cfg.has_attribute("color")) {
color = cfg["color"].str();
@ -204,7 +206,7 @@ void team::team_info::read(const config &cfg)
}
//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 || this->controller == CONTROLLER::NETWORK);
persistent = cfg["persistent"].to_bool(this->controller == CONTROLLER::HUMAN);
//========================================================
//END OF MESSY CODE
@ -475,14 +477,6 @@ bool team::calculate_is_enemy(size_t index) const
namespace
{
team::CONTROLLER unified_controller(team::CONTROLLER c)
{
if(c == team::CONTROLLER::NETWORK)
return team::CONTROLLER::HUMAN;
if(c == team::CONTROLLER::NETWORK_AI)
return team::CONTROLLER::AI;
return c;
}
class controller_server_choice : public synced_context::server_choice
{
public:
@ -501,7 +495,7 @@ namespace
{
return config_of
("new_controller", new_controller_)
("old_controller", unified_controller(team_.controller()))
("old_controller", team_.controller())
("side", team_.side());
}
virtual const char* name() const
@ -519,17 +513,15 @@ void team::change_controller_by_wml(const std::string& new_controller_string)
try
{
CONTROLLER new_controller = lexical_cast<CONTROLLER> (new_controller_string);
if(new_controller == CONTROLLER::NETWORK || new_controller == CONTROLLER::NETWORK_AI) {
throw bad_enum_cast(new_controller_string, "CONTROLLER"); //catched below
}
if(new_controller == CONTROLLER::EMPTY && resources::controller->current_side() == this->side()) {
//We dont allow changing the currently active side to "null" controlled.
//We don't allow changing the currently active side to "null" controlled.
throw bad_enum_cast(new_controller_string, "CONTROLLER"); //catched below
}
config choice = synced_context::ask_server_choice(controller_server_choice(new_controller, *this));
if(!new_controller.parse(choice["controller"])) {
ERR_NG << "recieved an invalid controller string from the server" << choice["controller"] << std::endl;
}
set_local(choice["is_local"].to_bool());
change_controller(new_controller);
}
catch(const bad_enum_cast&)

View file

@ -55,8 +55,6 @@ public:
MAKE_ENUM(CONTROLLER,
(HUMAN, "human")
(AI, "ai")
(NETWORK, "network")
(NETWORK_AI, "network_ai")
(EMPTY, "null")
)
@ -140,6 +138,7 @@ private:
mutable bool objectives_changed;
CONTROLLER controller;
bool is_local;
DEFEAT_CONDITION defeat_condition;
PROXY_CONTROLLER proxy_controller; // when controller == HUMAN, the proxy controller determines what input method is actually used.
@ -257,24 +256,26 @@ public:
void set_color(const std::string& color) { info_.color = color; }
bool is_empty() const { return info_.controller == CONTROLLER::EMPTY; }
bool is_local() const { return is_local_human() || is_local_ai(); }
bool is_network() const { return is_network_human() || is_network_ai(); }
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 is_local_human() || is_network_human(); }
bool is_human() const { return info_.controller == CONTROLLER::HUMAN; }
bool is_ai() const { return info_.controller == CONTROLLER::AI; }
bool is_local_human() const { return info_.controller == CONTROLLER::HUMAN; }
bool is_local_ai() const { return info_.controller == CONTROLLER::AI; }
bool is_network_human() const { return info_.controller == CONTROLLER::NETWORK; }
bool is_network_ai() const { return info_.controller == CONTROLLER::NETWORK_AI; }
bool is_local_human() const { return is_human() && is_local(); }
bool is_local_ai() const { return is_ai() && is_local(); }
bool is_network_human() const { return is_human() && is_network(); }
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 change_controller(const std::string& new_controller) {
info_.controller = CONTROLLER::AI;
info_.controller.parse(new_controller);
}
void change_controller_by_wml(const std::string& new_controller);
void change_controller(CONTROLLER controller) { info_.controller = controller; }
void change_controller_by_wml(const std::string& new_controller);
PROXY_CONTROLLER proxy_controller() const { return info_.proxy_controller; }
bool is_proxy_human() const { return info_.proxy_controller == PROXY_CONTROLLER::PROXY_HUMAN; }