void playsingle_controller::play_scenario_main_loop()
{
	LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks()) << "\n";


	// Avoid autosaving after loading, but still
	// allow the first turn to have an autosave.
	ai_testing::log_game_start();
	if(gamestate().board_.teams().empty())
	{
		ERR_NG << "Playing game with 0 teams." << std::endl;
	}
	while(true) {
		try {
			play_turn();
			if (is_regular_game_end()) {
				turn_data_.send_data();
				return;
			}
			gamestate_->player_number_ = 1;
		}
		catch(const reset_gamestate_exception& ex) {
			//
			// TODO:
			//
			// The MP replay feature still doesn't work properly (causes OOS)
			// because:
			//
			// 1) The undo stack is not reset along with the gamestate (fixed).
			// 2) The server_request_number_ is not reset along with the
			//    gamestate (fixed).
			// 3) chat and other unsynced actions are inserted in the middle of
			//    the replay bringing the replay_pos in unorder (fixed).
			// 4) untracked changes in side controllers are lost when resetting
			//    gamestate (fixed).
			// 5) The game should have a stricter check for whether the loaded
			//    game is actually a parent of this game.
			// 6) If an action was undone after a game was saved it can cause
			//    OOS if the undone action is in the snapshot of the saved
			//    game (luckily this is never the case for autosaves).
			//
			boost::dynamic_bitset<> local_players;
			local_players.resize(gamestate().board_.teams().size(), true);
			//Preserve side controllers, becasue we won't get the side controoller updates again when replaying.
			for(size_t i = 0; i < local_players.size(); ++i) {
				local_players[i] = gamestate().board_.teams()[i].is_local();
			}
			reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
			for(size_t i = 0; i < local_players.size(); ++i) {
				resources::gameboard->teams()[i].set_local(local_players[i]);
			}
			play_scenario_init();
			replay_.reset(new replay_controller(*this, false, ex.level));
			if(ex.start_replay) {
				replay_->play_replay();
			}
		}
	} //end for loop
}
void playsingle_controller::play_scenario_main_loop()
{
	LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks()) << "\n";


	// Avoid autosaving after loading, but still
	// allow the first turn to have an autosave.
	ai_testing::log_game_start();
	if(gamestate().board_.teams().empty())
	{
		ERR_NG << "Playing game with 0 teams." << std::endl;
	}
	while(true) {
		try {
			play_turn();
			if (is_regular_game_end()) {
				return;
			}
			gamestate_->player_number_ = 1;
		}
		catch(const reset_gamestate_exception& ex) {
			/**
				@TODO: The mp replay feature still doesnt work properly (casues OOS) becasue:
					1) The undo stack is not reset along with the gamestate (fixed).
					2) The server_request_number_ is not reset along with the gamestate (fixed).
					3) chat and other unsynced actions are inserted in the middle of the replay bringing the replay_pos in unorder (fixed).
					4) untracked changes in side controllers are lost when resetting gamestate.
					5) The game should have a stricter check for whether the loaded game is actually a parent of this game.
					6) If an action was undone after a game was saved it can casue if teh undone action is in the snapshot of the saved game. (luckyli this is never the case for autosaves)
			*/
			reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
			play_scenario_init(*ex.level);
			mp_replay_.reset(new replay_controller(*this, false, ex.level));
			mp_replay_->play_replay();
		}
	} //end for loop
}
LEVEL_RESULT playsingle_controller::play_scenario(const config& level)
{
	LOG_NG << "in playsingle_controller::play_scenario()...\n";

	// Start music.
	BOOST_FOREACH(const config &m, level.child_range("music")) {
		sound::play_music_config(m);
	}
	sound::commit_music_changes();

	if(!this->is_skipping_replay()) {
		show_story(gui_->video(), get_scenario_name(), level.child_range("story"));
	}
	gui_->labels().read(level);

	// Read sound sources
	assert(soundsources_manager_ != NULL);
	BOOST_FOREACH(const config &s, level.child_range("sound_source")) {
		try {
			soundsource::sourcespec spec(s);
			soundsources_manager_->add(spec);
		} catch (bad_lexical_cast &) {
			ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl;
			ERR_NG << "sound_source config was: " << s.debug() << std::endl;
			ERR_NG << "Skipping this sound source..." << std::endl;
		}
	}
	LOG_NG << "entering try... " << (SDL_GetTicks() - ticks()) << "\n";
	try {
		play_scenario_init();
		// clears level config;
		this->saved_game_.remove_snapshot();

		if (!is_regular_game_end() && !linger_) {
			play_scenario_main_loop();
		}
		if (game_config::exit_at_end) {
			exit(0);
		}
		const bool is_victory = get_end_level_data_const().is_victory;

		if(gamestate().gamedata_.phase() <= game_data::PRESTART) {
			sdl::draw_solid_tinted_rectangle(
				0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0,
				gui_->video().getSurface()
				);
			update_rect(0, 0, gui_->video().getx(), gui_->video().gety());
		}

		ai_testing::log_game_end();

		const end_level_data& end_level = get_end_level_data_const();
		if (!end_level.transient.custom_endlevel_music.empty()) {
			if (!is_victory) {
				set_defeat_music_list(end_level.transient.custom_endlevel_music);
			} else {
				set_victory_music_list(end_level.transient.custom_endlevel_music);
			}
		}

		if (gamestate().board_.teams().empty())
		{
			//store persistent teams
			saved_game_.set_snapshot(config());

			return LEVEL_RESULT::VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event
		}
		if(linger_) {
			LOG_NG << "resuming from loaded linger state...\n";
			//as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
			saved_game_.set_snapshot(config());
			if(!is_observer()) {
				persist_.end_transaction();
			}
			return LEVEL_RESULT::VICTORY;
		}
		pump().fire(is_victory ? "victory" : "defeat");
		{ // Block for set_scontext_synced_base
			set_scontext_synced_base sync;
			pump().fire("scenario end");
		}
		if(end_level.proceed_to_next_level) {
			gamestate().board_.heal_all_survivors();
		}
		if(is_observer()) {
			gui2::show_transient_message(gui_->video(), _("Game Over"), _("The game is over."));
			return LEVEL_RESULT::OBSERVER_END;
		}
		// If we're a player, and the result is victory/defeat, then send
		// a message to notify the server of the reason for the game ending.
		network::send_data(config_of
			("info", config_of
				("type", "termination")
				("condition", "game over")
				("result", is_victory ? "victory" : "defeat")
			));
		// Play victory music once all victory events
		// are finished, if we aren't observers.
		//
		// Some scenario authors may use 'continue'
		// result for something that is not story-wise
		// a victory, so let them use [music] tags
		// instead should they want special music.
		const std::string& end_music = is_victory ? select_victory_music() : select_defeat_music();
		if(end_music.empty() != true) {
			sound::play_music_once(end_music);
		}
		persist_.end_transaction();
		return is_victory ? LEVEL_RESULT::VICTORY : LEVEL_RESULT::DEFEAT;
	} catch(const game::load_game_exception &) {
		// Loading a new game is effectively a quit.
		//
		if ( game::load_game_exception::game != "" ) {
			saved_game_ = saved_game();
		}
		throw;
	} catch(network::error& e) {
		bool disconnect = false;
		if(e.socket) {
			e.disconnect();
			disconnect = true;
		}

		scoped_savegame_snapshot snapshot(*this);
		savegame::ingame_savegame save(saved_game_, *gui_, preferences::save_compression_format());
		save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
		if(disconnect) {
			throw network::error();
		} else {
			return LEVEL_RESULT::QUIT;
		}
	}

	return LEVEL_RESULT::QUIT;
}
LEVEL_RESULT playsingle_controller::play_scenario(
    const config::const_child_itors &story,
    bool skip_replay)
{
    LOG_NG << "in playsingle_controller::play_scenario()...\n";

    // Start music.
    BOOST_FOREACH(const config &m, level_.child_range("music")) {
        sound::play_music_config(m);
    }
    sound::commit_music_changes();

    if(!skip_replay) {
        show_story(*gui_, level_["name"], story);
    }
    gui_->labels().read(level_);

    // Read sound sources
    assert(soundsources_manager_ != NULL);
    BOOST_FOREACH(const config &s, level_.child_range("sound_source")) {
        try {
            soundsource::sourcespec spec(s);
            soundsources_manager_->add(spec);
        } catch (bad_lexical_cast &) {
            ERR_NG << "Error when parsing sound_source config: bad lexical cast." << std::endl;
            ERR_NG << "sound_source config was: " << s.debug() << std::endl;
            ERR_NG << "Skipping this sound source..." << std::endl;
        }
    }

    set_victory_when_enemies_defeated(level_["victory_when_enemies_defeated"].to_bool(true));
    set_remove_from_carryover_on_defeat(level_["remove_from_carryover_on_defeat"].to_bool(true));
    end_level_data &end_level = get_end_level_data();
    end_level.carryover_percentage = level_["carryover_percentage"].to_int(game_config::gold_carryover_percentage);
    end_level.carryover_add = level_["carryover_add"].to_bool();

    bool past_prestart = false;

    LOG_NG << "entering try... " << (SDL_GetTicks() - ticks_) << "\n";
    try {
        possible_end_play_signal signal = play_scenario_init(end_level, past_prestart);

        if (!signal) {

            signal = play_scenario_main_loop(end_level, past_prestart);
        }

        if (signal) {
            switch (boost::apply_visitor( get_signal_type(), *signal )) {
            //BEGIN CASES
            case END_TURN:
                assert(false && "end turn signal propogated to playsingle_controller::play_scenario. This results in terminate!");
                throw 42;
            case END_LEVEL:
                if(!past_prestart) {
                    sdl::draw_solid_tinted_rectangle(
                        0, 0, gui_->video().getx(), gui_->video().gety(), 0, 0, 0, 1.0,
                        gui_->video().getSurface()
                    );
                    update_rect(0, 0, gui_->video().getx(), gui_->video().gety());
                }

                ai_testing::log_game_end();
                LEVEL_RESULT end_level_result = boost::apply_visitor( get_result(), *signal );
                if (!end_level.transient.custom_endlevel_music.empty()) {
                    if (end_level_result == DEFEAT) {
                        set_defeat_music_list(end_level.transient.custom_endlevel_music);
                    } else {
                        set_victory_music_list(end_level.transient.custom_endlevel_music);
                    }
                }

                if (gamestate_.board_.teams().empty())
                {
                    //store persistent teams
                    saved_game_.set_snapshot(config());

                    return VICTORY; // this is probably only a story scenario, i.e. has its endlevel in the prestart event
                }
                const bool obs = is_observer();
                if (game_config::exit_at_end) {
                    exit(0);
                }
                if (end_level_result == DEFEAT || end_level_result == VICTORY)
                {
                    saved_game_.classification().completion = (end_level_result == VICTORY) ? "victory" : "defeat";
                    // If we're a player, and the result is victory/defeat, then send
                    // a message to notify the server of the reason for the game ending.
                    if (!obs) {
                        config cfg;
                        config& info = cfg.add_child("info");
                        info["type"] = "termination";
                        info["condition"] = "game over";
                        info["result"] = saved_game_.classification().completion;
                        network::send_data(cfg, 0);
                    } else {
                        gui2::show_transient_message(gui_->video(),_("Game Over"),
                                                     _("The game is over."));
                        return OBSERVER_END;
                    }
                }

                if (end_level_result == QUIT) {
                    return QUIT;
                }
                else if (end_level_result == DEFEAT)
                {
                    saved_game_.classification().completion = "defeat";
                    game_events::fire("defeat");

                    if (!obs) {
                        const std::string& defeat_music = select_defeat_music();
                        if(defeat_music.empty() != true)
                            sound::play_music_once(defeat_music);

                        persist_.end_transaction();
                        return DEFEAT;
                    } else {
                        return QUIT;
                    }
                }
                else if (end_level_result == VICTORY)
                {
                    saved_game_.classification().completion =
                        !end_level.transient.linger_mode ? "running" : "victory";
                    game_events::fire("victory");

                    //
                    // Play victory music once all victory events
                    // are finished, if we aren't observers.
                    //
                    // Some scenario authors may use 'continue'
                    // result for something that is not story-wise
                    // a victory, so let them use [music] tags
                    // instead should they want special music.
                    //
                    if (!obs && end_level.transient.linger_mode) {
                        const std::string& victory_music = select_victory_music();
                        if(victory_music.empty() != true)
                            sound::play_music_once(victory_music);
                    }

                    // Add all the units that survived the scenario.
                    // this function doesn't move unit to the recalllist anymore i just keep this name to prevent merging conflicts.
                    LOG_NG << "Add units that survived the scenario to the recall list.\n";
                    gamestate_.board_.all_survivors_to_recall();

                    saved_game_.remove_snapshot();
                    if(!is_observer()) {
                        persist_.end_transaction();
                    }

                    return VICTORY;
                }
                else if (end_level_result == SKIP_TO_LINGER)
                {
                    LOG_NG << "resuming from loaded linger state...\n";
                    //as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
                    saved_game_.set_snapshot(config());
                    if(!is_observer()) {
                        persist_.end_transaction();
                    }
                    return VICTORY;
                }

                break;
                //END CASES
            } // END SWITCH
        } //end if
    } catch(const game::load_game_exception &) {
        // Loading a new game is effectively a quit.
        //
        if ( game::load_game_exception::game != "" ) {
            saved_game_ = saved_game();
        }
        throw;
    } catch(network::error& e) {
        bool disconnect = false;
        if(e.socket) {
            e.disconnect();
            disconnect = true;
        }

        savegame::ingame_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format());
        save.save_game_interactive(gui_->video(), _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"), gui::YES_NO);
        if(disconnect) {
            throw network::error();
        } else {
            return QUIT;
        }
    }

    return QUIT;
}