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.
This commit is contained in:
gfgtdf 2015-03-03 18:49:26 +01:00
parent 62cea304b3
commit fb68590637
30 changed files with 370 additions and 485 deletions

View file

@ -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);

View file

@ -1270,6 +1270,7 @@ size_t move_unit_and_record(const std::vector<map_location> &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;
}

View file

@ -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<playsingle_controller*>(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

View file

@ -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: "<<num_interact_<<std::endl;
DBG_AI_MANAGER << "side " << side << ": total turn time: "<<turn_end_time - turn_start_time << " ms "<< std::endl;

View file

@ -162,7 +162,7 @@ gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_se
assert(dismissed_unit);
// Record the dismissal, then delete the unit.
synced_context::run_in_synced_context("disband", replay_helper::get_disband(dismissed_unit->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);

View file

@ -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 @@ public:
private:
IMPLEMENT_LUA_JAILBREAK_EXCEPTION(quit_game_exception)
};
/**
* The two end_*_exceptions are caught and transformed to this signaling object
*/
typedef boost::optional<end_level_struct> 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

View file

@ -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
{

View file

@ -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) {

View file

@ -79,12 +79,12 @@ public:
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() {}

View file

@ -42,13 +42,13 @@ public:
{ 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(); }

View file

@ -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();
}
}

View file

@ -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()));
}

View file

@ -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<unsigned> 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());
}
}

View file

@ -111,12 +111,12 @@ 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 @@ 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 @@ 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);

View file

@ -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()
{
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<countdown_clock> 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)
process_network_data();
if (player_type_changed_)
{
// Clean undo stack if turn has to be restarted (losing control)
if ( undo_stack_->can_undo() )
{
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."));
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);
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)
{
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::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");
}
}

View file

@ -46,13 +46,14 @@ protected:
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 @@ protected:
private:
void set_end_scenario_button();
void reset_end_scenario_button();
void process_network_data();
static unsigned int replay_last_turn_;
};

View file

@ -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();
}
}

View file

@ -38,7 +38,7 @@ public:
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 @@ public:
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);

View file

@ -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";

View file

@ -42,7 +42,9 @@ public:
/** 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 @@ public:
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();

View file

@ -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;
}

View file

@ -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

View file

@ -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<size_t>(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<size_t>(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();

View file

@ -32,13 +32,13 @@ public:
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 @@ public:
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 @@ protected:
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();

View file

@ -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

View file

@ -48,7 +48,6 @@ public:
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 @@ public:
*/
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.

View file

@ -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';

View file

@ -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
}

View file

@ -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) {

View file

@ -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