diff --git a/src/game_initialization/connect_engine.cpp b/src/game_initialization/connect_engine.cpp index 0b2cf4531e4e..d9bae4b41a6f 100644 --- a/src/game_initialization/connect_engine.cpp +++ b/src/game_initialization/connect_engine.cpp @@ -23,6 +23,7 @@ #include "multiplayer_ui.hpp" #include "mp_game_utils.hpp" #include "mt_rng.hpp" +#include "playcampaign.hpp" #include "tod_manager.hpp" #include "multiplayer_ui.hpp" // For get_color_string #include @@ -76,20 +77,19 @@ const std::string attributes_to_trim[] = { namespace ng { connect_engine::connect_engine(saved_game& state, - const bool local_players_only, const bool first_scenario, const std::set& players) : + const bool first_scenario, mp_campaign_info* campaign_info) : level_(), state_(state), params_(state.mp_settings()), - default_controller_(local_players_only ? CNTR_LOCAL: CNTR_NETWORK), - local_players_only_(local_players_only), + default_controller_(campaign_info ? CNTR_NETWORK : CNTR_LOCAL), + campaign_info_(campaign_info), first_scenario_(first_scenario), force_lock_settings_(), side_engines_(), era_factions_(), team_names_(), user_team_names_(), - player_teams_(), - connected_users_(players) + player_teams_() { // Initial level config from the mp_game_settings. level_ = mp::initial_level_config(state_); @@ -227,8 +227,9 @@ void connect_engine::import_user(const config& data, const bool observer, { const std::string& username = data["name"]; assert(!username.empty()); - - connected_users_.insert(username); + if(campaign_info_) { + connected_users_rw().insert(username); + } update_side_controller_options(); if (observer) { @@ -607,7 +608,7 @@ std::pair connect_engine::process_network_data(const config& data, side_engine_ptr side_to_drop = side_engines_[side_drop]; // Remove user, whose side was dropped. - connected_users_.erase(side_to_drop->player_id()); + connected_users_rw().erase(side_to_drop->player_id()); update_side_controller_options(); side_to_drop->reset(); @@ -634,7 +635,7 @@ std::pair connect_engine::process_network_data(const config& data, return result; } - if (connected_users_.find(name) != connected_users_.end()) { + if (connected_users().find(name) != connected_users().end()) { // TODO: Seems like a needless limitation // to only allow one side per player. if (find_user_side_index_by_id(name) != -1) { @@ -646,7 +647,7 @@ std::pair connect_engine::process_network_data(const config& data, return result; } else { - connected_users_.erase(name); + connected_users_rw().erase(name); update_side_controller_options(); config observer_quit; observer_quit.add_child("observer_quit")["name"] = name; @@ -727,11 +728,10 @@ std::pair connect_engine::process_network_data(const config& data, if (const config& observer = data.child("observer_quit")) { const t_string& observer_name = observer["name"]; if (!observer_name.empty()) { - if ((connected_users_.find(observer_name) != - connected_users_.end()) && + if ((connected_users().find(observer_name) != connected_users().end()) && (find_user_side_index_by_id(observer_name) != -1)) { - connected_users_.erase(observer_name); + connected_users_rw().erase(observer_name); update_side_controller_options(); update_and_send_diff(); } @@ -823,7 +823,7 @@ void connect_engine::load_previous_sides_users() //Do this in an extra loop to make sure we import each user only once. BOOST_FOREACH(const std::string& name, names) { - if (connected_users_.find(name) != connected_users_.end()) { + if (connected_users().find(name) != connected_users().end() || !campaign_info_) { import_user(name, false); } } @@ -836,6 +836,23 @@ void connect_engine::update_side_controller_options() } } + +const std::set& connect_engine::connected_users() const +{ + if(campaign_info_) { + return campaign_info_->connected_players; + } + else { + static std::set empty; + return empty; + } +} +std::set& connect_engine::connected_users_rw() +{ + assert(campaign_info_); + return campaign_info_->connected_players; +} + side_engine::side_engine(const config& cfg, connect_engine& parent_engine, const int index) : cfg_(cfg), @@ -1238,7 +1255,7 @@ void side_engine::update_controller_options() controller_options_.clear(); // Default options. - if (!parent_.local_players_only_) { + if (parent_.campaign_info_) { add_controller_option(CNTR_NETWORK, _("Network Player"), "human"); } add_controller_option(CNTR_LOCAL, _("Local Player"), "human"); @@ -1251,7 +1268,7 @@ void side_engine::update_controller_options() // Connected users. add_controller_option(CNTR_LAST, _("--give--"), "human"); - BOOST_FOREACH(const std::string& user, parent_.connected_users_) { + BOOST_FOREACH(const std::string& user, parent_.connected_users()) { add_controller_option(parent_.default_controller_, user, "human"); } diff --git a/src/game_initialization/connect_engine.hpp b/src/game_initialization/connect_engine.hpp index a8f1dbee1794..0a7c943dfd24 100644 --- a/src/game_initialization/connect_engine.hpp +++ b/src/game_initialization/connect_engine.hpp @@ -24,6 +24,7 @@ #include namespace rand_rng { class mt_rng; } +struct mp_campaign_info; namespace ng { @@ -49,9 +50,7 @@ class connect_engine /// @param players the player which are already connected to the current game. /// This is always empty unless we advance form a previous scenario. connect_engine(saved_game& state, - const bool local_players_only, - const bool first_scenario, - const std::set& players = std::set()); + const bool first_scenario, mp_campaign_info* campaign_info); ~connect_engine(); config* current_config(); @@ -96,8 +95,7 @@ class connect_engine else throw "No scenariodata found"; } - const std::set& connected_users() const - { return connected_users_; } + const std::set& connected_users() const; const std::vector& user_team_names() { return user_team_names_; } std::vector& side_engines() { return side_engines_; } @@ -124,7 +122,7 @@ class connect_engine const mp_game_settings& params_; const ng::controller default_controller_; - const bool local_players_only_; + mp_campaign_info* campaign_info_; const bool first_scenario_; bool force_lock_settings_; @@ -134,7 +132,8 @@ class connect_engine std::vector team_names_; std::vector user_team_names_; std::vector player_teams_; - std::set connected_users_; + + std::set& connected_users_rw(); }; class side_engine diff --git a/src/game_initialization/multiplayer.cpp b/src/game_initialization/multiplayer.cpp index 820ff99a54d9..d697cbe45858 100644 --- a/src/game_initialization/multiplayer.cpp +++ b/src/game_initialization/multiplayer.cpp @@ -40,7 +40,6 @@ #include "multiplayer_wait.hpp" #include "multiplayer_lobby.hpp" #include "playcampaign.hpp" -#include "playmp_controller.hpp" #include "settings.hpp" #include "scripting/plugins/context.hpp" #include "sound.hpp" @@ -443,7 +442,7 @@ static server_type open_connection(game_display& disp, const std::string& origin // of those screen functions. static void enter_wait_mode(game_display& disp, const config& game_config, - saved_game& state, bool observe) + saved_game& state, bool observe, int current_turn = 0) { DBG_MP << "entering wait mode" << std::endl; @@ -452,6 +451,12 @@ static void enter_wait_mode(game_display& disp, const config& game_config, gamelist.clear(); statistics::fresh_stats(); + mp_campaign_info campaign_info; + campaign_info.is_host = false; + if(preferences::skip_mp_replay() || preferences::blindfold_replay()) { + campaign_info.skip_replay_until_turn = current_turn; + campaign_info.skip_replay_blindfolded = preferences::blindfold_replay(); + } { mp::wait ui(disp, game_config, state, gamechat, gamelist); @@ -460,6 +465,7 @@ static void enter_wait_mode(game_display& disp, const config& game_config, run_lobby_loop(disp, ui); res = ui.get_result(); + campaign_info.connected_players.insert(ui.user_list().begin(), ui.user_list().end()); if (res == mp::ui::PLAY) { ui.start_game(); @@ -474,10 +480,12 @@ static void enter_wait_mode(game_display& disp, const config& game_config, } switch (res) { - case mp::ui::PLAY: - play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_CLIENT, - preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe); + case mp::ui::PLAY: { + campaign_controller controller(disp, state, game_config, game_config_manager::get()->terrain_types()); + controller.set_mp_info(&campaign_info); + controller.play_game(); break; + } case mp::ui::QUIT: default: break; @@ -499,9 +507,15 @@ static bool enter_connect_mode(game_display& disp, const config& game_config, gamelist.clear(); statistics::fresh_stats(); + boost::optional campaign_info; + if(!local_players_only) { + campaign_info = mp_campaign_info(); + campaign_info->connected_players.insert(preferences::login()); + campaign_info->is_host = true; + } { - ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, local_players_only, true)); + ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, campaign_info.get_ptr())); mp::connect ui(disp, state.mp_settings().name, game_config, gamechat, gamelist, *connect_engine); run_lobby_loop(disp, ui); @@ -516,10 +530,12 @@ static bool enter_connect_mode(game_display& disp, const config& game_config, } // end connect_engine_ptr scope switch (res) { - case mp::ui::PLAY: - play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, - !local_players_only); + case mp::ui::PLAY: { + campaign_controller controller(disp, state, game_config, game_config_manager::get()->terrain_types()); + controller.set_mp_info(campaign_info.get_ptr()); + controller.play_game(); break; + } case mp::ui::CREATE: enter_create_mode(disp, game_config, state, local_players_only); break; @@ -652,7 +668,7 @@ static void enter_lobby_mode(game_display& disp, const config& game_config, DBG_MP << "entering lobby mode" << std::endl; mp::ui::result res; - + int current_turn = 0; while (true) { const config &cfg = game_config.child("lobby_music"); if (cfg) { @@ -699,12 +715,14 @@ static void enter_lobby_mode(game_display& disp, const config& game_config, mp::lobby ui(disp, game_config, gamechat, gamelist, installed_addons); run_lobby_loop(disp, ui); res = ui.get_result(); + current_turn = ui.current_turn; } switch (res) { case mp::ui::JOIN: + case mp::ui::OBSERVE: try { - enter_wait_mode(disp, game_config, state, false); + enter_wait_mode(disp, game_config, state, res == mp::ui::OBSERVE, current_turn); } catch(config::error& error) { if(!error.message.empty()) { gui2::show_error_message(disp.video(), error.message); @@ -713,18 +731,6 @@ static void enter_lobby_mode(game_display& disp, const config& game_config, network::send_data(config("refresh_lobby"), 0); } break; - case mp::ui::OBSERVE: - try { - enter_wait_mode(disp, game_config, state, true); - } catch(config::error& error) { - if(!error.message.empty()) { - gui2::show_error_message(disp.video(), error.message); - } - } - // update lobby content unconditionally because we might have left only after the - // game ended in which case we ignored the gamelist and need to request it again - network::send_data(config("refresh_lobby"), 0); - break; case mp::ui::CREATE: try { enter_create_mode(disp, game_config, state, false); @@ -758,7 +764,6 @@ void start_local_game(game_display& disp, const config& game_config, DBG_MP << "starting local game" << std::endl; gamechat.clear_history(); gamelist.clear(); - playmp_controller::set_replay_last_turn(0); preferences::set_message_private(false); enter_create_mode(disp, game_config, state, true); } @@ -773,7 +778,6 @@ void start_local_game_commandline(game_display& disp, const config& game_config, // needed in commandline mode, but they are required by the functions called. gamechat.clear_history(); gamelist.clear(); - playmp_controller::set_replay_last_turn(0); preferences::set_message_private(false); DBG_MP << "entering create mode" << std::endl; @@ -882,7 +886,7 @@ void start_local_game_commandline(game_display& disp, const config& game_config, statistics::fresh_stats(); { - ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, true)); + ng::connect_engine_ptr connect_engine(new ng::connect_engine(state, true, NULL)); mp::connect ui(disp, parameters.name, game_config, gamechat, gamelist, *connect_engine); @@ -898,7 +902,9 @@ void start_local_game_commandline(game_display& disp, const config& game_config, unsigned int repeat = (cmdline_opts.multiplayer_repeat) ? *cmdline_opts.multiplayer_repeat : 1; for(unsigned int i = 0; i < repeat; i++){ saved_game state_copy(state); - play_game(disp, state_copy, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, false); + campaign_controller controller(disp, state_copy, game_config, game_config_manager::get()->terrain_types()); + controller.play_game(); + break; } } @@ -942,7 +948,6 @@ void start_client(game_display& disp, const config& game_config, } while (re_enter); break; case SIMPLE_SERVER: - playmp_controller::set_replay_last_turn(0); preferences::set_message_private(false); enter_wait_mode(disp, *game_config_ptr, state, false); break; diff --git a/src/game_initialization/multiplayer_lobby.cpp b/src/game_initialization/multiplayer_lobby.cpp index 555ef97e6fe7..4b426857a156 100644 --- a/src/game_initialization/multiplayer_lobby.cpp +++ b/src/game_initialization/multiplayer_lobby.cpp @@ -34,11 +34,13 @@ #include "gui/widgets/window.hpp" // for gui2::twindow::OK #include "lobby_reload_request_exception.hpp" #include "log.hpp" -#include "playmp_controller.hpp" #include "sound.hpp" #include "wml_exception.hpp" #include "formula_string_utils.hpp" #include "terrain_type_data.hpp" +#include "version.hpp" +#include "game_display.hpp" + #include #include @@ -1025,6 +1027,7 @@ bool lobby::lobby_sorter::less(int column, const gui::menu::item& row1, const gu lobby::lobby(game_display& disp, const config& cfg, chat& c, config& gamelist, const std::vector & installed_addons) : mp::ui(disp, _("Game Lobby"), cfg, c, gamelist), + current_turn(0), game_vacant_slots_(), game_observers_(), @@ -1256,7 +1259,6 @@ void lobby::process_event_impl(const process_event_data & data) preferences::set_skip_mp_replay(replay_options_.selected() == 1); preferences::set_blindfold_replay(replay_options_.selected() == 2); - playmp_controller::set_replay_last_turn(0); preferences::set_message_private(false); int selected_game = games_menu_.selection(); @@ -1298,9 +1300,7 @@ void lobby::process_event_impl(const process_event_data & data) network::send_data(response, 0); if(observe) { - if (game.started){ - playmp_controller::set_replay_last_turn(game.current_turn); - } + this->current_turn = game.current_turn; set_result(OBSERVE); } else { set_result(JOIN); diff --git a/src/game_initialization/multiplayer_lobby.hpp b/src/game_initialization/multiplayer_lobby.hpp index 718d0ece0f08..afb9aa57daa5 100644 --- a/src/game_initialization/multiplayer_lobby.hpp +++ b/src/game_initialization/multiplayer_lobby.hpp @@ -152,6 +152,7 @@ class gamebrowser : public gui::menu { game_item selected_game() { return games_[selected_]; } void select_game(const std::string& id); bool game_matches_filter(const game_item& i, const config& cfg); + protected: unsigned int row_height() const { return item_height_ + (2 * style_->get_thickness()); } private: @@ -189,6 +190,7 @@ class lobby : public ui virtual void process_event(); + int current_turn; protected: virtual void hide_children(bool hide=true); virtual void layout_children(const SDL_Rect& rect); diff --git a/src/game_initialization/multiplayer_ui.hpp b/src/game_initialization/multiplayer_ui.hpp index 9f93f4a1f7c5..9669b254dd46 100644 --- a/src/game_initialization/multiplayer_ui.hpp +++ b/src/game_initialization/multiplayer_ui.hpp @@ -103,7 +103,7 @@ class ui : public gui::widget, private events::chat_handler, private font::float */ void set_location(const SDL_Rect& rect); using widget::set_location; - + const std::vector& user_list() const { return user_list_; } protected: int xscale(int x) const; int yscale(int y) const; diff --git a/src/game_initialization/playcampaign.cpp b/src/game_initialization/playcampaign.cpp index 3adb34c3ee52..f98c90ace0e1 100644 --- a/src/game_initialization/playcampaign.cpp +++ b/src/game_initialization/playcampaign.cpp @@ -59,7 +59,7 @@ static lg::log_domain log_engine("engine"); static lg::log_domain log_enginerefac("enginerefac"); #define LOG_RG LOG_STREAM(info, log_enginerefac) -static void report_victory( +void campaign_controller::report_victory( std::ostringstream &report, team& t, int finishing_bonus_per_turn, int turns_left, int finishing_bonus) { @@ -121,7 +121,7 @@ static void report_victory( report << "\n" << goldmsg; } -static void show_carryover_message(saved_game& gamestate, playsingle_controller& playcontroller, display& disp, const end_level_data& end_level, const LEVEL_RESULT res) +void campaign_controller::show_carryover_message(playsingle_controller& playcontroller, const end_level_data& end_level, const LEVEL_RESULT res) { assert(resources::teams); @@ -157,7 +157,7 @@ static void show_carryover_message(saved_game& gamestate, playsingle_controller& } if (persistent_teams > 0 && ((has_next_scenario && end_level.proceed_to_next_level)|| - gamestate.classification().campaign_type == game_classification::CAMPAIGN_TYPE::TEST)) + state_.classification().campaign_type == game_classification::CAMPAIGN_TYPE::TEST)) { gamemap map = playcontroller.get_map_const(); tod_manager tod = playcontroller.get_tod_manager_const(); @@ -184,59 +184,51 @@ static void show_carryover_message(saved_game& gamestate, playsingle_controller& } if (end_level.transient.carryover_report) { - gui2::show_transient_message(disp.video(), title, report.str(), "", true); + gui2::show_transient_message(disp_.video(), title, report.str(), "", true); } } -static LEVEL_RESULT playsingle_scenario(const config& game_config, - const tdata_cache & tdata, - display& disp, saved_game& state_of_game, - bool skip_replay, end_level_data &end_level, bool& is_replay, bool is_unit_test) +LEVEL_RESULT campaign_controller::playsingle_scenario(end_level_data &end_level) { - playsingle_controller playcontroller(is_replay ? state_of_game.get_replay_starting_pos() : state_of_game.get_starting_pos(), state_of_game, game_config, tdata, disp.video(), skip_replay); + playsingle_controller playcontroller(is_replay_ ? state_.get_replay_starting_pos() : state_.get_starting_pos(), state_, game_config_, tdata_, disp_.video(), false); LOG_NG << "created objects... " << (SDL_GetTicks() - playcontroller.get_ticks()) << "\n"; - if(is_replay) { - playcontroller.enable_replay(is_unit_test); + if(is_replay_) { + playcontroller.enable_replay(is_unit_test_); } - LEVEL_RESULT res = playcontroller.play_scenario(is_replay ? state_of_game.get_replay_starting_pos() : state_of_game.get_starting_pos()); + LEVEL_RESULT res = playcontroller.play_scenario(is_replay_ ? state_.get_replay_starting_pos() : state_.get_starting_pos()); if (res == LEVEL_RESULT::QUIT) { return LEVEL_RESULT::QUIT; } - if(!is_unit_test) + if(!is_unit_test_) { - is_replay = false; + is_replay_ = false; } - if(is_replay) + if(is_replay_) { return res; } end_level = playcontroller.get_end_level_data_const(); - show_carryover_message(state_of_game, playcontroller, disp, end_level, res); - if(!disp.video().faked()) + show_carryover_message(playcontroller, end_level, res); + if(!disp_.video().faked()) { playcontroller.maybe_linger(); } - state_of_game.set_snapshot(playcontroller.to_config()); + state_.set_snapshot(playcontroller.to_config()); return res; } -static LEVEL_RESULT playmp_scenario(const config& game_config, - const tdata_cache & tdata, - display& disp, saved_game& state_of_game, - bool skip_replay, - std::set& mp_players, bool blindfold_replay, io_type_t& io_type, end_level_data &end_level) +LEVEL_RESULT campaign_controller::playmp_scenario(end_level_data &end_level) { - playmp_controller playcontroller(state_of_game.get_starting_pos(), state_of_game, - game_config, tdata, disp.video(), skip_replay, blindfold_replay, io_type == IO_SERVER); - LEVEL_RESULT res = playcontroller.play_scenario(state_of_game.get_starting_pos()); + + playmp_controller playcontroller(state_.get_starting_pos(), state_, + game_config_, tdata_, disp_.video(), mp_info_); + LEVEL_RESULT res = playcontroller.play_scenario(state_.get_starting_pos()); //Check if the player started as mp client and changed to host - if (io_type == IO_CLIENT && playcontroller.is_host()) - io_type = IO_SERVER; if (res == LEVEL_RESULT::QUIT) { @@ -249,84 +241,80 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, { //We need to call this before linger because it prints the defeated/victory message. //(we want to see that message before entering the linger mode) - show_carryover_message(state_of_game, playcontroller, disp, end_level, res); + show_carryover_message(playcontroller, end_level, res); } - if(!disp.video().faked()) + if(!disp_.video().faked()) { playcontroller.maybe_linger(); } playcontroller.update_savegame_snapshot(); - mp_players = playcontroller.all_players(); + mp_info_->connected_players = playcontroller.all_players(); return res; } -LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, - const config& game_config, const tdata_cache & tdata, - io_type_t io_type, bool skip_replay, - bool network_game, bool blindfold_replay, bool is_unit_test, bool is_replay) +LEVEL_RESULT campaign_controller::play_game() { - if(is_replay) { - gamestate.get_replay().set_pos(0); + if(is_replay_) { + state_.get_replay().set_pos(0); } else { - gamestate.get_replay().set_to_end(); + state_.get_replay().set_to_end(); } - gamestate.expand_scenario(); + state_.expand_scenario(); - game_classification::CAMPAIGN_TYPE game_type = gamestate.classification().campaign_type; + game_classification::CAMPAIGN_TYPE game_type = state_.classification().campaign_type; - while(gamestate.valid()) + while(state_.valid()) { LEVEL_RESULT res = LEVEL_RESULT::VICTORY; end_level_data end_level; - std::set mp_players; try { - gamestate.expand_random_scenario(); + state_.expand_random_scenario(); //In case this an mp scenario reloaded by sp this was not already done yet. - gamestate.expand_mp_events(); + state_.expand_mp_events(); sound::empty_playlist(); - gamestate.expand_carryover(); + state_.expand_carryover(); //expand_mp_options must be called after expand_carryover because expand_carryover will to set previous variables if there are already variables in the [scenario] - gamestate.expand_mp_options(); + state_.expand_mp_options(); #if !defined(ALWAYS_USE_MP_CONTROLLER) if (game_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) { - res = playsingle_scenario(game_config, tdata, disp, gamestate, skip_replay, end_level, is_replay, is_unit_test); - if(is_replay) { + res = playsingle_scenario(end_level); + if(is_replay_) { return res; } } else #endif { - res = playmp_scenario(game_config, tdata, disp, gamestate, skip_replay, mp_players, blindfold_replay, io_type, end_level); + res = playmp_scenario(end_level); } } catch(game::load_game_failed& e) { - gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message); + gui2::show_error_message(disp_.video(), _("The game could not be loaded: ") + e.message); return LEVEL_RESULT::QUIT; } catch(quit_game_exception&) { LOG_NG << "The game was aborted\n"; return LEVEL_RESULT::QUIT; } catch(game::game_error& e) { - gui2::show_error_message(disp.video(), _("Error while playing the game: ") + e.message); + gui2::show_error_message(disp_.video(), _("Error while playing the game: ") + e.message); return LEVEL_RESULT::QUIT; } catch(incorrect_map_format_error& e) { - gui2::show_error_message(disp.video(), std::string(_("The game map could not be loaded: ")) + e.message); + gui2::show_error_message(disp_.video(), std::string(_("The game map could not be loaded: ")) + e.message); return LEVEL_RESULT::QUIT; } catch (mapgen_exception& e) { - gui2::show_error_message(disp.video(), std::string(_("Map generator error: ") + e.message)); + gui2::show_error_message(disp_.video(), std::string(_("Map generator error: ") + e.message)); } catch(config::error& e) { - gui2::show_error_message(disp.video(), _("Error while reading the WML: ") + e.message); + gui2::show_error_message(disp_.video(), _("Error while reading the WML: ") + e.message); return LEVEL_RESULT::QUIT; } catch(twml_exception& e) { - e.show(disp); + e.show(disp_); return LEVEL_RESULT::QUIT; } - if (is_unit_test) { + if (is_unit_test_) { return res; } if(res == LEVEL_RESULT::QUIT) { @@ -340,24 +328,24 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, } if (preferences::delete_saves()) { - savegame::clean_saves(gamestate.classification().label); + savegame::clean_saves(state_.classification().label); } if (preferences::save_replays() && end_level.replay_save) { - savegame::replay_savegame save(gamestate, preferences::save_compression_format()); - save.save_game_automatic(disp.video(), true); + savegame::replay_savegame save(state_, preferences::save_compression_format()); + save.save_game_automatic(disp_.video(), true); } - gamestate.convert_to_start_save(); + state_.convert_to_start_save(); //If there is no next scenario we're done now. - if(gamestate.get_scenario_id().empty()) + if(state_.get_scenario_id().empty()) { return res; } else if(res == LEVEL_RESULT::OBSERVER_END) { // TODO: does it make sense to ask this question if we are currently the host? - const int dlg_res = gui2::show_message(disp.video(), _("Game Over"), + const int dlg_res = gui2::show_message(disp_.video(), _("Game Over"), _("This scenario has ended. Do you want to continue the campaign?"), gui2::tmessage::yes_no_buttons); @@ -366,10 +354,10 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, } } - if (io_type == IO_CLIENT) { + if (mp_info_ && !mp_info_->is_host) { // Opens mp::connect dialog to get a new gamestate. - mp::ui::result wait_res = mp::goto_mp_wait(gamestate, disp, - game_config, res == LEVEL_RESULT::OBSERVER_END); + mp::ui::result wait_res = mp::goto_mp_wait(state_, disp_, + game_config_, res == LEVEL_RESULT::OBSERVER_END); if (wait_res == mp::ui::QUIT) { return LEVEL_RESULT::QUIT; } @@ -377,23 +365,22 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, //The host should send the complete savegame now that also contains the carryvoer sides start. } else { // Retrieve next scenario data. - gamestate.expand_scenario(); + state_.expand_scenario(); - if (gamestate.valid()) { + if (state_.valid()) { //note that although starting_pos is const it might be changed by gamestate.some_non_const_operation() . - const config& starting_pos = gamestate.get_starting_pos(); + const config& starting_pos = state_.get_starting_pos(); - gamestate.mp_settings().num_turns = starting_pos["turns"].to_int(-1); - gamestate.mp_settings().saved_game = false; - gamestate.mp_settings().use_map_settings = starting_pos["force_lock_settings"].to_bool(); + state_.mp_settings().num_turns = starting_pos["turns"].to_int(-1); + state_.mp_settings().saved_game = false; + state_.mp_settings().use_map_settings = starting_pos["force_lock_settings"].to_bool(); - ng::connect_engine_ptr - connect_engine(new ng::connect_engine(gamestate, !network_game, false, mp_players)); + ng::connect_engine_ptr connect_engine(new ng::connect_engine(state_, false, mp_info_)); if (!connect_engine->can_start_game() || (game_config::debug && game_type == game_classification::CAMPAIGN_TYPE::MULTIPLAYER)) { // Opens mp::connect dialog to allow users to make an adjustments for scenario. - mp::ui::result connect_res = mp::goto_mp_connect(disp, - *connect_engine, game_config, gamestate.mp_settings().name); + mp::ui::result connect_res = mp::goto_mp_connect(disp_, + *connect_engine, game_config_, state_.mp_settings().name); if (connect_res == mp::ui::QUIT) { return LEVEL_RESULT::QUIT; } @@ -404,9 +391,9 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, } } - if(gamestate.valid()) { + if(state_.valid()) { // Update the label - gamestate.update_label(); + state_.update_label(); // If this isn't the last scenario, then save the game if(end_level.prescenario_save) { @@ -418,26 +405,26 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, // starting position needs to be empty, // to force a reload of the scenario config. - savegame::scenariostart_savegame save(gamestate, preferences::save_compression_format()); + savegame::scenariostart_savegame save(state_, preferences::save_compression_format()); - save.save_game_automatic(disp.video()); + save.save_game_automatic(disp_.video()); } } } - if (!gamestate.get_scenario_id().empty()) { + if (!state_.get_scenario_id().empty()) { std::string message = _("Unknown scenario: '$scenario|'"); utils::string_map symbols; - symbols["scenario"] = gamestate.get_scenario_id(); + symbols["scenario"] = state_.get_scenario_id(); message = utils::interpolate_variables_into_string(message, &symbols); - gui2::show_error_message(disp.video(), message); + gui2::show_error_message(disp_.video(), message); return LEVEL_RESULT::QUIT; } if (game_type == game_classification::CAMPAIGN_TYPE::SCENARIO){ if (preferences::delete_saves()) { - savegame::clean_saves(gamestate.classification().label); + savegame::clean_saves(state_.classification().label); } } return LEVEL_RESULT::VICTORY; diff --git a/src/game_initialization/playcampaign.hpp b/src/game_initialization/playcampaign.hpp index c856b20564e3..7c78ad30b40b 100644 --- a/src/game_initialization/playcampaign.hpp +++ b/src/game_initialization/playcampaign.hpp @@ -21,28 +21,72 @@ #include "game_end_exceptions.hpp" #include +#include +#include +#include class display; class game_display; class saved_game; class terrain_type_data; +class team; +class playsingle_controller; typedef boost::shared_ptr tdata_cache; class config; -enum io_type_t { - IO_SERVER, - IO_CLIENT +struct mp_campaign_info +{ + mp_campaign_info() + : connected_players() + , is_host() + , skip_replay_until_turn(0) + , skip_replay_blindfolded(false) + , is_connected(true) + { + + } + /// players and observers + std::set connected_players; + bool is_host; + int skip_replay_until_turn; + bool skip_replay_blindfolded; + bool is_connected; +}; + +class campaign_controller +{ + game_display& disp_; + saved_game& state_; + const config& game_config_; + const tdata_cache & tdata_; + const bool is_unit_test_; + bool is_replay_; + mp_campaign_info* mp_info_; +public: + campaign_controller(game_display& disp, saved_game& state, const config& game_config, const tdata_cache & tdata, bool is_unit_test = false) + : disp_(disp) + , state_(state) + , game_config_(game_config) + , tdata_(tdata) + , is_unit_test_(is_unit_test) + , is_replay_(false) + , mp_info_(NULL) + { + } + LEVEL_RESULT play_game(); + LEVEL_RESULT play_replay() + { + is_replay_ = true; + return play_game(); + } + void set_mp_info(mp_campaign_info* mp_info) { mp_info_ = mp_info; } +private: + LEVEL_RESULT playsingle_scenario(end_level_data &end_level); + LEVEL_RESULT playmp_scenario(end_level_data &end_level); + void show_carryover_message(playsingle_controller& playcontroller, const end_level_data& end_level, LEVEL_RESULT res); + static void report_victory(std::ostringstream &report, team& t, int finishing_bonus_per_turn, int turns_left, int finishing_bonus); }; -LEVEL_RESULT play_game(game_display& disp, saved_game& state, - const config& game_config, - const tdata_cache & tdata, - io_type_t io_type=IO_SERVER, - bool skip_replay = false, - bool network_game = false, - bool blindfold_replay = false, - bool is_unit_test = false, - bool is_replay = false); #endif // PLAYCAMPAIGN_H_INCLUDED diff --git a/src/game_initialization/singleplayer.cpp b/src/game_initialization/singleplayer.cpp index 9e1530d0fa0e..0491330ebdaf 100644 --- a/src/game_initialization/singleplayer.cpp +++ b/src/game_initialization/singleplayer.cpp @@ -157,7 +157,7 @@ bool enter_configure_mode(game_display& disp, const config& game_config, saved_g bool enter_connect_mode(game_display& disp, const config& game_config, saved_game& state, bool local_players_only) { - ng::connect_engine connect_eng(state, local_players_only, true); + ng::connect_engine connect_eng(state, true, NULL); if (state.mp_settings().show_connect) { mp::ui::result res; diff --git a/src/game_launcher.cpp b/src/game_launcher.cpp index ec3198f76547..1b47c484ac7a 100644 --- a/src/game_launcher.cpp +++ b/src/game_launcher.cpp @@ -572,7 +572,8 @@ bool game_launcher::play_test() load_game_config_for_game(state_.classification()); try { - play_game(disp(),state_,game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types()); + campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types()); + ccontroller.play_game(); } catch (game::load_game_exception &) { return true; } @@ -604,7 +605,8 @@ int game_launcher::unit_test() load_game_config_for_game(state_.classification()); try { - LEVEL_RESULT res = play_game(disp(),state_,game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), IO_SERVER, false, false, false, true); + campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), true); + LEVEL_RESULT res = ccontroller.play_game(); if (!(res == LEVEL_RESULT::VICTORY) || lg::broke_strict()) { return 1; } @@ -639,8 +641,8 @@ int game_launcher::unit_test() } try { - //LEVEL_RESULT res = play_game(disp(), state_, game_config_manager::get()->game_config(), IO_SERVER, false,false,false,true); - LEVEL_RESULT res = ::play_game(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), IO_SERVER, false, false, false, true, true); + campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types(), true); + LEVEL_RESULT res = ccontroller.play_replay(); if (!(res == LEVEL_RESULT::VICTORY)) { std::cerr << "Observed failure on replay" << std::endl; return 4; @@ -1090,8 +1092,8 @@ void game_launcher::launch_game(RELOAD_GAME_DATA reload) } try { - const LEVEL_RESULT result = play_game(disp(),state_, - game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types()); + campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types()); + LEVEL_RESULT result = ccontroller.play_game(); // don't show The End for multiplayer scenario // change this if MP campaigns are implemented if(result == LEVEL_RESULT::VICTORY && !state_.classification().is_normal_mp_game()) { @@ -1113,8 +1115,8 @@ void game_launcher::launch_game(RELOAD_GAME_DATA reload) void game_launcher::play_replay() { try { - ::play_game(disp(),state_,game_config_manager::get()->game_config(), - game_config_manager::get()->terrain_types(), IO_SERVER, false, false, false, false, true); + campaign_controller ccontroller(disp(), state_, game_config_manager::get()->game_config(), game_config_manager::get()->terrain_types()); + ccontroller.play_replay(); clear_loaded_game(); } catch (game::load_game_exception &) { diff --git a/src/gui/dialogs/lobby_main.cpp b/src/gui/dialogs/lobby_main.cpp index b2da61a09fb7..cc432d973169 100644 --- a/src/gui/dialogs/lobby_main.cpp +++ b/src/gui/dialogs/lobby_main.cpp @@ -1652,7 +1652,7 @@ bool tlobby_main::do_game_join(int idx, bool observe) } network::send_data(response, 0); if(observe && game.started) { - playmp_controller::set_replay_last_turn(game.current_turn); + // playmp_controller::set_replay_last_turn(game.current_turn); } return true; } diff --git a/src/hotkey_handler_mp.cpp b/src/hotkey_handler_mp.cpp index 7b310efe11d2..0b1137bff8f4 100644 --- a/src/hotkey_handler_mp.cpp +++ b/src/hotkey_handler_mp.cpp @@ -57,7 +57,7 @@ bool playmp_controller::hotkey_handler::can_execute_command(const hotkey::hotkey { bool has_next_scenario = !gamestate().gamedata_.next_scenario().empty() && gamestate().gamedata_.next_scenario() != "null"; - return playsingle_controller_.is_host() || !has_next_scenario; + return playmp_controller_.is_host() || !has_next_scenario; } else { diff --git a/src/playmp_controller.cpp b/src/playmp_controller.cpp index 97f1e8b4f2d8..263962c16bbe 100644 --- a/src/playmp_controller.cpp +++ b/src/playmp_controller.cpp @@ -26,6 +26,7 @@ #include "mp_ui_alerts.hpp" #include "playturn.hpp" #include "preferences.hpp" +#include "game_initialization/playcampaign.hpp" #include "resources.hpp" #include "savegame.hpp" #include "sound.hpp" @@ -41,29 +42,25 @@ static lg::log_domain log_engine("engine"); #define LOG_NG LOG_STREAM(info, log_engine) -unsigned int playmp_controller::replay_last_turn_ = 0; - playmp_controller::playmp_controller(const config& level, saved_game& state_of_game, const config& game_config, const tdata_cache & tdata, CVideo& video, - bool skip_replay, bool blindfold_replay_, bool is_host) + mp_campaign_info* mp_info) : playsingle_controller(level, state_of_game, - game_config, tdata, video, skip_replay || blindfold_replay_) //this || means that if blindfold is enabled, quick replays will be on. + game_config, tdata, video, mp_info && mp_info->skip_replay_until_turn != 0) //this || means that if blindfold is enabled, quick replays will be on. , network_processing_stopped_(false) - , blindfold_(*gui_,blindfold_replay_) + , blindfold_(*gui_, mp_info && mp_info->skip_replay_blindfolded) + , mp_info_(mp_info) { hotkey_handler_.reset(new hotkey_handler(*this, saved_game_)); //upgrade hotkey handler to the mp (network enabled) version - turn_data_.set_host(is_host); + //turn_data_.set_host(is_host); turn_data_.host_transfer().attach_handler(this); // We stop quick replay if play isn't yet past turn 1 - if ( replay_last_turn_ <= 1) + if (!mp_info_ || mp_info_->skip_replay_until_turn > 0) { skip_replay_ = false; } - if (blindfold_replay_) { - LOG_NG << "Putting on the blindfold now " << std::endl; - } } playmp_controller::~playmp_controller() { @@ -73,10 +70,6 @@ playmp_controller::~playmp_controller() { } catch (...) {} } -void playmp_controller::set_replay_last_turn(unsigned int turn){ - replay_last_turn_ = turn; -} - void playmp_controller::start_network(){ network_processing_stopped_ = false; LOG_NG << "network processing activated again"; @@ -345,7 +338,7 @@ void playmp_controller::play_network_turn(){ { if (!network_processing_stopped_) { process_network_data(); - if (replay_last_turn_ <= turn()) { + if (!mp_info_ || mp_info_->skip_replay_until_turn > 0) { skip_replay_ = false; } } @@ -399,12 +392,18 @@ void playmp_controller::handle_generic_event(const std::string& name){ turn_data_.send_data(); } else if (name == "host_transfer"){ + assert(mp_info_); + mp_info_->is_host = true; if (linger_){ end_turn_enable(true); gui_->invalidate_theme(); } } } +bool playmp_controller::is_host() const +{ + return !mp_info_ || mp_info_->is_host; +} void playmp_controller::do_idle_notification() { diff --git a/src/playmp_controller.hpp b/src/playmp_controller.hpp index af34e1c06ea6..70f26674483b 100644 --- a/src/playmp_controller.hpp +++ b/src/playmp_controller.hpp @@ -20,19 +20,16 @@ #include "syncmp_handler.hpp" class turn_info; - +struct mp_campaign_info; class playmp_controller : public playsingle_controller, public syncmp_handler { public: playmp_controller(const config& level, saved_game& state_of_game, const config& game_config, const tdata_cache & tdata, CVideo& video, - bool skip_replay, bool blindfold_replay, bool is_host); + mp_campaign_info* mp_info); virtual ~playmp_controller(); - static unsigned int replay_last_turn() { return replay_last_turn_; } - static void set_replay_last_turn(unsigned int turn); - void maybe_linger(); void process_oos(const std::string& err_msg) const; @@ -61,6 +58,7 @@ class playmp_controller : public playsingle_controller, public syncmp_handler mutable bool network_processing_stopped_; virtual void on_not_observer(); + bool is_host() const; void remove_blindfold(); blindfold blindfold_; @@ -68,7 +66,7 @@ class playmp_controller : public playsingle_controller, public syncmp_handler void set_end_scenario_button(); void reset_end_scenario_button(); void process_network_data(); - static unsigned int replay_last_turn_; + mp_campaign_info* mp_info_; }; #endif diff --git a/src/playsingle_controller.cpp b/src/playsingle_controller.cpp index 00799f27b0eb..c113118d55bb 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -601,11 +601,6 @@ void playsingle_controller::check_objectives() } -bool playsingle_controller::is_host() const -{ - return turn_data_.is_host(); -} - void playsingle_controller::maybe_linger() { // mouse_handler expects at least one team for linger mode to work. diff --git a/src/playsingle_controller.hpp b/src/playsingle_controller.hpp index 4226fee57bd3..1ee5a50b878a 100644 --- a/src/playsingle_controller.hpp +++ b/src/playsingle_controller.hpp @@ -48,7 +48,6 @@ class playsingle_controller : public play_controller virtual void check_objectives(); virtual void on_not_observer() {} - bool is_host() const ; virtual void maybe_linger(); void end_turn(); diff --git a/src/playturn.cpp b/src/playturn.cpp index bd35708e3f68..fb31ee21e500 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -55,8 +55,7 @@ static lg::log_domain log_network("network"); turn_info::turn_info(replay_network_sender &replay_sender,playturn_network_adapter &network_reader) : replay_sender_(replay_sender), host_transfer_("host_transfer"), - network_reader_(network_reader), - is_host_(true) + network_reader_(network_reader) { } @@ -366,7 +365,6 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg //If this client becomes the new host, notify the play_controller object about it else if (const config &cfg_host_transfer = cfg.child("host_transfer")){ if (cfg_host_transfer["value"] == "1") { - is_host_ = true; host_transfer_.notify_observers(); } } diff --git a/src/playturn.hpp b/src/playturn.hpp index 255a54f20792..90dff6586853 100644 --- a/src/playturn.hpp +++ b/src/playturn.hpp @@ -59,9 +59,6 @@ class turn_info events::generic_event& host_transfer() { return host_transfer_; } - - bool is_host() const { return is_host_; } - void set_host(bool val) { is_host_ = val; } static PROCESS_DATA_RESULT replay_to_process_data_result(REPLAY_RETURN replayreturn); private: static void change_controller(int side, const std::string& controller); @@ -75,8 +72,6 @@ class turn_info events::generic_event host_transfer_; playturn_network_adapter& network_reader_; - - bool is_host_; }; #endif diff --git a/src/tests/test_mp_connect.cpp b/src/tests/test_mp_connect.cpp index 97ee262a2be9..051f8689ec80 100644 --- a/src/tests/test_mp_connect.cpp +++ b/src/tests/test_mp_connect.cpp @@ -43,7 +43,7 @@ class test_connect_engine : public ng::connect_engine { public: test_connect_engine(game_display& /*disp*/, saved_game& gamestate) : - ng::connect_engine(gamestate, true, true) + ng::connect_engine(gamestate, true, NULL) {} }; diff --git a/src/tests/utils/play_scenario.cpp b/src/tests/utils/play_scenario.cpp index 6de18e930d20..4da16012fbee 100644 --- a/src/tests/utils/play_scenario.cpp +++ b/src/tests/utils/play_scenario.cpp @@ -118,7 +118,9 @@ namespace test_utils { state.set_carryover_sides_start( config_of("next_scenario", id_) ); - play_game(get_fake_display(1024, 768), state, game_config_, tdata_); + campaign_controller controller(get_fake_display(1024, 768), state, game_config_, tdata_); + + controller.play_game(); } std::string play_scenario::get_unit_id(const map_location &loc)