From fb68590637c766b7f19256d22559eb4849e2c71c Mon Sep 17 00:00:00 2001 From: gfgtdf Date: Tue, 3 Mar 2015 18:49:26 +0100 Subject: [PATCH] more refactor of play_side and game_end_exception.hpp Instead of having different exceptions (ai_end_turn_exception, end_level_exception and restart_turn_exception) we now have one exception (return_to_play_side_exception) that is used to escape from play_ai_turn or play_slice and is catched in the play_side related functions. The information why we returned to play_side is already stored in the playsingle_controller object. That is also why we do not need the possible_end_play_signal return value anymore. play_controller::check_victory does not throw exceptions anymore Also do_replay and thus turn_info::process_network_data don't throw exceptions anymore when the scenario is finished by victory or defeat. Instead it returns REPLAY_FOUND_END_LEVEL, this means return_to_play_side_exception is not thrown during replay at all. this also fixed up a previous commit 'refactor play_side' where accidently play_idle_loop was called instead of play_human_turn in play_side. --- src/actions/attack.cpp | 21 +-- src/actions/move.cpp | 1 + src/ai/actions.cpp | 12 +- src/ai/manager.cpp | 18 +- src/dialogs.cpp | 2 +- src/game_end_exceptions.hpp | 106 +----------- src/game_events/menu_item.cpp | 2 +- src/game_initialization/playcampaign.cpp | 16 +- src/hotkey/command_executor.hpp | 8 +- src/hotkey_handler_replay.hpp | 8 +- src/menu_events.cpp | 13 +- src/mouse_events.cpp | 2 +- src/play_controller.cpp | 34 +++- src/play_controller.hpp | 13 +- src/playmp_controller.cpp | 204 ++++++++++------------- src/playmp_controller.hpp | 10 +- src/playsingle_controller.cpp | 141 ++++++++-------- src/playsingle_controller.hpp | 16 +- src/playturn.cpp | 2 + src/playturn.hpp | 6 +- src/replay.cpp | 7 +- src/replay.hpp | 3 +- src/replay_controller.cpp | 127 +++++++------- src/replay_controller.hpp | 24 +-- src/synced_context.cpp | 43 +++-- src/synced_context.hpp | 5 +- src/wesnoth.cpp | 6 +- src/whiteboard/move.cpp | 3 +- src/whiteboard/recall.cpp | 3 +- src/whiteboard/side_actions.cpp | 3 +- 30 files changed, 372 insertions(+), 487 deletions(-) diff --git a/src/actions/attack.cpp b/src/actions/attack.cpp index d409a431790c..349cf3d26700 100644 --- a/src/actions/attack.cpp +++ b/src/actions/attack.cpp @@ -1497,25 +1497,8 @@ static void advance_unit_at(const map_location& loc, const ai::unit_advancements void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display, const ai::unit_advancements_aspect& ai_advancement) -{ try - { - attack_unit(attacker, defender, attack_with, defend_with, update_display); - } - catch(end_level_exception&) - { - - unit_map::const_iterator atku = resources::units->find(attacker); - - if (atku != resources::units->end()) { - advance_unit_at(attacker, ai_advancement); - } - - unit_map::const_iterator defu = resources::units->find(defender); - if (defu != resources::units->end()) { - advance_unit_at(defender, ai_advancement); - } - throw; - } +{ + attack_unit(attacker, defender, attack_with, defend_with, update_display); unit_map::const_iterator atku = resources::units->find(attacker); if (atku != resources::units->end()) { advance_unit_at(attacker, ai_advancement); diff --git a/src/actions/move.cpp b/src/actions/move.cpp index 494da5ba491f..0cc3b2cbe558 100644 --- a/src/actions/move.cpp +++ b/src/actions/move.cpp @@ -1270,6 +1270,7 @@ size_t move_unit_and_record(const std::vector &steps, set_scontext_synced sync; size_t r = move_unit_internal(undo_stack, show_move, interrupted, mover); resources::controller->check_victory(); + resources::controller->maybe_throw_return_to_play_side(); sync.do_final_checkup(); return r; } diff --git a/src/ai/actions.cpp b/src/ai/actions.cpp index bf4045bbc3b8..c61da00a4dd7 100644 --- a/src/ai/actions.cpp +++ b/src/ai/actions.cpp @@ -94,7 +94,7 @@ void action_result::execute() if (is_success()){ try { do_execute(); - } catch (end_level_exception&) { + } catch (return_to_play_side_exception&) { if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked. This may cause bugs! " << std::endl; } //Demotes to DBG "unchecked result" warning throw; } @@ -102,15 +102,6 @@ void action_result::execute() if (is_success()){ check_after(); } - if(resources::controller->is_end_turn()) { - throw ai_end_turn_exception(); - } - - if(playsingle_controller* psc = dynamic_cast(resources::controller)) { - if(psc->get_player_type_changed()) { - throw restart_turn_exception(); - } - } is_execution_ = false; } @@ -302,6 +293,7 @@ void attack_result::do_execute() set_scontext_synced sync; attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_); resources::controller->check_victory(); + resources::controller->maybe_throw_return_to_play_side(); sync.do_final_checkup(); } else diff --git a/src/ai/manager.cpp b/src/ai/manager.cpp index 77336605909f..84550be86c7f 100644 --- a/src/ai/manager.cpp +++ b/src/ai/manager.cpp @@ -788,18 +788,12 @@ void manager::play_turn( side_number side ){ num_interact_ = 0; const int turn_start_time = SDL_GetTicks(); /*hack. @todo 1.9 rework via extended event system*/ - try { - get_ai_info().recent_attacks.clear(); - interface& ai_obj = get_active_ai_for_side(side); - resources::game_events->pump().fire("ai turn"); - raise_turn_started(); - ai_obj.new_turn(); - ai_obj.play_turn(); - } - catch (const ai_end_turn_exception&) { - } - catch(const restart_turn_exception&) { - } + get_ai_info().recent_attacks.clear(); + interface& ai_obj = get_active_ai_for_side(side); + resources::game_events->pump().fire("ai turn"); + raise_turn_started(); + ai_obj.new_turn(); + ai_obj.play_turn(); const int turn_end_time= SDL_GetTicks(); DBG_AI_MANAGER << "side " << side << ": number of user interactions: "<id())); + synced_context::run_and_throw("disband", replay_helper::get_disband(dismissed_unit->id())); //recorder.add_disband(dismissed_unit->id()); //recall_list.erase(dismissed_unit); diff --git a/src/game_end_exceptions.hpp b/src/game_end_exceptions.hpp index 4e1282848565..7c05a84bab9b 100644 --- a/src/game_end_exceptions.hpp +++ b/src/game_end_exceptions.hpp @@ -40,74 +40,22 @@ MAKE_ENUM(LEVEL_RESULT, MAKE_ENUM_STREAM_OPS1(LEVEL_RESULT) /** - * Exception used to escape form the ai code in case of [end_turn]. + * Exception used to escape form the ai or ui code to playsingle_controller::play_side. + * Never thrown during replays. */ -class ai_end_turn_exception - : public tlua_jailbreak_exception, public std::exception +class return_to_play_side_exception : public tlua_jailbreak_exception, public std::exception { public: - ai_end_turn_exception() + return_to_play_side_exception() : tlua_jailbreak_exception() , std::exception() { } - const char * what() const throw() { return "ai_end_turn_exception"; } + const char * what() const throw() { return "return_to_play_side_exception"; } private: - IMPLEMENT_LUA_JAILBREAK_EXCEPTION(ai_end_turn_exception) -}; - -/** - * Exception used to signal the end of a player turn. - */ -class restart_turn_exception - : public tlua_jailbreak_exception, public std::exception -{ -public: - - restart_turn_exception() - : tlua_jailbreak_exception() - , std::exception() - { - } - const char * what() const throw() { return "restart_turn_exception"; } - -private: - - IMPLEMENT_LUA_JAILBREAK_EXCEPTION(restart_turn_exception) -}; - -/** - * Struct used to transmit info caught from an end_turn_exception. - */ -struct end_level_struct -{ -}; - -/** - * Exception used to signal the end of a scenario. - */ -class end_level_exception - : public tlua_jailbreak_exception - , public std::exception -{ -public: - - end_level_exception() - : tlua_jailbreak_exception() - , std::exception() - { - } - - end_level_struct to_struct() { - return end_level_struct(); - } - - const char * what() const throw() { return "end_level_exception"; } -private: - - IMPLEMENT_LUA_JAILBREAK_EXCEPTION(end_level_exception) + IMPLEMENT_LUA_JAILBREAK_EXCEPTION(return_to_play_side_exception) }; class quit_game_exception @@ -125,48 +73,6 @@ class quit_game_exception private: IMPLEMENT_LUA_JAILBREAK_EXCEPTION(quit_game_exception) }; -/** - * The two end_*_exceptions are caught and transformed to this signaling object - */ -typedef boost::optional possible_end_play_signal; - -#define HANDLE_END_PLAY_SIGNAL( X )\ -do\ -{\ - try {\ - X;\ - } catch (end_level_exception & e) {\ - return possible_end_play_signal(e.to_struct());\ - }\ -}\ -while(0) - - - -#define PROPOGATE_END_PLAY_SIGNAL( X )\ -do\ -{\ - possible_end_play_signal temp;\ - temp = X;\ - if (temp) {\ - return temp;\ - }\ -}\ -while(0) - - - -#define HANDLE_AND_PROPOGATE_END_PLAY_SIGNAL( X )\ -do\ -{\ - possible_end_play_signal temp;\ - HANDLE_END_PLAY_SIGNAL( temp = X );\ - if (temp) {\ - return temp;\ - }\ -}\ -while(0) - /** * The non-persistent part of end_level_data diff --git a/src/game_events/menu_item.cpp b/src/game_events/menu_item.cpp index 713a05700df5..36854eebc9ab 100644 --- a/src/game_events/menu_item.cpp +++ b/src/game_events/menu_item.cpp @@ -208,7 +208,7 @@ void wml_menu_item::fire_event(const map_location & event_hex, const game_data & // note that there coudn't be a user choice during the last "select" event because it didn't run in a synced context. if ( needs_select_ && last_select.valid() ) { - synced_context::run_in_synced_context("fire_event", replay_helper::get_event(event_name_, event_hex, &last_select)); + synced_context::run_and_throw("fire_event", replay_helper::get_event(event_name_, event_hex, &last_select)); } else { diff --git a/src/game_initialization/playcampaign.cpp b/src/game_initialization/playcampaign.cpp index 987e6a1f5bcb..03a8db980be2 100644 --- a/src/game_initialization/playcampaign.cpp +++ b/src/game_initialization/playcampaign.cpp @@ -153,7 +153,7 @@ LEVEL_RESULT play_replay(display& disp, saved_game& gamestate, const config& gam gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message); } - } catch(quit_game_exception& e) { + } catch(quit_game_exception&) { LOG_NG << "The replay was aborted\n"; return QUIT; } catch(game::game_error& e) { @@ -207,11 +207,7 @@ static LEVEL_RESULT playsingle_scenario(const config& game_config, show_carryover_message(state_of_game, playcontroller, disp, end_level, res); if(!disp.video().faked()) { - try { - playcontroller.maybe_linger(); - } catch(end_level_exception&) { - return QUIT; - } + playcontroller.maybe_linger(); } state_of_game.set_snapshot(playcontroller.to_config()); return res; @@ -249,11 +245,7 @@ static LEVEL_RESULT playmp_scenario(const config& game_config, } if(!disp.video().faked()) { - try { - playcontroller.maybe_linger(); - } catch(end_level_exception&) { - return QUIT; - } + playcontroller.maybe_linger(); } playcontroller.update_savegame_snapshot(); return res; @@ -300,7 +292,7 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate, } catch(game::load_game_failed& e) { gui2::show_error_message(disp.video(), _("The game could not be loaded: ") + e.message); return QUIT; - } catch(quit_game_exception& e) { + } catch(quit_game_exception&) { LOG_NG << "The game was aborted\n"; return QUIT; } catch(game::game_error& e) { diff --git a/src/hotkey/command_executor.hpp b/src/hotkey/command_executor.hpp index df81908667c1..141d82ad6066 100644 --- a/src/hotkey/command_executor.hpp +++ b/src/hotkey/command_executor.hpp @@ -79,12 +79,12 @@ class command_executor virtual void ai_formula() {} virtual void clear_messages() {} virtual void change_language() {} - virtual possible_end_play_signal play_replay() { return boost::none; } + virtual void play_replay() { } virtual void reset_replay() {} virtual void stop_replay() {} - virtual possible_end_play_signal replay_next_turn() { return boost::none; } - virtual possible_end_play_signal replay_next_side() { return boost::none; } - virtual possible_end_play_signal replay_next_move() { return boost::none; } + virtual void replay_next_turn() { } + virtual void replay_next_side() { } + virtual void replay_next_move() { } virtual void replay_show_everything() {} virtual void replay_show_each() {} virtual void replay_show_team1() {} diff --git a/src/hotkey_handler_replay.hpp b/src/hotkey_handler_replay.hpp index 04ad321bebb8..c378cde6a2a7 100644 --- a/src/hotkey_handler_replay.hpp +++ b/src/hotkey_handler_replay.hpp @@ -42,13 +42,13 @@ class replay_controller::hotkey_handler : public play_controller::hotkey_handler { return replay_controller_.reset_replay(); } virtual void stop_replay() OVERRIDE { return replay_controller_.stop_replay(); } - virtual possible_end_play_signal play_replay() OVERRIDE + virtual void play_replay() OVERRIDE { return replay_controller_.play_replay(); } - virtual possible_end_play_signal replay_next_turn() OVERRIDE + virtual void replay_next_turn() OVERRIDE { return replay_controller_.replay_next_turn(); } - virtual possible_end_play_signal replay_next_side() OVERRIDE + virtual void replay_next_side() OVERRIDE { return replay_controller_.replay_next_side(); } - virtual possible_end_play_signal replay_next_move() OVERRIDE + virtual void replay_next_move() OVERRIDE { return replay_controller_.replay_next_move(); } virtual void replay_show_everything() OVERRIDE { return replay_controller_.replay_show_everything(); } diff --git a/src/menu_events.cpp b/src/menu_events.cpp index 39c4890c9c9e..409f603c57de 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -663,7 +663,7 @@ bool menu_handler::do_recruit(const std::string &name, int side_num, // Do the recruiting. - synced_context::run_in_synced_context("recruit", replay_helper::get_recruit(u_type->id(), loc, recruited_from)); + synced_context::run_and_throw("recruit", replay_helper::get_recruit(u_type->id(), loc, recruited_from)); return true; } return false; @@ -749,11 +749,10 @@ void menu_handler::recall(int side_num, const map_location &last_hex) } if (!pc_.get_whiteboard() || !pc_.get_whiteboard()->save_recall(*recall_list_team->at(res), side_num, recall_location)) { - bool success = synced_context::run_in_synced_context("recall", + bool success = synced_context::run_and_throw("recall", replay_helper::get_recall(recall_list_team->at(res)->id(), recall_location, recall_from), true, true, - true, synced_context::ignore_error_function); if(!success) @@ -795,12 +794,12 @@ void menu_handler::toggle_shroud_updates(int side_num) if (!auto_shroud) update_shroud_now(side_num); // Toggle the setting and record this. - synced_context::run_in_synced_context("auto_shroud", replay_helper::get_auto_shroud(!auto_shroud)); + synced_context::run_and_throw("auto_shroud", replay_helper::get_auto_shroud(!auto_shroud)); } void menu_handler::update_shroud_now(int /* side_num */) { - synced_context::run_in_synced_context("update_shroud", replay_helper::get_update_shroud()); + synced_context::run_and_throw("update_shroud", replay_helper::get_update_shroud()); } @@ -2971,7 +2970,7 @@ void console_handler::do_next_level() e.proceed_to_next_level = true; e.is_victory = true; menu_handler_.pc_.set_end_level_data(e); - throw end_level_exception(); + throw return_to_play_side_exception(); } void console_handler::do_choose_level() { @@ -3021,7 +3020,7 @@ void console_handler::do_choose_level() { e.proceed_to_next_level = true; e.is_victory = true; menu_handler_.pc_.set_end_level_data(e); - throw end_level_exception(); + throw return_to_play_side_exception(); } } diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp index 9878a3e7cdf5..3fdb8dd9bbdc 100644 --- a/src/mouse_events.cpp +++ b/src/mouse_events.cpp @@ -1156,7 +1156,7 @@ void mouse_handler::attack_enemy_(const map_location& att_loc const tod_manager & tod_man = pc_.get_tod_manager_const(); - synced_context::run_in_synced_context("attack", replay_helper::get_attack(attacker_loc, defender_loc, att.attack_num, def.attack_num, + synced_context::run_and_throw("attack", replay_helper::get_attack(attacker_loc, defender_loc, att.attack_num, def.attack_num, attacker->type_id(), defender->type_id(), att.level, def.level, tod_man.turn(), tod_man.get_time_of_day())); } diff --git a/src/play_controller.cpp b/src/play_controller.cpp index a628436bcf20..6210ffcff1e8 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -300,7 +300,6 @@ void play_controller::fire_prestart() update_locker lock_display(gui_->video()); gamestate_.gamedata_.set_phase(game_data::PRESTART); pump().fire("prestart"); - check_end_level(); // prestart event may modify start turn with WML, reflect any changes. start_turn_ = turn(); gamestate_.gamedata_.get_variable("turn_number") = int(start_turn_); @@ -310,11 +309,10 @@ void play_controller::fire_start() { gamestate_.gamedata_.set_phase(game_data::START); pump().fire("start"); - check_end_level(); // start event may modify start turn with WML, reflect any changes. start_turn_ = turn(); gamestate_.gamedata_.get_variable("turn_number") = int(start_turn_); - + check_objectives(); // prestart and start events may modify the initial gold amount, // reflect any changes. BOOST_FOREACH(team& tm, gamestate_.board_.teams_) @@ -867,9 +865,10 @@ void play_controller::check_victory() { return; } - - check_end_level(); - + check_objectives(); + if (is_regular_game_end()) { + return; + } bool continue_level, found_player, found_network_player, invalidate_all; std::set not_defeated; @@ -885,7 +884,10 @@ void play_controller::check_victory() if (found_player || found_network_player) { pump().fire("enemies defeated"); - check_end_level(); + check_objectives(); + if (is_regular_game_end()) { + return; + } } DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl; @@ -914,7 +916,6 @@ void play_controller::check_victory() el_data.proceed_to_next_level = found_player || found_network_player; el_data.is_victory = found_player; set_end_level_data(el_data); - throw end_level_exception(); } void play_controller::process_oos(const std::string& msg) const @@ -927,7 +928,7 @@ void play_controller::process_oos(const std::string& msg) const update_savegame_snapshot(); savegame::oos_savegame save(saved_game_, *gui_); - save.save_game_interactive(gui_->video(), message.str(), gui::YES_NO); // can throw end_level_exception + save.save_game_interactive(gui_->video(), message.str(), gui::YES_NO); // can throw quit_game_exception } void play_controller::update_gui_to_player(const int team_index, const bool observe) @@ -988,3 +989,18 @@ bool play_controller::is_browsing() const || !init_side_done_ || this->gamestate_.gamedata_.phase() != game_data::PLAY; } + +void play_controller::play_slice_catch() +{ + if(should_return_to_play_side()) { + return; + } + try + { + play_slice(); + } + catch(const return_to_play_side_exception&) + { + assert(should_return_to_play_side()); + } +} diff --git a/src/play_controller.hpp b/src/play_controller.hpp index 785c1378b1ce..ac5647eaf174 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -111,12 +111,12 @@ class play_controller : public controller_base, public events::observer, public void init_side_end(); virtual void force_end_turn() = 0; - virtual void check_end_level() = 0; + virtual void check_objectives() = 0; virtual void on_not_observer() = 0; /** * Asks the user whether to continue on an OOS error. - * @throw end_level_exception If the user wants to abort. + * @throw quit_game_exception If the user wants to abort. */ virtual void process_oos(const std::string& msg) const; @@ -161,7 +161,7 @@ class play_controller : public controller_base, public events::observer, public } /** - * Checks to see if a side has won, and throws an end_level_exception. + * Checks to see if a side has won. * Will also remove control of villages from sides with dead leaders. */ void check_victory(); @@ -209,8 +209,13 @@ class play_controller : public controller_base, public events::observer, public bool get_disallow_recall() { return level_["disallow_recall"].to_bool(); } void update_savegame_snapshot() const; -protected: + virtual bool should_return_to_play_side() + { return is_regular_game_end(); } + void maybe_throw_return_to_play_side() + { if(should_return_to_play_side()) { throw return_to_play_side_exception(); } } +protected: + void play_slice_catch(); game_display& get_display(); bool have_keyboard_focus(); void process_focus_keydown_event(const SDL_Event& event); diff --git a/src/playmp_controller.cpp b/src/playmp_controller.cpp index 0bf16921ab17..435c681e7aa6 100644 --- a/src/playmp_controller.cpp +++ b/src/playmp_controller.cpp @@ -86,7 +86,7 @@ void playmp_controller::stop_network(){ LOG_NG << "network processing stopped"; } -possible_end_play_signal playmp_controller::play_side() +void playmp_controller::play_side() { mp_ui_alerts::turn_changed(current_team().current_player()); // Proceed with the parent function. @@ -105,80 +105,79 @@ void playmp_controller::remove_blindfold() { } } -possible_end_play_signal playmp_controller::play_human_turn() +void playmp_controller::play_linger_turn() { - LOG_NG << "playmp::play_human_turn...\n"; + if (is_host()) { + end_turn_enable(true); + } + + while(!end_turn_) { + config cfg; + if(network_reader_.read(cfg)) { + if(turn_data_.process_network_data(cfg) == turn_info::PROCESS_END_LINGER) + { + end_turn(); + } + } + play_slice(); + gui_->draw(); + } +} +void playmp_controller::play_human_turn() +{ + LOG_NG << "playmp::play_human_turn...\n"; + assert(!linger_); remove_blindfold(); boost::scoped_ptr timer; - if(!linger_ && saved_game_.mp_settings().mp_countdown) { + if(saved_game_.mp_settings().mp_countdown) { timer.reset(new countdown_clock(current_team())); } show_turn_dialog(); if(undo_stack_->can_undo()) { // If we reload a networked mp game we cannot undo moved made before the save // Becasue other players already received them - synced_context::run_in_synced_context("update_shroud", replay_helper::get_update_shroud()); + synced_context::run_and_store("update_shroud", replay_helper::get_update_shroud()); undo_stack_->clear(); } if (!preferences::disable_auto_moves()) { - HANDLE_END_PLAY_SIGNAL(execute_gotos()); + execute_gotos(); } - if (!linger_ || is_host()) { - end_turn_enable(true); - } - while(!end_turn_ && !player_type_changed_) { + end_turn_enable(true); + while(!should_return_to_play_side()) { try { - config cfg; - - if(network_reader_.read(cfg)) { - turn_info::PROCESS_DATA_RESULT res; - HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg) ); - if (res == turn_info::PROCESS_RESTART_TURN) - { - player_type_changed_ = true; - // Clean undo stack if turn has to be restarted (losing control) - if ( undo_stack_->can_undo() ) - { - font::floating_label flabel(_("Undoing moves not yet transmitted to the server.")); - - SDL_Color color = {255,255,255,255}; - flabel.set_color(color); - SDL_Rect rect = gui_->map_area(); - flabel.set_position(rect.w/2, rect.h/2); - flabel.set_lifetime(150); - flabel.set_clip_rect(rect); - - font::add_floating_label(flabel); - } - - while( undo_stack_->can_undo() ) - undo_stack_->undo(); - - return boost::none; - } - else if(res == turn_info::PROCESS_END_LINGER) + process_network_data(); + if (player_type_changed_) + { + // Clean undo stack if turn has to be restarted (losing control) + if ( undo_stack_->can_undo() ) { - if(!linger_) - replay::process_error("Received unexpected next_scenario during the game"); - else - { - //we end the turn immidiately to prevent receiving data of the next scenario while we are not playing it. - end_turn(); - } + font::floating_label flabel(_("Undoing moves not yet transmitted to the server.")); + + SDL_Color color = {255,255,255,255}; + flabel.set_color(color); + SDL_Rect rect = gui_->map_area(); + flabel.set_position(rect.w/2, rect.h/2); + flabel.set_lifetime(150); + flabel.set_clip_rect(rect); + + font::add_floating_label(flabel); } + + while( undo_stack_->can_undo() ) + undo_stack_->undo(); + } - HANDLE_END_PLAY_SIGNAL( play_slice() ); + play_slice_catch(); if(timer) { SDL_Delay(1); bool time_left = timer->update(); if(!time_left) { - //End the turn of there is no time left. - return boost::none; + end_turn_ = true; } } } @@ -191,38 +190,22 @@ possible_end_play_signal playmp_controller::play_human_turn() gui_->draw(); } - return boost::none; } -possible_end_play_signal playmp_controller::play_idle_loop() +void playmp_controller::play_idle_loop() { LOG_NG << "playmp::play_human_turn...\n"; remove_blindfold(); - while (!end_turn_ && !player_type_changed_) + while (!should_return_to_play_side()) { try { - config cfg; - if(network_reader_.read(cfg)) { - turn_info::PROCESS_DATA_RESULT res; - HANDLE_END_PLAY_SIGNAL( res = turn_data_.process_network_data(cfg) ); - - if (res == turn_info::PROCESS_RESTART_TURN) - { - player_type_changed_ = true; - return boost::none; - } - } - - HANDLE_END_PLAY_SIGNAL ( play_slice() ); - - if (!linger_) { + process_network_data(); + play_slice_catch(); SDL_Delay(1); - } - - gui_->draw(); + gui_->draw(); } catch(...) { @@ -231,7 +214,6 @@ possible_end_play_signal playmp_controller::play_idle_loop() } turn_data_.send_data(); } - return boost::none; } void playmp_controller::set_end_scenario_button() @@ -280,19 +262,13 @@ void playmp_controller::linger() player_number_ = first_player_; turn_data_.send_data(); end_turn_ = false; - play_human_turn(); + play_linger_turn(); after_human_turn(); LOG_NG << "finished human turn" << std::endl; } catch (game::load_game_exception&) { LOG_NG << "caught load-game-exception" << std::endl; // this should not happen, the option to load a game is disabled throw; - } catch (end_level_exception&) { - // thrown if the host ends the scenario and let us advance - // to the next level - LOG_NG << "caught end-level-exception" << std::endl; - reset_end_scenario_button(); - throw; } catch (network::error&) { LOG_NG << "caught network-error-exception" << std::endl; quit = false; @@ -327,7 +303,7 @@ void playmp_controller::wait_for_upload() throw_quit_game_exception(); } - } catch(const end_level_exception&) { + } catch(const quit_game_exception&) { network_reader_.set_source(playturn_network_adapter::read_network); turn_data_.send_data(); throw; @@ -362,48 +338,22 @@ void playmp_controller::finish_side_turn(){ play_controller::finish_side_turn(); } -possible_end_play_signal playmp_controller::play_network_turn(){ +void playmp_controller::play_network_turn(){ LOG_NG << "is networked...\n"; end_turn_enable(false); turn_data_.send_data(); - for(;;) { - - if (!network_processing_stopped_){ - config cfg; - if(network_reader_.read(cfg)) { - if (replay_last_turn_ <= turn()){ - skip_replay_ = false; - } - turn_info::PROCESS_DATA_RESULT result; - HANDLE_END_PLAY_SIGNAL ( result = turn_data_.process_network_data(cfg) ); - if(player_type_changed_ == true) - { - //we received a player change/quit during waiting in get_user_choice/synced_context::pull_remote_user_input - return boost::none; - } - if (result == turn_info::PROCESS_RESTART_TURN) { - player_type_changed_ = true; - return boost::none; - } else if (result == turn_info::PROCESS_END_TURN) { - break; - } - } - /* - we might have data left in replay that we recieved during prestart events. (or maybe other events.) - */ - else if(!recorder.at_end()) - { - if(do_replay() == REPLAY_FOUND_END_TURN) - { - break; - } + while(!should_return_to_play_side()) + { + if (!network_processing_stopped_) { + process_network_data(); + if (replay_last_turn_ <= turn()) { + skip_replay_ = false; } } - HANDLE_END_PLAY_SIGNAL( play_slice() ); - + play_slice_catch(); if (!network_processing_stopped_){ turn_data_.send_data(); } @@ -412,7 +362,6 @@ possible_end_play_signal playmp_controller::play_network_turn(){ } LOG_NG << "finished networked...\n"; - return boost::none; } @@ -498,3 +447,30 @@ void playmp_controller::send_user_choice() { turn_data_.send_data(); } + +void playmp_controller::process_network_data() +{ + if(should_return_to_play_side()) { + return; + } + turn_info::PROCESS_DATA_RESULT res = turn_info::PROCESS_CONTINUE; + config cfg; + if(!recorder.at_end()) { + res = turn_info::replay_to_process_data_result(do_replay()); + } + else if(network_reader_.read(cfg)) { + res = turn_data_.process_network_data(cfg); + } + + if (res == turn_info::PROCESS_RESTART_TURN) { + player_type_changed_ = true; + } + else if (res == turn_info::PROCESS_END_TURN) { + end_turn_ = true; + } + else if (res == turn_info::PROCESS_END_LEVEL) { + } + else if (res == turn_info::PROCESS_END_LINGER) { + replay::process_error("Received unexpected next_scenario during the game"); + } +} \ No newline at end of file diff --git a/src/playmp_controller.hpp b/src/playmp_controller.hpp index d4f6d13225c5..551a9e7e30d0 100644 --- a/src/playmp_controller.hpp +++ b/src/playmp_controller.hpp @@ -46,13 +46,14 @@ class playmp_controller : public playsingle_controller, public syncmp_handler void start_network(); void stop_network(); - virtual possible_end_play_signal play_side(); - virtual possible_end_play_signal play_human_turn(); + virtual void play_side(); + virtual void play_human_turn(); + virtual void play_linger_turn(); virtual void after_human_turn(); virtual void finish_side_turn(); - virtual possible_end_play_signal play_network_turn(); + virtual void play_network_turn(); virtual void do_idle_notification(); - virtual possible_end_play_signal play_idle_loop(); + virtual void play_idle_loop(); void linger(); /** Wait for the host to upload the next scenario. */ @@ -67,6 +68,7 @@ class playmp_controller : public playsingle_controller, public syncmp_handler private: void set_end_scenario_button(); void reset_end_scenario_button(); + void process_network_data(); static unsigned int replay_last_turn_; }; diff --git a/src/playsingle_controller.cpp b/src/playsingle_controller.cpp index a012430adeda..3d11762d8674 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -205,12 +205,8 @@ void playsingle_controller::play_scenario_init() { saved_game_.replay_start() = to_config(); } - try { - fire_preload(); - } catch (end_level_exception) { - return; - } - + fire_preload(); + assert(recorder.at_end()); if(!loading_game_ ) @@ -221,20 +217,17 @@ void playsingle_controller::play_scenario_init() { //we can only use a set_scontext_synced with a non empty recorder. set_scontext_synced sync; - try { - fire_prestart(); - } catch (end_level_exception) { + fire_prestart(); + if (is_regular_game_end()) { return; } - init_gui(); LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n"; events::raise_draw_event(); - try { - fire_start(); - } catch (end_level_exception) { + fire_start(); + if (is_regular_game_end()) { return; } sync.do_final_checkup(); @@ -279,9 +272,8 @@ void playsingle_controller::play_scenario_main_loop() { ERR_NG << "Playing game with 0 teams." << std::endl; } for(; ; first_player_ = 1) { - possible_end_play_signal signal = play_turn(); - - if (signal) { + play_turn(); + if (is_regular_game_end()) { return; } @@ -427,7 +419,7 @@ LEVEL_RESULT playsingle_controller::play_scenario( return QUIT; } -possible_end_play_signal playsingle_controller::play_turn() +void playsingle_controller::play_turn() { whiteboard_manager_->on_gamestate_change(); gui_->new_turn(); @@ -445,7 +437,6 @@ possible_end_play_signal playsingle_controller::play_turn() // If a side is empty skip over it. if (current_team().is_empty()) continue; - possible_end_play_signal signal; { save_blocker blocker; init_side_begin(false); @@ -456,9 +447,14 @@ possible_end_play_signal playsingle_controller::play_turn() } ai_testing::log_turn_start(player_number_); - PROPOGATE_END_PLAY_SIGNAL ( play_side() ); - HANDLE_END_PLAY_SIGNAL(finish_side_turn()); - + play_side(); + if(is_regular_game_end()) { + return; + } + finish_side_turn(); + if(is_regular_game_end()) { + return; + } if(non_interactive()) { LOG_AIT << " Player " << player_number_ << ": " << current_team().villages().size() << " Villages" << @@ -475,30 +471,28 @@ possible_end_play_signal playsingle_controller::play_turn() finish_turn(); // Time has run out - PROPOGATE_END_PLAY_SIGNAL ( check_time_over() ); - return boost::none; + check_time_over(); } -possible_end_play_signal playsingle_controller::play_idle_loop() +void playsingle_controller::play_idle_loop() { - while(!end_turn_ && !player_type_changed_) { - HANDLE_END_PLAY_SIGNAL( play_slice() ); + while(!should_return_to_play_side()) { + play_slice_catch(); gui_->draw(); SDL_Delay(10); } - return boost::none; } -possible_end_play_signal playsingle_controller::play_side() +void playsingle_controller::play_side() { //check for team-specific items in the scenario gui_->parse_team_overlays(); - try { - maybe_do_init_side(); - } catch (end_level_exception & e) { - return possible_end_play_signal(e.to_struct()); + maybe_do_init_side(); + if(is_regular_game_end()) { + return; } + //flag used when we fallback from ai and give temporarily control to human bool temporary_human = false; do { @@ -525,8 +519,9 @@ possible_end_play_signal playsingle_controller::play_side() { before_human_turn(); if (!end_turn_) { - if(possible_end_play_signal signal = play_idle_loop()) { - return signal; + play_human_turn(); + if(is_regular_game_end()) { + return; } } } @@ -542,8 +537,9 @@ possible_end_play_signal playsingle_controller::play_side() // Give control to a human for this turn. player_type_changed_ = true; temporary_human = true; - } catch (end_level_exception &e) { - return possible_end_play_signal(e.to_struct()); + } + if(is_regular_game_end()) { + return; } if(!player_type_changed_) { @@ -551,14 +547,18 @@ possible_end_play_signal playsingle_controller::play_side() } } else if(current_team().is_network()) { - PROPOGATE_END_PLAY_SIGNAL( play_network_turn() ); + play_network_turn(); + if(is_regular_game_end()) { + return; + } } else if(current_team().is_local_human() && current_team().is_idle()) { end_turn_enable(false); do_idle_notification(); before_human_turn(); if (!end_turn_) { - if(possible_end_play_signal signal = play_idle_loop()) { - return signal; + play_idle_loop(); + if(is_regular_game_end()) { + return; } } } @@ -570,7 +570,6 @@ possible_end_play_signal playsingle_controller::play_side() // Keep looping if the type of a team (human/ai/networked) // has changed mid-turn skip_next_turn_ = false; - return boost::none; } void playsingle_controller::before_human_turn() @@ -607,24 +606,34 @@ void playsingle_controller::show_turn_dialog(){ } } -void playsingle_controller::execute_gotos(){ - menu_handler_.execute_gotos(mouse_handler_, player_number_); +void playsingle_controller::execute_gotos() +{ + if(should_return_to_play_side()) + { + return; + } + try + { + menu_handler_.execute_gotos(mouse_handler_, player_number_); + } + catch (const return_to_play_side_exception&) + { + } } -possible_end_play_signal playsingle_controller::play_human_turn() { +void playsingle_controller::play_human_turn() { show_turn_dialog(); if (!preferences::disable_auto_moves()) { - HANDLE_END_PLAY_SIGNAL(execute_gotos()); + execute_gotos(); } end_turn_enable(true); - while(!end_turn_ && !player_type_changed_) { - HANDLE_END_PLAY_SIGNAL( play_slice() ); + while(!should_return_to_play_side()) { + play_slice_catch(); gui_->draw(); } - return boost::none; } void playsingle_controller::linger() @@ -707,13 +716,17 @@ void playsingle_controller::play_ai_turn() if ( !cur_team.auto_shroud_updates() ) { // We just took control, so the undo stack is empty. We still need // to record this change for the replay though. - synced_context::run_in_synced_context("auto_shroud", replay_helper::get_auto_shroud(true)); + synced_context::run_and_store("auto_shroud", replay_helper::get_auto_shroud(true)); } undo_stack_->clear(); turn_data_.send_data(); try { - ai::manager::play_turn(player_number_); + try { + ai::manager::play_turn(player_number_); + } + catch (return_to_play_side_exception&) { + } } catch(...) { turn_data_.sync_network(); @@ -742,11 +755,10 @@ void playsingle_controller::do_idle_notification() /** * Will handle networked turns in descendent classes. */ -possible_end_play_signal playsingle_controller::play_network_turn() +void playsingle_controller::play_network_turn() { // There should be no networked sides in single-player. ERR_NG << "Networked team encountered by playsingle_controller." << std::endl; - return boost::none; } @@ -756,7 +768,7 @@ void playsingle_controller::handle_generic_event(const std::string& name){ } } -possible_end_play_signal playsingle_controller::check_time_over(){ +void playsingle_controller::check_time_over(){ bool time_left = gamestate_.tod_manager_.next_turn(gamestate_.gamedata_); it_is_a_new_turn_ = true; if(!time_left) { @@ -766,7 +778,7 @@ possible_end_play_signal playsingle_controller::check_time_over(){ LOG_NG << "done firing time over event...\n"; //if turns are added while handling 'time over' event if (gamestate_.tod_manager_.is_time_left()) { - return boost::none; + return; } if(non_interactive()) { @@ -774,14 +786,15 @@ possible_end_play_signal playsingle_controller::check_time_over(){ ai_testing::log_draw(); } - HANDLE_END_PLAY_SIGNAL( check_victory() ); + check_victory(); + if (is_regular_game_end()) { + return; + } end_level_data e; e.proceed_to_next_level = false; e.is_victory = false; set_end_level_data(e); - return possible_end_play_signal (end_level_struct()); } - return boost::none; } void playsingle_controller::end_turn(){ @@ -797,18 +810,16 @@ void playsingle_controller::force_end_turn(){ end_turn_ = true; } -void playsingle_controller::check_end_level() +void playsingle_controller::check_objectives() { - if (!is_regular_game_end() || linger_) - { - const team &t = gamestate_.board_.teams()[gui_->viewing_team()]; - if (!is_browsing() && t.objectives_changed()) { - dialogs::show_objectives(get_scenario_name().str(), t.objectives()); - t.reset_objectives_changed(); - } + if (!is_regular_game_end() || linger_) { return; } - throw end_level_exception(); + const team &t = gamestate_.board_.teams()[gui_->viewing_team()]; + if (!is_browsing() && t.objectives_changed()) { + dialogs::show_objectives(get_scenario_name().str(), t.objectives()); + t.reset_objectives_changed(); + } } diff --git a/src/playsingle_controller.hpp b/src/playsingle_controller.hpp index 15dc17477901..c8788982eb88 100644 --- a/src/playsingle_controller.hpp +++ b/src/playsingle_controller.hpp @@ -38,7 +38,7 @@ class playsingle_controller : public play_controller virtual void handle_generic_event(const std::string& name); - virtual void check_end_level(); + virtual void check_objectives(); void report_victory(std::ostringstream &report, team& t, int finishing_bonus_per_turn, int turns_left, int finishing_bonus); virtual void on_not_observer() {} @@ -54,21 +54,23 @@ class playsingle_controller : public play_controller bool get_player_type_changed() const { return player_type_changed_; } void set_player_type_changed() { player_type_changed_ = true; } + virtual bool should_return_to_play_side() + { return player_type_changed_ || end_turn_ || is_regular_game_end(); } protected: - possible_end_play_signal play_turn(); - virtual possible_end_play_signal play_side(); + void play_turn(); + virtual void play_side(); void before_human_turn(); void show_turn_dialog(); void execute_gotos(); - virtual possible_end_play_signal play_human_turn(); + virtual void play_human_turn(); virtual void after_human_turn(); void end_turn_enable(bool enable); void play_ai_turn(); - virtual possible_end_play_signal play_idle_loop(); + virtual void play_idle_loop(); virtual void do_idle_notification(); - virtual possible_end_play_signal play_network_turn(); + virtual void play_network_turn(); virtual void init_gui(); - possible_end_play_signal check_time_over(); + void check_time_over(); void store_recalls(); void store_gold(bool obs = false); diff --git a/src/playturn.cpp b/src/playturn.cpp index fe517d53ed52..0cd0d0e05482 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -413,6 +413,8 @@ turn_info::PROCESS_DATA_RESULT turn_info::replay_to_process_data_result(REPLAY_R return PROCESS_FOUND_DEPENDENT; case REPLAY_FOUND_END_TURN: return PROCESS_END_TURN; + case REPLAY_FOUND_END_LEVEL: + return PROCESS_END_LEVEL; default: assert(false); throw "found invalid REPLAY_RETURN"; diff --git a/src/playturn.hpp b/src/playturn.hpp index 0480e91a4e26..255a54f20792 100644 --- a/src/playturn.hpp +++ b/src/playturn.hpp @@ -42,7 +42,9 @@ class turn_info /** When the host uploaded the next scenario this is returned. */ PROCESS_END_LINGER, /** When we couldn't process the network data because we found a dependent command, this should only happen if we were called playmp_controller::from handle_generic_event -> sync_network*/ - PROCESS_FOUND_DEPENDENT + PROCESS_FOUND_DEPENDENT, + /** We foudn a player action in the replay that caused the game to end*/ + PROCESS_END_LEVEL }; PROCESS_DATA_RESULT sync_network(); @@ -60,10 +62,10 @@ class turn_info 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); static void change_side_controller(int side, const std::string& player); - static PROCESS_DATA_RESULT replay_to_process_data_result(REPLAY_RETURN replayreturn); PROCESS_DATA_RESULT handle_turn(const config& t); void do_save(); diff --git a/src/replay.cpp b/src/replay.cpp index 00605e16619a..727719032070 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -210,7 +210,7 @@ void replay::process_error(const std::string& msg) { ERR_REPLAY << msg << std::flush; - resources::controller->process_oos(msg); // might throw end_level_exception(QUIT) + resources::controller->process_oos(msg); // might throw quit_game_exception() } void replay::add_unit_checksum(const map_location& loc,config& cfg) @@ -853,7 +853,10 @@ REPLAY_RETURN do_replay_handle(bool one_move) /* we need to use the undo stack during replays in order to make delayed shroud updated work. */ - synced_context::run_in_synced_context(commandname, data, true, !resources::controller->is_skipping_replay(), false,show_oos_error_error_function); + synced_context::run(commandname, data, true, !resources::controller->is_skipping_replay(), show_oos_error_error_function); + if(resources::controller->is_regular_game_end()) { + return REPLAY_FOUND_END_LEVEL; + } if (one_move) { return REPLAY_FOUND_END_MOVE; } diff --git a/src/replay.hpp b/src/replay.hpp index 9f0d0f3d5b00..b527b29b91df 100644 --- a/src/replay.hpp +++ b/src/replay.hpp @@ -160,7 +160,8 @@ enum REPLAY_RETURN REPLAY_RETURN_AT_END, REPLAY_FOUND_DEPENDENT, REPLAY_FOUND_END_TURN, - REPLAY_FOUND_END_MOVE + REPLAY_FOUND_END_MOVE, + REPLAY_FOUND_END_LEVEL }; //replays up to one turn from the recorder object //returns true if it got to the end of the turn without data running out diff --git a/src/replay_controller.cpp b/src/replay_controller.cpp index 9e86f2000f9b..81e4be04fae4 100644 --- a/src/replay_controller.cpp +++ b/src/replay_controller.cpp @@ -52,7 +52,7 @@ static lg::log_domain log_replay("replay"); #define LOG_REPLAY LOG_STREAM(info, log_replay) #define ERR_REPLAY LOG_STREAM(err, log_replay) -possible_end_play_signal play_replay_level_main_loop(replay_controller & replaycontroller, bool & is_unit_test); +void play_replay_level_main_loop(replay_controller & replaycontroller, bool & is_unit_test); LEVEL_RESULT play_replay_level(const config& game_config, const tdata_cache & tdata, CVideo& video, saved_game& state_of_game, bool is_unit_test) @@ -65,42 +65,43 @@ LEVEL_RESULT play_replay_level(const config& game_config, const tdata_cache & td const events::command_disabler disable_commands; - try { - rc.reset(new replay_controller(state_of_game.get_replay_starting_pos(), state_of_game, ticks, game_config, tdata, video)); - } catch (end_level_exception&){ - //TODO: When can this happen? - return QUIT; - } + rc.reset(new replay_controller(state_of_game.get_replay_starting_pos(), state_of_game, ticks, game_config, tdata, video)); DBG_NG << "created objects... " << (SDL_GetTicks() - rc->get_ticks()) << std::endl; //replay event-loop - possible_end_play_signal signal = play_replay_level_main_loop(*rc, is_unit_test); - - if (signal) { - DBG_NG << "play_replay_level: end_level_exception" << std::endl; + play_replay_level_main_loop(*rc, is_unit_test); + if(rc->is_regular_game_end()) + { + // return rc->get_end_level_data_const().is_victory ? VICTORY : DEFEAT; + //The replay contained the whole scenario, returns VICTORY regardless of the original outcome. + return VICTORY; + } + else + { + //The replay was finished without reaching the scenario end. + return QUIT; } - - return VICTORY; } -possible_end_play_signal play_replay_level_main_loop(replay_controller & replaycontroller, bool & is_unit_test) { +void play_replay_level_main_loop(replay_controller & replaycontroller, bool & is_unit_test) { if (is_unit_test) { return replaycontroller.try_run_to_completion(); } - for (;;){ - HANDLE_END_PLAY_SIGNAL( replaycontroller.play_slice() ); + for (;;) { + //Quits by quit_level_exception + replaycontroller.play_slice(); } } -possible_end_play_signal replay_controller::try_run_to_completion() { +void replay_controller::try_run_to_completion() { for (;;) { - HANDLE_END_PLAY_SIGNAL( play_slice() ); + play_slice(); if (recorder.at_end()) { - return boost::none; + return; } else { if (!is_playing_) { - PROPOGATE_END_PLAY_SIGNAL( play_replay() ); + play_replay(); } } } @@ -380,31 +381,33 @@ void replay_controller::reset_replay() reset_replay_ui(); } -void replay_controller::stop_replay(){ +void replay_controller::stop_replay() +{ is_playing_ = false; } -possible_end_play_signal replay_controller::replay_next_turn(){ +void replay_controller::replay_next_turn() +{ is_playing_ = true; replay_ui_playback_should_start(); - PROPOGATE_END_PLAY_SIGNAL( play_turn() ); + play_turn(); if (!is_skipping_replay() || !is_playing_){ gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } replay_ui_playback_should_stop(); - return boost::none; } -possible_end_play_signal replay_controller::replay_next_move_or_side(bool one_move){ +void replay_controller::replay_next_move_or_side(bool one_move) +{ is_playing_ = true; replay_ui_playback_should_start(); - HANDLE_END_PLAY_SIGNAL( play_move_or_side(one_move) ); + play_move_or_side(one_move); while (current_team().is_empty()) { - HANDLE_END_PLAY_SIGNAL( play_move_or_side(one_move) ); + play_move_or_side(one_move); } if ( (!is_skipping_replay() || !is_playing_) && (last_replay_action == REPLAY_FOUND_END_TURN) ){ @@ -412,14 +415,15 @@ possible_end_play_signal replay_controller::replay_next_move_or_side(bool one_mo } replay_ui_playback_should_stop(); - return boost::none; } -possible_end_play_signal replay_controller::replay_next_side(){ +void replay_controller::replay_next_side() +{ return replay_next_move_or_side(false); } -possible_end_play_signal replay_controller::replay_next_move(){ +void replay_controller::replay_next_move() +{ return replay_next_move_or_side(true); } @@ -469,40 +473,36 @@ void replay_controller::replay_skip_animation(){ } //move all sides till stop/end -possible_end_play_signal replay_controller::play_replay(){ +void replay_controller::play_replay() +{ - if (recorder.at_end()){ - //shouldn't actually happen - return boost::none; + if (recorder.at_end()) + { } is_playing_ = true; replay_ui_playback_should_start(); - possible_end_play_signal signal = play_replay_main_loop(); - - if(signal) { - } + play_replay_main_loop(); if (!is_playing_) { gui_->scroll_to_leader(player_number_,game_display::ONSCREEN,false); } replay_ui_playback_should_stop(); - - return boost::none; } -possible_end_play_signal replay_controller::play_replay_main_loop() { +void replay_controller::play_replay_main_loop() +{ DBG_REPLAY << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n"; for(; !recorder.at_end() && is_playing_; first_player_ = 1) { - PROPOGATE_END_PLAY_SIGNAL ( play_turn() ); + play_turn(); } - return boost::none; } //make all sides move, then stop -possible_end_play_signal replay_controller::play_turn(){ +void replay_controller::play_turn() +{ LOG_REPLAY << "turn: " << current_turn_ << "\n"; @@ -514,23 +514,22 @@ possible_end_play_signal replay_controller::play_turn(){ while ( (!last_team) && (!recorder.at_end()) && is_playing_ ){ last_team = static_cast(player_number_) == gamestate_.board_.teams().size(); - PROPOGATE_END_PLAY_SIGNAL( play_side() ); - HANDLE_END_PLAY_SIGNAL( play_slice() ); + play_side(); + play_slice(); } - return boost::none; } -possible_end_play_signal replay_controller::play_side() { +void replay_controller::play_side() { return play_move_or_side(false); } -possible_end_play_signal replay_controller::play_move() { +void replay_controller::play_move() { return play_move_or_side(true); } //make only one side move -possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { +void replay_controller::play_move_or_side(bool one_move) { DBG_REPLAY << "Status turn number: " << turn() << "\n"; DBG_REPLAY << "Replay_Controller turn number: " << current_turn_ << "\n"; @@ -540,7 +539,6 @@ possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { if (!current_team().is_empty()) { statistics::reset_turn_stats(current_team().save_id()); - possible_end_play_signal signal = NULL; if (last_replay_action == REPLAY_FOUND_END_TURN) { play_controller::init_side_begin(true); } @@ -549,18 +547,10 @@ possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { // if have reached the end we don't want to execute finish_side_turn and finish_turn // because we might not have enough data to execute them (like advancements during turn_end for example) - try { - last_replay_action = do_replay(one_move); - if(last_replay_action != REPLAY_FOUND_END_TURN) { - //We reached the end of the replay without finding an end turn tag. - //REPLAY_FOUND_DEPENDENT here might indicate an OOS error - return boost::none; - } - } catch(end_level_exception& e){ - //dont return to title screen if the game ended the normal way - if (!is_regular_game_end()) { - return possible_end_play_signal(e.to_struct()); - } + last_replay_action = do_replay(one_move); + if(last_replay_action != REPLAY_FOUND_END_TURN) { + //We reached the end of the replay without finding an end turn tag. + return; } finish_side_turn(); @@ -589,11 +579,10 @@ possible_end_play_signal replay_controller::play_move_or_side(bool one_move) { update_teams(); update_gui(); - - return boost::none; } -void replay_controller::update_teams(){ +void replay_controller::update_teams() +{ int next_team = player_number_; if(static_cast(next_team) > gamestate_.board_.teams().size()) { @@ -610,7 +599,8 @@ void replay_controller::update_teams(){ gui_->invalidate_all(); } -void replay_controller::update_gui(){ +void replay_controller::update_gui() +{ (*gui_).recalculate_minimap(); (*gui_).redraw_minimap(); (*gui_).invalidate_all(); @@ -618,7 +608,8 @@ void replay_controller::update_gui(){ (*gui_).draw(); } -void replay_controller::handle_generic_event(const std::string& name){ +void replay_controller::handle_generic_event(const std::string& name) +{ if( name == "completely_redrawn" ) { update_replay_ui(); diff --git a/src/replay_controller.hpp b/src/replay_controller.hpp index a0d3b50a0b03..63dff2946fa8 100644 --- a/src/replay_controller.hpp +++ b/src/replay_controller.hpp @@ -32,13 +32,13 @@ class replay_controller : public play_controller const int ticks, const config& game_config, const tdata_cache & tdata, CVideo& video); virtual ~replay_controller(); - possible_end_play_signal play_replay(); + void play_replay(); void reset_replay(); void stop_replay(); - possible_end_play_signal replay_next_move_or_side(bool one_move); - possible_end_play_signal replay_next_turn(); - possible_end_play_signal replay_next_side(); - possible_end_play_signal replay_next_move(); + void replay_next_move_or_side(bool one_move); + void replay_next_turn(); + void replay_next_side(); + void replay_next_move(); void process_oos(const std::string& msg) const; void replay_show_everything(); void replay_show_each(); @@ -46,10 +46,10 @@ class replay_controller : public play_controller void replay_skip_animation(); virtual void force_end_turn() {} - virtual void check_end_level() {} + virtual void check_objectives() {} virtual void on_not_observer() {} - possible_end_play_signal try_run_to_completion(); + void try_run_to_completion(); bool recorder_at_end(); @@ -61,17 +61,17 @@ class replay_controller : public play_controller private: void init(); - possible_end_play_signal play_turn(); - possible_end_play_signal play_move_or_side(bool one_move = false); - possible_end_play_signal play_side(); - possible_end_play_signal play_move(); + void play_turn(); + void play_move_or_side(bool one_move = false); + void play_side(); + void play_move(); void update_teams(); void update_gui(); void init_replay_display(); void rebuild_replay_theme(); void handle_generic_event(const std::string& /*name*/); - possible_end_play_signal play_replay_main_loop(); + void play_replay_main_loop(); void reset_replay_ui(); void update_replay_ui(); diff --git a/src/synced_context.cpp b/src/synced_context.cpp index a63fcb754ab7..2a3ad9527cdc 100644 --- a/src/synced_context.cpp +++ b/src/synced_context.cpp @@ -54,23 +54,11 @@ synced_context::synced_state synced_context::state_ = synced_context::UNSYNCED; int synced_context::last_unit_id_ = 0; bool synced_context::is_simultaneously_ = false; -bool synced_context::run_in_synced_context(const std::string& commandname, const config& data, bool use_undo, bool show, bool store_in_replay, synced_command::error_handler_function error_handler) +bool synced_context::run(const std::string& commandname, const config& data, bool use_undo, bool show, synced_command::error_handler_function error_handler) { DBG_REPLAY << "run_in_synced_context:" << commandname << "\n"; - if(!recorder.at_end() && store_in_replay) - { - //Most likeley we are in a replay now. - //We don't want to execute [do_command] & similar now - WRN_REPLAY << "invalid run_in_synced_context call ignored" << std::endl; - return false; - } - assert(use_undo || (!resources::undo_stack->can_redo() && !resources::undo_stack->can_undo())); - if(store_in_replay) - { - recorder.add_synced_command(commandname, data); - } /* use this after recorder.add_synced_command because set_scontext_synced sets the checkup to the last added command @@ -84,10 +72,8 @@ bool synced_context::run_in_synced_context(const std::string& commandname, const else { bool success = it->second(data, use_undo, show, error_handler); - if(!success && store_in_replay) + if(!success) { - //remove it from replay if we weren't sucessful. - recorder.undo(); return false; } } @@ -100,6 +86,27 @@ bool synced_context::run_in_synced_context(const std::string& commandname, const return true; } +bool synced_context::run_and_store(const std::string& commandname, const config& data, bool use_undo, bool show, synced_command::error_handler_function error_handler) +{ + assert(recorder.at_end()); + recorder.add_synced_command(commandname, data); + bool success = run(commandname, data, use_undo, show, error_handler); + if(!success) + { + recorder.undo(); + } + return success; +} + +bool synced_context::run_and_throw(const std::string& commandname, const config& data, bool use_undo, bool show, synced_command::error_handler_function error_handler) +{ + bool success = run_and_store(commandname, data, use_undo, show, error_handler); + if(success) + { + resources::controller->maybe_throw_return_to_play_side(); + } + return success; +} bool synced_context::run_in_synced_context_if_not_already(const std::string& commandname,const config& data, bool use_undo, bool show, synced_command::error_handler_function error_handler) { @@ -112,7 +119,7 @@ bool synced_context::run_in_synced_context_if_not_already(const std::string& com ERR_REPLAY << "ignored attempt to invoke a synced command during replay\n"; return false; } - return run_in_synced_context(commandname, data, use_undo, show, true, error_handler); + return run_and_throw(commandname, data, use_undo, show, error_handler); } case(synced_context::LOCAL_CHOICE): ERR_REPLAY << "trying to execute action while being in a local_choice" << std::endl; @@ -125,7 +132,7 @@ bool synced_context::run_in_synced_context_if_not_already(const std::string& com synced_command::map::iterator it = synced_command::registry().find(commandname); if(it == synced_command::registry().end()) { - error_handler("commandname [" +commandname +"]not found", true); + error_handler("commandname [" +commandname +"] not found", true); return false; } else diff --git a/src/synced_context.hpp b/src/synced_context.hpp index d825bc4c557c..fa277a391c62 100644 --- a/src/synced_context.hpp +++ b/src/synced_context.hpp @@ -48,7 +48,6 @@ class synced_context also there are no events of similar fired when redoing an action (in most cases). @param use_undo this parameter is used to ignore undos during an ai move to optimize. - @param store_in_replay only true if called by do_replay_handle @param error_handler an error handler for the case that data contains invalid data. @return true if the action was successful. @@ -56,7 +55,9 @@ class synced_context */ - static bool run_in_synced_context(const std::string& commandname,const config& data, bool use_undo = true, bool show = true , bool store_in_replay = true , synced_command::error_handler_function error_handler = default_error_function); + static bool run(const std::string& commandname, const config& data, bool use_undo = true, bool show = true, synced_command::error_handler_function error_handler = default_error_function); + static bool run_and_store(const std::string& commandname, const config& data, bool use_undo = true, bool show = true, synced_command::error_handler_function error_handler = default_error_function); + static bool run_and_throw(const std::string& commandname, const config& data, bool use_undo = true, bool show = true, synced_command::error_handler_function error_handler = default_error_function); /** checks whether we are currently running in a synced context, and if not we enters it. this is never called from so_replay_handle. diff --git a/src/wesnoth.cpp b/src/wesnoth.cpp index ccc9e0b6992d..243b38b318b1 100644 --- a/src/wesnoth.cpp +++ b/src/wesnoth.cpp @@ -985,8 +985,10 @@ int main(int argc, char** argv) return 1; } catch(CVideo::quit&) { //just means the game should quit - } catch(end_level_exception&) { - std::cerr << "caught end_level_exception (quitting)\n"; + } catch(return_to_play_side_exception&) { + std::cerr << "caught return_to_play_side_exception, please report this bug (quitting)\n"; + } catch(quit_game_exception&) { + std::cerr << "caught quit_game_exception (quitting)\n"; } catch(twml_exception& e) { std::cerr << "WML exception:\nUser message: " << e.user_message << "\nDev message: " << e.dev_message << '\n'; diff --git a/src/whiteboard/move.cpp b/src/whiteboard/move.cpp index cf5ad6e42171..2ee52b6e6297 100644 --- a/src/whiteboard/move.cpp +++ b/src/whiteboard/move.cpp @@ -225,8 +225,7 @@ void move::execute(bool& success, bool& complete) try { events::mouse_handler& mouse_handler = resources::controller->get_mouse_handler_base(); num_steps = mouse_handler.move_unit_along_route(steps, interrupted); - } catch (restart_turn_exception&) { - //This exception is thown by the ai code, i dont know whther the whiteboard can interact with the ai so i leave this catch here for now + } catch (return_to_play_side_exception&) { set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); throw; // we rely on the caller to delete this action } diff --git a/src/whiteboard/recall.cpp b/src/whiteboard/recall.cpp index abfc13806246..2e3c7fec800a 100644 --- a/src/whiteboard/recall.cpp +++ b/src/whiteboard/recall.cpp @@ -126,11 +126,10 @@ void recall::execute(bool& success, bool& complete) cost=temp_unit_->recall_cost(); } current_team.get_side_actions()->change_gold_spent_by(-cost); - bool const result = synced_context::run_in_synced_context("recall", + bool const result = synced_context::run_and_throw("recall", replay_helper::get_recall(temp_unit_->id(), recall_hex_, map_location::null_location()), true, true, - true, synced_context::ignore_error_function); if (!result) { diff --git a/src/whiteboard/side_actions.cpp b/src/whiteboard/side_actions.cpp index 7a4c87fa50af..f753f0d8e965 100644 --- a/src/whiteboard/side_actions.cpp +++ b/src/whiteboard/side_actions.cpp @@ -336,8 +336,7 @@ bool side_actions::execute(side_actions::iterator position) bool action_complete; try { action->execute(action_successful, action_complete); - } catch (restart_turn_exception&) { - //This exception is thown by the ai code, i dont know whther the whiteboard can interact with the ai so i leave this catch here for now + } catch (return_to_play_side_exception&) { synced_erase(position); LOG_WB << "End turn exception caught during execution, deleting action. " << *this << "\n"; //validate actions at next map rebuild