mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-01 14:31:17 +00:00
move controller tweaks to server
(fixup of 736ceaa6c7e81882c9c5b2e932307b1f1ecb3bfd) Following discussion with Soliton, we move all "controller tweaks" i.e. assignments of networked side to human on the matching client at start of game, to be server decisions, performed concurrent with start of game. In fact the controller tweaks are performed using game::change_controller, which is modified so that it doesn't try to make server messages before the game has started. When the server receives [start_game] from the host, the server will call game::change_controller for every side, sending corresponding messages to all players and observers, updating them as appropriate. The server will also rewrite level_ so that human sides are "network" and ai sides are "network_ai", making the level_ correct for any observer who subsequently joins. We remove the obsoleted client-side code in playcampaign.cpp, multiplayer.cpp. We also add to multiplayer_wait.cpp the necessary support code to interpret [change_controller] messages recieved before game start.
This commit is contained in:
parent
8e5572a239
commit
fb726e0610
@ -459,7 +459,7 @@ static void enter_wait_mode(game_display& disp, const config& game_config,
|
||||
switch (res) {
|
||||
case mp::ui::PLAY:
|
||||
play_game(disp, state, game_config, IO_CLIENT,
|
||||
preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe, observe);
|
||||
preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe);
|
||||
recorder.clear();
|
||||
|
||||
break;
|
||||
|
@ -36,6 +36,9 @@ static lg::log_domain log_network("network");
|
||||
#define DBG_NW LOG_STREAM(debug, log_network)
|
||||
#define LOG_NW LOG_STREAM(info, log_network)
|
||||
|
||||
static lg::log_domain log_enginerefac("enginerefac");
|
||||
#define LOG_RG LOG_STREAM(info, log_enginerefac)
|
||||
|
||||
namespace {
|
||||
|
||||
const SDL_Rect leader_pane_position = {-260,-370,260,370};
|
||||
@ -448,6 +451,19 @@ void wait::process_network_data(const config& data, const network::connection so
|
||||
/** @todo We should catch config::error and then leave the game. */
|
||||
level_.apply_diff(c);
|
||||
generate_menu();
|
||||
} else if(const config &change = data.child("change_controller")) {
|
||||
LOG_NW << "received change controller" << std::endl;
|
||||
LOG_RG << "multiplayer_wait: [change_controller]" << std::endl;
|
||||
LOG_RG << data.debug() << std::endl;
|
||||
//const int side = lexical_cast<int>(change["side"]);
|
||||
|
||||
if (config & sidetochange = level_.find_child("side", "side", change["side"])) {
|
||||
LOG_RG << "found side : " << sidetochange.debug() << std::endl;
|
||||
sidetochange.merge_with(change);
|
||||
LOG_RG << "changed to : " << sidetochange.debug() << std::endl;
|
||||
} else {
|
||||
LOG_RG << "change_controller didn't find any side!" << std::endl;
|
||||
}
|
||||
} else if(data.child("side") || data.child("next_scenario")) {
|
||||
level_ = first_scenario_ ? data : data.child("next_scenario");
|
||||
LOG_NW << "got some sides. Current number of sides = "
|
||||
|
@ -376,7 +376,7 @@ static LEVEL_RESULT playmp_scenario(const config& game_config,
|
||||
|
||||
LEVEL_RESULT play_game(game_display& disp, game_state& gamestate,
|
||||
const config& game_config, io_type_t io_type, bool skip_replay,
|
||||
bool network_game, bool blindfold_replay, bool observer)
|
||||
bool network_game, bool blindfold_replay)
|
||||
{
|
||||
std::string type = gamestate.classification().campaign_type;
|
||||
if(type.empty())
|
||||
@ -439,27 +439,12 @@ LEVEL_RESULT play_game(game_display& disp, game_state& gamestate,
|
||||
|
||||
while(scenario != NULL) {
|
||||
// If we are a multiplayer client, tweak the controllers
|
||||
LOG_RG << "*** Playcampaign.cpp: Tweaking controllers ***" << std::endl;
|
||||
// (actually, moved to server. do we still need this starting_pos thing?)
|
||||
if(io_type == IO_CLIENT) {
|
||||
LOG_RG << "*** Playcampaign.cpp: We are a IO_CLIENT ***" << std::endl;
|
||||
if(scenario != &starting_pos) {
|
||||
starting_pos = *scenario;
|
||||
scenario = &starting_pos;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(config &side, starting_pos.child_range("side"))
|
||||
{
|
||||
LOG_RG << "*** Playcampaign.cpp: Tweaked " << side["controller"] << " -> " << std::endl;
|
||||
if (!observer && side["current_player"] == preferences::login()) {//if we are not an observer and we are this player, it is our side
|
||||
side["controller"] = "human";
|
||||
} else if (side["controller"] == "ai" || side["controller"] == "human_ai" || side["controller"] == "network_ai") {
|
||||
side["controller"] = "network_ai"; //if server sends an ai side at start of scenario, it
|
||||
} else if (side["controller"] != "null") { //is owned by the host (and definitely not us)
|
||||
side["controller"] = "network"; //otherwise, side is controlled by human-host or null
|
||||
} //and shold be networked, null resp.
|
||||
//(did we miss anything?)
|
||||
LOG_RG << "\t\t\t\t\t" << side["controller"] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
config::const_child_itors story = scenario->child_range("story");
|
||||
|
@ -36,8 +36,7 @@ LEVEL_RESULT play_game(game_display& disp, game_state& state,
|
||||
io_type_t io_type=IO_NONE,
|
||||
bool skip_replay = false,
|
||||
bool network_game = false,
|
||||
bool blindfold_replay = false,
|
||||
bool observer = false);
|
||||
bool blindfold_replay = false);
|
||||
|
||||
void play_replay(display& disp, game_state& state,
|
||||
const config& game_config, CVideo& video);
|
||||
|
@ -153,7 +153,70 @@ std::string game::list_users(user_vector users, const std::string& func) const
|
||||
return list;
|
||||
}
|
||||
|
||||
void game::perform_controller_tweaks() {
|
||||
const simple_wml::node::child_list & sides = level_.root().children("side");
|
||||
|
||||
DBG_GAME << "****\n Performing controller tweaks. sides = " << std::endl;
|
||||
DBG_GAME << debug_sides_info() << std::endl;
|
||||
DBG_GAME << "****" << std::endl;
|
||||
|
||||
update_side_data(); // Necessary to read the level_ and get sides_, etc. updated to match
|
||||
|
||||
nsides_ = 0;
|
||||
|
||||
for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
||||
nsides_++;
|
||||
if ((**s)["controller"] != "null") {
|
||||
int side_num = (**s)["side"].to_int() - 1;
|
||||
|
||||
if (sides_[side_num] == 0) {
|
||||
sides_[side_num] = owner_;
|
||||
std::stringstream msg;
|
||||
msg << "Side " << side_num + 1 << " had no controller during controller tweaks! The host was assigned control.";
|
||||
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
|
||||
send_and_record_server_message(msg.str());
|
||||
}
|
||||
|
||||
const player_map::const_iterator user = player_info_->find(sides_[side_num]);
|
||||
std::string user_name = "null (server missing user)";
|
||||
if (user == player_info_->end()) {
|
||||
missing_user(user->first, __func__);
|
||||
} else {
|
||||
user_name = username(user);
|
||||
}
|
||||
|
||||
change_controller(side_num, sides_[side_num], user_name , false, (**s)["controller"].to_string());
|
||||
|
||||
//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");
|
||||
}
|
||||
|
||||
if (sides_[side_num] == 0) {
|
||||
std::stringstream msg;
|
||||
msg << "Side " << side_num + 1 << " had no controller AFTER controller tweaks! Ruh Roh!";
|
||||
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
update_side_data(); // this is the last time that update_side_data will actually run, as now the game will start and started_ will be true.
|
||||
|
||||
//TODO: Does it matter that the server is telling the host to change a bunch of sides?
|
||||
//According to playturn.cpp, the host should ignore all such messages. Still might be better
|
||||
//not to send them at all, although not if it complicates the server code.
|
||||
}
|
||||
|
||||
void game::start_game(const player_map::const_iterator starter) {
|
||||
const simple_wml::node::child_list & sides = level_.root().children("side");
|
||||
DBG_GAME << "****\n Starting game. sides = " << std::endl;
|
||||
DBG_GAME << debug_sides_info() << std::endl;
|
||||
DBG_GAME << "****" << std::endl;
|
||||
|
||||
|
||||
started_ = true;
|
||||
// Prevent inserting empty keys when reading.
|
||||
const simple_wml::node& s = level_.root();
|
||||
@ -177,19 +240,7 @@ void game::start_game(const player_map::const_iterator starter) {
|
||||
"\tturn bonus: " + s["mp_countdown_turn_bonus"].to_string() : "")
|
||||
<< "\n";
|
||||
|
||||
update_side_data();
|
||||
|
||||
nsides_ = 0;
|
||||
// Set all side controllers to 'human' so that observers will understand
|
||||
// that they can't take control of any sides if they happen to have the
|
||||
// same name as one of the descriptions.
|
||||
// iceiceice 3/17/2014: disabled this behavior as it causes out of sync
|
||||
// the observer issue is now handled by ... client remembers if they are observing.
|
||||
// iceiceice: the server now performs the tweaks to controller types in
|
||||
// in level as appropriate, so that it is ready to send to an observer.
|
||||
const simple_wml::node::child_list& sides = level_.root().children("side");
|
||||
for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
||||
nsides_++;
|
||||
if ((**s)["controller"] != "null") {
|
||||
int side_num = (**s)["side"].to_int() - 1;
|
||||
if (sides_[side_num] == 0) {
|
||||
@ -198,11 +249,6 @@ void game::start_game(const player_map::const_iterator starter) {
|
||||
LOG_GAME << msg.str() << " (game id: " << id_ << ")\n";
|
||||
send_and_record_server_message(msg.str());
|
||||
}
|
||||
if ((**s)["controller"] == "ai") {
|
||||
(*s)->set_attr("controller", "network_ai");
|
||||
} else { //this catches "reserved" also
|
||||
(*s)->set_attr("controller", "network");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,11 +373,16 @@ void game::update_side_data() {
|
||||
sides_[side_num] = *user;
|
||||
side_found = true;
|
||||
}
|
||||
} else if (*user == owner_
|
||||
&& ((**side)["controller"] == "ai" || (**side)["controller"] == "human")) {
|
||||
side_controllers_[side_num] = (**side)["controller"].to_string();
|
||||
sides_[side_num] = owner_;
|
||||
side_found = true;
|
||||
} else if (*user == owner_) {
|
||||
if ((**side)["controller"] == "ai" || (**side)["controller"] == "human") {
|
||||
side_controllers_[side_num] = (**side)["controller"].to_string();
|
||||
sides_[side_num] = owner_;
|
||||
side_found = true;
|
||||
} else if ((**side)["controller"] == "network_ai") {
|
||||
side_controllers_[side_num] = "ai"; //on server this field should only contain "ai" for ais. (there are no ais local to server)
|
||||
sides_[side_num] = owner_;
|
||||
side_found = true;
|
||||
}
|
||||
} else {
|
||||
// "null", "reserved"
|
||||
side_controllers_[side_num] = (**side)["controller"].to_string();
|
||||
@ -460,11 +511,15 @@ void game::change_controller(const size_t side_num,
|
||||
if (player_left && side_controllers_[side_num] == "ai") {
|
||||
// Automatic AI side transfer.
|
||||
} else if (controller.empty()) {
|
||||
send_and_record_server_message(player_name + " takes control of side " + side + ".");
|
||||
if (started_) {
|
||||
send_and_record_server_message(player_name + " takes control of side " + side + ".");
|
||||
}
|
||||
side_controllers_[side_num] = "human";
|
||||
} else {
|
||||
send_and_record_server_message(player_name + (controller == "ai" ? " " : " un")
|
||||
+ "droids side " + side + ".");
|
||||
if (started_) {
|
||||
send_and_record_server_message(player_name + (controller == "ai" ? " " : " un")
|
||||
+ "droids side " + side + ".");
|
||||
}
|
||||
side_controllers_[side_num] = (controller == "ai" ? "ai" : "human");
|
||||
}
|
||||
|
||||
@ -1168,6 +1223,8 @@ bool game::remove_player(const network::connection player, const bool disconnect
|
||||
drop.root().set_attr("side_drop", side_drop.c_str());
|
||||
drop.root().set_attr("controller", side_controllers_[side_num].c_str());
|
||||
|
||||
DBG_GAME << "*** sending side drop: \n" << drop.output() << std::endl;
|
||||
|
||||
wesnothd::send_to_one(drop, owner_);
|
||||
}
|
||||
if (ai_transfer) send_and_record_server_message("AI sides transferred to host.");
|
||||
@ -1459,6 +1516,22 @@ std::string game::debug_player_info() const {
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string game::debug_sides_info() const {
|
||||
std::stringstream result;
|
||||
result << "game id: " << id_ << "\n";
|
||||
const simple_wml::node::child_list & sides = level_.root().children("side");
|
||||
|
||||
result << "\t\t level, server\n";
|
||||
for(simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
|
||||
result << "side " << (**s)["side"].to_int() << " :\t" << (**s)["controller"].to_string()
|
||||
<< "\t, " << side_controllers_[(**s)["side"].to_int() - 1]
|
||||
<< "\t( " << sides_[(**s)["side"].to_int()-1] << ",\t"
|
||||
<< (**s)["current_player"].to_string() << " )\n";
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
player_map::iterator game::find_user(const simple_wml::string_span& name)
|
||||
{
|
||||
player_map::iterator pl;
|
||||
|
@ -108,6 +108,9 @@ public:
|
||||
const user_vector all_game_users() const;
|
||||
|
||||
void start_game(const player_map::const_iterator starter);
|
||||
void perform_controller_tweaks(); //this is performed just before starting and before [start_game] signal
|
||||
//send scenario_diff's specific to each client so that they locally
|
||||
//control their human sides
|
||||
|
||||
void update_game();
|
||||
|
||||
@ -292,6 +295,8 @@ private:
|
||||
|
||||
/** Helps debugging player and observer lists. */
|
||||
std::string debug_player_info() const;
|
||||
/** Helps debugging controller tweaks. */
|
||||
std::string debug_sides_info() const;
|
||||
|
||||
player_map* player_info_;
|
||||
|
||||
|
@ -2541,6 +2541,8 @@ void server::process_data_game(const network::connection sock,
|
||||
return;
|
||||
} else if (data.child("start_game")) {
|
||||
if (!g->is_owner(sock)) return;
|
||||
//perform controller tweaks, assigning sides as human for their owners etc.
|
||||
g->perform_controller_tweaks();
|
||||
// Send notification of the game starting immediately.
|
||||
// g->start_game() will send data that assumes
|
||||
// the [start_game] message has been sent
|
||||
|
Loading…
x
Reference in New Issue
Block a user