recorder writes directly into saved_game object

Previously recorder had config memaber and saved_game had a config
memeber.
And when saving a game the config was copied from recorder to saved_game
and the other way when loading a game.

Now the recorder object directly writes into the saved_game object. This
saves some copying when saving and loading data.

I also moved the pos_ variable from the recorder object to the
saved_game replay_recorder_base object, This fixes a bug where saving a
game during a replay also caused to not yet played turns to be written
into the savefile.
This commit is contained in:
gfgtdf 2015-03-06 03:03:57 +01:00
parent 9aa6bb37fd
commit bb0ecd14c0
30 changed files with 312 additions and 197 deletions

View file

@ -956,6 +956,7 @@ set(wesnoth-main_SRC
replay.cpp replay.cpp
replay_helper.cpp replay_helper.cpp
replay_controller.cpp replay_controller.cpp
replay_recorder_base.cpp
resources.cpp resources.cpp
save_blocker.cpp save_blocker.cpp
save_index.cpp save_index.cpp

View file

@ -529,6 +529,7 @@ wesnoth_sources = Split("""
replay.cpp replay.cpp
replay_helper.cpp replay_helper.cpp
replay_controller.cpp replay_controller.cpp
replay_recorder_base.cpp
resources.cpp resources.cpp
save_blocker.cpp save_blocker.cpp
save_index.cpp save_index.cpp

View file

@ -1266,7 +1266,7 @@ size_t move_unit_and_record(const std::vector<map_location> &steps,
/* /*
enter the synced mode and do the actual movement. enter the synced mode and do the actual movement.
*/ */
recorder.add_synced_command("move",replay_helper::get_movement(steps, continued_move, skip_ally_sighted)); resources::recorder->add_synced_command("move",replay_helper::get_movement(steps, continued_move, skip_ally_sighted));
set_scontext_synced sync; set_scontext_synced sync;
size_t r = move_unit_internal(undo_stack, show_move, interrupted, mover); size_t r = move_unit_internal(undo_stack, show_move, interrupted, mover);
resources::controller->check_victory(); resources::controller->check_victory();

View file

@ -659,7 +659,7 @@ void undo_list::undo()
n_unit::id_manager::instance().set_save_id(last_unit_id - action->unit_id_diff); n_unit::id_manager::instance().set_save_id(last_unit_id - action->unit_id_diff);
// Bookkeeping. // Bookkeeping.
recorder.undo_cut(action->get_replay_data()); resources::recorder->undo_cut(action->get_replay_data());
redos_.push_back(action.release()); redos_.push_back(action.release());
resources::whiteboard->on_gamestate_change(); resources::whiteboard->on_gamestate_change();
@ -822,10 +822,10 @@ bool undo_list::auto_shroud_action::undo(int /*side*/, undo_list & undos)
{ {
// This does not count as an undoable action, so undo the next // This does not count as an undoable action, so undo the next
// action instead. // action instead.
recorder.undo(); resources::recorder->undo();
undos.undo(); undos.undo();
// Now keep the auto-shroud toggle at the top of the undo stack. // Now keep the auto-shroud toggle at the top of the undo stack.
recorder.add_synced_command("auto_shroud", replay_helper::get_auto_shroud(active)); resources::recorder->add_synced_command("auto_shroud", replay_helper::get_auto_shroud(active));
undos.add_auto_shroud(active, this->unit_id_diff); undos.add_auto_shroud(active, this->unit_id_diff);
// Shroud actions never get moved to the redo stack, so claim an error. // Shroud actions never get moved to the redo stack, so claim an error.
return false; return false;
@ -839,10 +839,10 @@ bool undo_list::update_shroud_action::undo(int /*side*/, undo_list & undos)
{ {
// This does not count as an undoable action, so undo the next // This does not count as an undoable action, so undo the next
// action instead. // action instead.
recorder.undo(); resources::recorder->undo();
undos.undo(); undos.undo();
// Now keep the shroud update at the top of the undo stack. // Now keep the shroud update at the top of the undo stack.
recorder.add_synced_command("update_shroud", replay_helper::get_update_shroud()); resources::recorder->add_synced_command("update_shroud", replay_helper::get_update_shroud());
undos.add_update_shroud(this->unit_id_diff); undos.add_update_shroud(this->unit_id_diff);
// Shroud actions never get moved to the redo stack, so claim an error. // Shroud actions never get moved to the redo stack, so claim an error.
@ -898,7 +898,7 @@ bool undo_list::dismiss_action::redo(int side)
<< ", which has no recall list!\n"; << ", which has no recall list!\n";
return false; return false;
} }
recorder.redo(replay_data); resources::recorder->redo(replay_data);
replay_data.clear(); replay_data.clear();
current_team.recall_list().erase_if_matches_id(dismissed_unit->id()); current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
return true; return true;
@ -931,7 +931,7 @@ bool undo_list::recall_action::redo(int side)
const std::string &msg = find_recall_location(side, loc, from, *un); const std::string &msg = find_recall_location(side, loc, from, *un);
if ( msg.empty() ) { if ( msg.empty() ) {
recorder.redo(replay_data); resources::recorder->redo(replay_data);
replay_data.clear(); replay_data.clear();
set_scontext_synced sync; set_scontext_synced sync;
recall_unit(id, current_team, loc, from, true, false); recall_unit(id, current_team, loc, from, true, false);
@ -979,7 +979,7 @@ bool undo_list::recruit_action::redo(int side)
if ( msg.empty() ) { if ( msg.empty() ) {
//MP_COUNTDOWN: restore recruitment bonus //MP_COUNTDOWN: restore recruitment bonus
current_team.set_action_bonus_count(1 + current_team.action_bonus_count()); current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
recorder.redo(replay_data); resources::recorder->redo(replay_data);
replay_data.clear(); replay_data.clear();
set_scontext_synced sync; set_scontext_synced sync;
recruit_unit(u_type, side, loc, from, true, false); recruit_unit(u_type, side, loc, from, true, false);
@ -1044,7 +1044,7 @@ bool undo_list::move_action::redo(int side)
} }
gui.invalidate_unit_after_move(route.front(), route.back()); gui.invalidate_unit_after_move(route.front(), route.back());
recorder.redo(replay_data); resources::recorder->redo(replay_data);
replay_data.clear(); replay_data.clear();
return true; return true;
} }

View file

@ -287,7 +287,7 @@ void attack_result::do_execute()
if(synced_context::get_synced_state() != synced_context::SYNCED) //RAII block for set_scontext_synced if(synced_context::get_synced_state() != synced_context::SYNCED) //RAII block for set_scontext_synced
{ {
//we don't use synced_context::run_in_synced_context because that wouldn't allow us to pass advancements_ //we don't use synced_context::run_in_synced_context because that wouldn't allow us to pass advancements_
recorder.add_synced_command("attack", replay_helper::get_attack(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, a_->type_id(), resources::recorder->add_synced_command("attack", replay_helper::get_attack(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, a_->type_id(),
d_->type_id(), a_->level(), d_->level(), resources::tod_manager->turn(), d_->type_id(), a_->level(), d_->level(), resources::tod_manager->turn(),
resources::tod_manager->get_time_of_day())); resources::tod_manager->get_time_of_day()));
set_scontext_synced sync; set_scontext_synced sync;

View file

@ -1368,8 +1368,8 @@ private:
const terrain_label *res; const terrain_label *res;
res = gui->labels().set_label(location, text, team_name, color); res = gui->labels().set_label(location, text, team_name, color);
if (res) if (res && resources::recorder)
recorder.add_label(res); resources::recorder->add_label(res);
} }
const formula_ai& ai_; const formula_ai& ai_;

View file

@ -69,21 +69,21 @@ void ai_testing::log_turn(const char* msg, unsigned int side)
c["units_cost"] = _units_cost; c["units_cost"] = _units_cost;
c["gold"] = _gold; c["gold"] = _gold;
c["villages"] = _villages; c["villages"] = _villages;
recorder.add_log_data("ai_log","turn_info",c); resources::recorder->add_log_data("ai_log","turn_info",c);
} }
void ai_testing::log_draw() void ai_testing::log_draw()
{ {
LOG_AI_TESTING << "DRAW:" << std::endl; LOG_AI_TESTING << "DRAW:" << std::endl;
recorder.add_log_data("ai_log","result","draw"); resources::recorder->add_log_data("ai_log","result","draw");
} }
void ai_testing::log_victory(std::set<unsigned int> winners) void ai_testing::log_victory(std::set<unsigned int> winners)
{ {
recorder.add_log_data("ai_log","result","victory"); resources::recorder->add_log_data("ai_log","result","victory");
for(std::set<unsigned int>::const_iterator w = winners.begin(); w != winners.end(); ++w) { for(std::set<unsigned int>::const_iterator w = winners.begin(); w != winners.end(); ++w) {
LOG_AI_TESTING << "WINNER: "<< *w <<std::endl; LOG_AI_TESTING << "WINNER: "<< *w <<std::endl;
recorder.add_log_data("ai_log","winner",str_cast(*w)); resources::recorder->add_log_data("ai_log","winner",str_cast(*w));
} }
} }
@ -93,22 +93,22 @@ void ai_testing::log_game_start()
int side = tm-resources::teams->begin()+1; int side = tm-resources::teams->begin()+1;
LOG_AI_TESTING << "AI_IDENTIFIER"<<side<<": " << ai::manager::get_active_ai_identifier_for_side(side) <<std::endl; LOG_AI_TESTING << "AI_IDENTIFIER"<<side<<": " << ai::manager::get_active_ai_identifier_for_side(side) <<std::endl;
LOG_AI_TESTING << "TEAM"<<side<<": " << tm->name() << std::endl; LOG_AI_TESTING << "TEAM"<<side<<": " << tm->name() << std::endl;
recorder.add_log_data("ai_log","ai_id"+str_cast(side),ai::manager::get_active_ai_identifier_for_side(side)); resources::recorder->add_log_data("ai_log","ai_id"+str_cast(side),ai::manager::get_active_ai_identifier_for_side(side));
recorder.add_log_data("ai_log","faction"+str_cast(side),tm->name()); resources::recorder->add_log_data("ai_log","faction"+str_cast(side),tm->name());
///@todo 1.9: add information about ai_config ///@todo 1.9: add information about ai_config
} }
LOG_AI_TESTING << "VERSION: " << game_config::revision << std::endl; LOG_AI_TESTING << "VERSION: " << game_config::revision << std::endl;
recorder.add_log_data("ai_log","version",game_config::revision); resources::recorder->add_log_data("ai_log","version",game_config::revision);
} }
void ai_testing::log_game_end() void ai_testing::log_game_end()
{ {
LOG_AI_TESTING << "GAME_END_TURN: "<< resources::tod_manager->turn() <<std::endl; LOG_AI_TESTING << "GAME_END_TURN: "<< resources::tod_manager->turn() <<std::endl;
recorder.add_log_data("ai_log","end_turn", resources::recorder->add_log_data("ai_log","end_turn",
str_cast(resources::tod_manager->turn())); str_cast(resources::tod_manager->turn()));
for (std::vector<team>::const_iterator tm = resources::teams->begin(); tm != resources::teams->end(); ++tm) { for (std::vector<team>::const_iterator tm = resources::teams->begin(); tm != resources::teams->end(); ++tm) {
int side = tm-resources::teams->begin()+1; int side = tm-resources::teams->begin()+1;
recorder.add_log_data("ai_log","end_gold"+str_cast(side),str_cast(tm->gold())); resources::recorder->add_log_data("ai_log","end_gold"+str_cast(side),str_cast(tm->gold()));
recorder.add_log_data("ai_log","end_units"+str_cast(side),str_cast(resources::gameboard->side_units(side))); resources::recorder->add_log_data("ai_log","end_units"+str_cast(side),str_cast(resources::gameboard->side_units(side)));
} }
} }

View file

@ -58,8 +58,8 @@ static void display_label(int /*side*/, const map_location& location, const std:
const terrain_label *res; const terrain_label *res;
res = gui->labels().set_label(location, text, team_name, color); res = gui->labels().set_label(location, text, team_name, color);
if (res) if (res && resources::recorder)
recorder.add_label(res); resources::recorder->add_label(res);
} }

View file

@ -163,8 +163,6 @@ gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_se
// Record the dismissal, then delete the unit. // Record the dismissal, then delete the unit.
synced_context::run_and_throw("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);
return gui::DELETE_ITEM; return gui::DELETE_ITEM;
} else { } else {

View file

@ -497,7 +497,7 @@ wml_action::wml_action(const std::string & tag, handler function)
/// @todo Finish experimenting. /// @todo Finish experimenting.
WML_HANDLER_FUNCTION(clear_global_variable,/**/,pcfg) WML_HANDLER_FUNCTION(clear_global_variable,/**/,pcfg)
{ {
if (get_replay_source().at_end() || (network::nconnections() != 0)) if (!resources::controller->is_replay())
verify_and_clear_global_variable(pcfg); verify_and_clear_global_variable(pcfg);
} }

View file

@ -476,8 +476,6 @@ static void enter_wait_mode(game_display& disp, const config& game_config,
case mp::ui::PLAY: case mp::ui::PLAY:
play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_CLIENT, play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_CLIENT,
preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe); preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe);
recorder.clear();
break; break;
case mp::ui::QUIT: case mp::ui::QUIT:
default: default:
@ -520,8 +518,6 @@ static bool enter_connect_mode(game_display& disp, const config& game_config,
case mp::ui::PLAY: case mp::ui::PLAY:
play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false,
!local_players_only); !local_players_only);
recorder.clear();
break; break;
case mp::ui::CREATE: case mp::ui::CREATE:
enter_create_mode(disp, game_config, state, local_players_only); enter_create_mode(disp, game_config, state, local_players_only);
@ -895,14 +891,14 @@ void start_local_game_commandline(game_display& disp, const config& game_config,
std::string label = ""; std::string label = "";
if (cmdline_opts.multiplayer_label) label = *cmdline_opts.multiplayer_label; if (cmdline_opts.multiplayer_label) label = *cmdline_opts.multiplayer_label;
recorder.add_log_data("ai_log","ai_label",label);
//resources::recorder->add_log_data("ai_log","ai_label",label);
unsigned int repeat = (cmdline_opts.multiplayer_repeat) ? *cmdline_opts.multiplayer_repeat : 1; unsigned int repeat = (cmdline_opts.multiplayer_repeat) ? *cmdline_opts.multiplayer_repeat : 1;
for(unsigned int i = 0; i < repeat; i++){ for(unsigned int i = 0; i < repeat; i++){
saved_game state_copy(state); saved_game state_copy(state);
play_game(disp, state_copy, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, false); play_game(disp, state_copy, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, false);
} }
recorder.clear();
} }
void start_client(game_display& disp, const config& game_config, void start_client(game_display& disp, const config& game_config,

View file

@ -127,7 +127,7 @@ static void show_carryover_message(saved_game& gamestate, playsingle_controller&
LEVEL_RESULT play_replay(display& disp, saved_game& gamestate, const config& game_config, LEVEL_RESULT play_replay(display& disp, saved_game& gamestate, const config& game_config,
const tdata_cache & tdata, bool is_unit_test) const tdata_cache & tdata, bool is_unit_test)
{ {
recorder = replay(gamestate.replay_data); gamestate.get_replay().set_pos(0);
// 'starting_pos' will contain the position we start the game from. // 'starting_pos' will contain the position we start the game from.
// this call also might expand [scenario] in case thatt there is no replay_start // this call also might expand [scenario] in case thatt there is no replay_start
const config& starting_pos = gamestate.get_replay_starting_pos(); const config& starting_pos = gamestate.get_replay_starting_pos();
@ -141,9 +141,6 @@ LEVEL_RESULT play_replay(display& disp, saved_game& gamestate, const config& gam
LEVEL_RESULT res = play_replay_level(game_config, tdata, disp.video(), gamestate, is_unit_test); LEVEL_RESULT res = play_replay_level(game_config, tdata, disp.video(), gamestate, is_unit_test);
recorder.clear();
gamestate.replay_data.clear();
return res; return res;
} catch(game::load_game_failed& e) { } catch(game::load_game_failed& e) {
ERR_NG << std::string(_("The game could not be loaded: ")) + " (game::load_game_failed) " + e.message << std::endl; ERR_NG << std::string(_("The game could not be loaded: ")) + " (game::load_game_failed) " + e.message << std::endl;
@ -256,8 +253,7 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate,
io_type_t io_type, bool skip_replay, io_type_t io_type, bool skip_replay,
bool network_game, bool blindfold_replay, bool is_unit_test) bool network_game, bool blindfold_replay, bool is_unit_test)
{ {
recorder = replay(gamestate.replay_data); gamestate.get_replay().set_to_end();
recorder.set_to_end();
gamestate.expand_scenario(); gamestate.expand_scenario();
@ -333,7 +329,6 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate,
} }
gamestate.convert_to_start_save(); gamestate.convert_to_start_save();
recorder.clear();
//If there is no next scenario we're done now. //If there is no next scenario we're done now.
if(gamestate.get_scenario_id().empty()) if(gamestate.get_scenario_id().empty())

View file

@ -65,6 +65,7 @@
#include "preferences_display.hpp" #include "preferences_display.hpp"
#include "replay.hpp" #include "replay.hpp"
#include "replay_helper.hpp" #include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp" #include "savegame.hpp"
#include "save_index.hpp" #include "save_index.hpp"
#include "scripting/game_lua_kernel.hpp" #include "scripting/game_lua_kernel.hpp"
@ -513,9 +514,9 @@ void menu_handler::show_chat_log()
{ {
config c; config c;
c["name"] = "prototype of chat log"; c["name"] = "prototype of chat log";
gui2::tchat_log chat_log_dialog(vconfig(c),&recorder); gui2::tchat_log chat_log_dialog(vconfig(c), resources::recorder);
chat_log_dialog.show(gui_->video()); chat_log_dialog.show(gui_->video());
//std::string text = recorder.build_chat_log(); //std::string text = resources::recorder->build_chat_log();
//gui::show_dialog(*gui_,NULL,_("Chat Log"),"",gui::CLOSE_ONLY,NULL,NULL,"",&text); //gui::show_dialog(*gui_,NULL,_("Chat Log"),"",gui::CLOSE_ONLY,NULL,NULL,"",&text);
} }
@ -935,7 +936,7 @@ void menu_handler::rename_unit()
const std::string label(N_("Name:")); const std::string label(N_("Name:"));
if(gui2::tedit_text::execute(title, label, name, gui_->video())) { if(gui2::tedit_text::execute(title, label, name, gui_->video())) {
recorder.add_rename(name, un->get_location()); resources::recorder->add_rename(name, un->get_location());
un->rename(name); un->rename(name);
gui_->invalidate_unit(); gui_->invalidate_unit();
} }
@ -1205,7 +1206,7 @@ void menu_handler::label_terrain(mouse_handler& mousehandler, bool team_only)
} }
const terrain_label* res = gui_->labels().set_label(loc, label, team_name, color); const terrain_label* res = gui_->labels().set_label(loc, label, team_name, color);
if (res) if (res)
recorder.add_label(res); resources::recorder->add_label(res);
} }
} }
@ -1215,7 +1216,7 @@ void menu_handler::clear_labels()
&& !board().is_observer()) && !board().is_observer())
{ {
gui_->labels().clear(gui_->current_team_name(), false); gui_->labels().clear(gui_->current_team_name(), false);
recorder.clear_labels(gui_->current_team_name(), false); resources::recorder->clear_labels(gui_->current_team_name(), false);
} }
} }
@ -2581,7 +2582,7 @@ void menu_handler::send_chat_message(const std::string& message, bool allies_onl
} }
} }
recorder.speak(cfg); resources::recorder->speak(cfg);
add_chat_message(time, cfg["id"], side, message, add_chat_message(time, cfg["id"], side, message,
private_message ? events::chat_handler::MESSAGE_PRIVATE : events::chat_handler::MESSAGE_PUBLIC); private_message ? events::chat_handler::MESSAGE_PRIVATE : events::chat_handler::MESSAGE_PUBLIC);

View file

@ -99,6 +99,7 @@ static void clear_resources()
resources::tod_manager = NULL; resources::tod_manager = NULL;
resources::tunnels = NULL; resources::tunnels = NULL;
resources::undo_stack = NULL; resources::undo_stack = NULL;
resources::recorder = NULL;
resources::units = NULL; resources::units = NULL;
resources::whiteboard.reset(); resources::whiteboard.reset();
@ -132,6 +133,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game,
, xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100))) , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
, statistics_context_(new statistics::scenario_context(level["name"])) , statistics_context_(new statistics::scenario_context(level["name"]))
, undo_stack_(new actions::undo_list(level.child("undo_stack"))) , undo_stack_(new actions::undo_list(level.child("undo_stack")))
, replay_(new replay(state_of_game.get_replay()))
, loading_game_(level["playing_team"].empty() == false) , loading_game_(level["playing_team"].empty() == false)
, player_number_(1) , player_number_(1)
, first_player_(level["playing_team"].to_int() + 1) , first_player_(level["playing_team"].to_int() + 1)
@ -156,6 +158,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game,
resources::teams = &gamestate_.board_.teams_; resources::teams = &gamestate_.board_.teams_;
resources::tod_manager = &gamestate_.tod_manager_; resources::tod_manager = &gamestate_.tod_manager_;
resources::undo_stack = undo_stack_.get(); resources::undo_stack = undo_stack_.get();
resources::recorder = replay_.get();
resources::units = &gamestate_.board_.units_; resources::units = &gamestate_.board_.units_;
resources::filter_con = &gamestate_; resources::filter_con = &gamestate_;
@ -364,7 +367,7 @@ void play_controller::maybe_do_init_side()
return; return;
} }
recorder.init_side(); resources::recorder->init_side();
do_init_side(); do_init_side();
} }

View file

@ -35,7 +35,7 @@ class game_data;
class team; class team;
class unit; class unit;
class wmi_pager; class wmi_pager;
class replay;
class saved_game; class saved_game;
struct mp_game_settings; struct mp_game_settings;
class game_classification; class game_classification;
@ -273,6 +273,7 @@ protected:
/// undo_list can be an incomplete type at this point (which reduces the /// undo_list can be an incomplete type at this point (which reduces the
/// number of files that depend on actions/undo.hpp). /// number of files that depend on actions/undo.hpp).
boost::scoped_ptr<actions::undo_list> undo_stack_; boost::scoped_ptr<actions::undo_list> undo_stack_;
boost::scoped_ptr<replay> replay_;
//if a team is specified whose turn it is, it means we're loading a game //if a team is specified whose turn it is, it means we're loading a game
//instead of starting a fresh one. Gets reset to false after init_side //instead of starting a fresh one. Gets reset to false after init_side

View file

@ -26,6 +26,7 @@
#include "mp_ui_alerts.hpp" #include "mp_ui_alerts.hpp"
#include "playturn.hpp" #include "playturn.hpp"
#include "preferences.hpp" #include "preferences.hpp"
#include "resources.hpp"
#include "savegame.hpp" #include "savegame.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "formula_string_utils.hpp" #include "formula_string_utils.hpp"
@ -322,7 +323,7 @@ void playmp_controller::after_human_turn(){
current_team().set_action_bonus_count(0); current_team().set_action_bonus_count(0);
current_team().set_countdown_time(new_time); current_team().set_countdown_time(new_time);
recorder.add_countdown_update(new_time, player_number_); resources::recorder->add_countdown_update(new_time, player_number_);
} }
LOG_NG << "playmp::after_human_turn...\n"; LOG_NG << "playmp::after_human_turn...\n";
@ -454,7 +455,7 @@ void playmp_controller::process_network_data()
} }
turn_info::PROCESS_DATA_RESULT res = turn_info::PROCESS_CONTINUE; turn_info::PROCESS_DATA_RESULT res = turn_info::PROCESS_CONTINUE;
config cfg; config cfg;
if(!recorder.at_end()) { if(!resources::recorder->at_end()) {
res = turn_info::replay_to_process_data_result(do_replay()); res = turn_info::replay_to_process_data_result(do_replay());
} }
else if(network_reader_.read(cfg)) { else if(network_reader_.read(cfg)) {

View file

@ -40,6 +40,7 @@
#include "playturn.hpp" #include "playturn.hpp"
#include "random_new_deterministic.hpp" #include "random_new_deterministic.hpp"
#include "replay_helper.hpp" #include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp" #include "savegame.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "synced_context.hpp" #include "synced_context.hpp"
@ -76,7 +77,7 @@ playsingle_controller::playsingle_controller(const config& level,
: play_controller(level, state_of_game, ticks, game_config, tdata, video, skip_replay) : play_controller(level, state_of_game, ticks, game_config, tdata, video, skip_replay)
, cursor_setter(cursor::NORMAL) , cursor_setter(cursor::NORMAL)
, textbox_info_() , textbox_info_()
, replay_sender_(recorder) , replay_sender_(*resources::recorder)
, network_reader_() , network_reader_()
, turn_data_(replay_sender_, network_reader_) , turn_data_(replay_sender_, network_reader_)
, end_turn_(END_TURN_NONE) , end_turn_(END_TURN_NONE)
@ -207,13 +208,13 @@ void playsingle_controller::play_scenario_init() {
fire_preload(); fire_preload();
assert(recorder.at_end()); assert(resources::recorder->at_end());
if(!loading_game_ ) if(!loading_game_ )
{ {
assert(recorder.empty()); assert(resources::recorder->empty());
recorder.add_start(); resources::recorder->add_start();
recorder.get_next_action(); resources::recorder->get_next_action();
//we can only use a set_scontext_synced with a non empty recorder. //we can only use a set_scontext_synced with a non empty recorder.
set_scontext_synced sync; set_scontext_synced sync;
@ -839,7 +840,7 @@ void playsingle_controller::sync_end_turn()
assert(synced_context::synced_state() == synced_context::UNSYNCED); assert(synced_context::synced_state() == synced_context::UNSYNCED);
if(end_turn_ == END_TURN_REQUIRED && current_team().is_local()) if(end_turn_ == END_TURN_REQUIRED && current_team().is_local())
{ {
recorder.end_turn(); resources::recorder->end_turn();
end_turn_ = END_TURN_SYNCED; end_turn_ = END_TURN_SYNCED;
} }
} }

View file

@ -99,7 +99,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::handle_turn(const config& t)
/** @todo FIXME: Check what commands we execute when it's our turn! */ /** @todo FIXME: Check what commands we execute when it's our turn! */
//note, that this function might call itself recursively: do_replay -> ... -> get_user_choice -> ... -> playmp_controller::pull_remote_choice -> sync_network -> handle_turn //note, that this function might call itself recursively: do_replay -> ... -> get_user_choice -> ... -> playmp_controller::pull_remote_choice -> sync_network -> handle_turn
recorder.add_config(t, replay::MARK_AS_SENT); resources::recorder->add_config(t, replay::MARK_AS_SENT);
PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay()); PROCESS_DATA_RESULT retv = replay_to_process_data_result(do_replay());
return retv; return retv;
} }
@ -132,7 +132,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
assert(network::nconnections() <= 1); assert(network::nconnections() <= 1);
assert(cfg.all_children_count() == 1); assert(cfg.all_children_count() == 1);
assert(cfg.attribute_range().first == cfg.attribute_range().second); assert(cfg.attribute_range().first == cfg.attribute_range().second);
if(!recorder.at_end()) if(!resources::recorder->at_end())
{ {
ERR_NW << "processing network data while still having data on the replay." << std::endl; ERR_NW << "processing network data while still having data on the replay." << std::endl;
} }

View file

@ -38,6 +38,7 @@
#include "statistics.hpp" #include "statistics.hpp"
#include "unit.hpp" #include "unit.hpp"
#include "whiteboard/manager.hpp" #include "whiteboard/manager.hpp"
#include "replay_recorder_base.hpp"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
@ -142,11 +143,6 @@ static time_t get_time(const config &speak)
return time; return time;
} }
// FIXME: this one now has to be assigned with set_random_generator
// from play_level or similar. We should surely hunt direct
// references to it from this very file and move it out of here.
replay recorder;
chat_msg::chat_msg(const config &cfg) chat_msg::chat_msg(const config &cfg)
: color_() : color_()
, nick_() , nick_()
@ -183,22 +179,10 @@ chat_msg::~chat_msg()
{ {
} }
replay::replay() : replay::replay(replay_recorder_base& base)
cfg_(), : base_(&base)
pos_(0), , message_locations()
message_locations()
{} {}
replay::replay(const config& cfg) :
cfg_(cfg),
pos_(0),
message_locations()
{}
void replay::append(const config& cfg)
{
cfg_.append(cfg);
}
/* /*
TODO: there should be different types of OOS messages: TODO: there should be different types of OOS messages:
1)the normal OOS message 1)the normal OOS message
@ -314,27 +298,27 @@ void replay::end_turn()
void replay::add_log_data(const std::string &key, const std::string &var) void replay::add_log_data(const std::string &key, const std::string &var)
{ {
config& ulog = cfg_.child_or_add("upload_log"); config& ulog = base_->get_upload_log();
ulog[key] = var; ulog[key] = var;
} }
void replay::add_log_data(const std::string &category, const std::string &key, const std::string &var) void replay::add_log_data(const std::string &category, const std::string &key, const std::string &var)
{ {
config& ulog = cfg_.child_or_add("upload_log"); config& ulog = base_->get_upload_log();
config& cat = ulog.child_or_add(category); config& cat = ulog.child_or_add(category);
cat[key] = var; cat[key] = var;
} }
void replay::add_log_data(const std::string &category, const std::string &key, const config &c) void replay::add_log_data(const std::string &category, const std::string &key, const config &c)
{ {
config& ulog = cfg_.child_or_add("upload_log"); config& ulog = base_->get_upload_log();
config& cat = ulog.child_or_add(category); config& cat = ulog.child_or_add(category);
cat.add_child(key,c); cat.add_child(key,c);
} }
void replay::add_chat_message_location() void replay::add_chat_message_location()
{ {
message_locations.push_back(pos_-1); message_locations.push_back(base_->get_pos() - 1);
} }
void replay::speak(const config& cfg) void replay::speak(const config& cfg)
@ -355,7 +339,7 @@ void replay::add_chat_log_entry(const config &cfg, std::back_insert_iterator<std
void replay::remove_command(int index) void replay::remove_command(int index)
{ {
cfg_.remove_child("command", index); base_->remove_command(index);
std::vector<int>::reverse_iterator loc_it; std::vector<int>::reverse_iterator loc_it;
for (loc_it = message_locations.rbegin(); loc_it != message_locations.rend() && index < *loc_it;++loc_it) for (loc_it = message_locations.rbegin(); loc_it != message_locations.rend() && index < *loc_it;++loc_it)
{ {
@ -412,13 +396,12 @@ struct async_cmd
void replay::redo(const config& cfg) void replay::redo(const config& cfg)
{ {
//we set pos_ = ncommands(), if we recorded something else in the meantime it doesn't make sense to redo an action. assert(base_->get_pos() == ncommands());
assert(pos_ == ncommands());
BOOST_FOREACH(const config &cmd, cfg.child_range("command")) BOOST_FOREACH(const config &cmd, cfg.child_range("command"))
{ {
/*config &cfg = */cfg_.add_child("command", cmd); base_->add_child() = cmd;
} }
pos_ = ncommands(); base_->set_to_end();
} }
@ -426,7 +409,7 @@ void replay::redo(const config& cfg)
config& replay::get_last_real_command() config& replay::get_last_real_command()
{ {
for (int cmd_num = pos_ - 1; cmd_num >= 0; --cmd_num) for (int cmd_num = base_->get_pos() - 1; cmd_num >= 0; --cmd_num)
{ {
config &c = command(cmd_num); config &c = command(cmd_num);
const config &cc = c; const config &cc = c;
@ -445,8 +428,8 @@ config& replay::get_last_real_command()
void replay::undo_cut(config& dst) void replay::undo_cut(config& dst)
{ {
assert(dst.empty()); assert(dst.empty());
//pos_ < ncommands() could mean that we try to undo commands that haven't been executed yet. //assert that we are not undoing a command which we didn't execute yet.
assert(pos_ == ncommands()); assert(at_end());
std::vector<async_cmd> async_cmds; std::vector<async_cmd> async_cmds;
// Remember commands not yet synced and skip over them. // Remember commands not yet synced and skip over them.
// We assume that all already sent (sent=yes) data isn't undoable // We assume that all already sent (sent=yes) data isn't undoable
@ -478,13 +461,13 @@ void replay::undo_cut(config& dst)
if (cmd < 0) return; if (cmd < 0) return;
//we add the commands that we want to remove later to the passed cfg first. //we add the commands that we want to remove later to the passed cfg first.
dst.add_child("command", cfg_.child("command", cmd)); dst.add_child("command", base_->get_command_at(cmd));
//we do this in a seperate loop because we don't want to loop forward in the loop while when we remove the elements to keepo the indexes simple. //we do this in a seperate loop because we don't want to loop forward in the loop while when we remove the elements to keepo the indexes simple.
for(int cmd_2 = cmd + 1; cmd_2 < ncommands(); ++cmd_2) for(int cmd_2 = cmd + 1; cmd_2 < ncommands(); ++cmd_2)
{ {
if(command(cmd_2)["dependent"].to_bool(false)) if(command(cmd_2)["dependent"].to_bool(false))
{ {
dst.add_child("command", cfg_.child("command", cmd_2)); dst.add_child("command", base_->get_command_at(cmd_2));
} }
} }
@ -543,13 +526,15 @@ void replay::undo_cut(config& dst)
if (config &async_child = ac.cfg->child("rename")) if (config &async_child = ac.cfg->child("rename"))
{ {
map_location aloc(async_child, resources::gamedata); map_location aloc(async_child, resources::gamedata);
if (src == aloc) remove_command(ac.num); if (src == aloc) {
remove_command(ac.num);
}
} }
} }
} }
} }
remove_command(cmd); remove_command(cmd);
pos_ = ncommands(); set_to_end();
} }
void replay::undo() void replay::undo()
@ -560,72 +545,74 @@ void replay::undo()
config &replay::command(int n) config &replay::command(int n)
{ {
config & retv = cfg_.child("command", n); config & retv = base_->get_command_at(n);
assert(retv); assert(retv);
return retv; return retv;
} }
int replay::ncommands() const int replay::ncommands() const
{ {
return cfg_.child_count("command"); return base_->size();
} }
config& replay::add_command() config& replay::add_command()
{ {
//pos_ != ncommands() means that there is a command on the replay which would be skipped. //if we werent at teh end of teh replay we sould skip one or mutiple commands.
assert(pos_ == ncommands()); assert(at_end());
pos_ = ncommands()+1; config& retv = base_->add_child();
return cfg_.add_child("command"); set_to_end();
return retv;
} }
config& replay::add_nonundoable_command() config& replay::add_nonundoable_command()
{ {
config& r = cfg_.add_child_at("command",config(), pos_); config& r = base_->insert_command(base_->get_pos());
r["undo"] = false; r["undo"] = false;
++pos_; base_->set_pos(base_->get_pos() + 1);
return r; return r;
} }
void replay::start_replay() void replay::start_replay()
{ {
pos_ = 0; base_->set_pos(0);
} }
void replay::revert_action() void replay::revert_action()
{ {
if (pos_ > 0)
--pos_; if (base_->get_pos() > 0)
base_->set_pos(base_->get_pos() - 1);
} }
config* replay::get_next_action() config* replay::get_next_action()
{ {
if (pos_ >= ncommands()) if (at_end())
return NULL; return NULL;
LOG_REPLAY << "up to replay action " << pos_ + 1 << '/' << ncommands() << '\n'; LOG_REPLAY << "up to replay action " << base_->get_pos() + 1 << '/' << ncommands() << '\n';
config* retv = &command(pos_); config* retv = &command(base_->get_pos());
++pos_; base_->set_pos(base_->get_pos() + 1);
return retv; return retv;
} }
bool replay::at_end() const bool replay::at_end() const
{ {
return pos_ >= ncommands(); assert(base_->get_pos() <= ncommands());
return base_->get_pos() == ncommands();
} }
void replay::set_to_end() void replay::set_to_end()
{ {
pos_ = ncommands(); base_->set_to_end();
} }
void replay::clear() void replay::clear()
{ {
message_locations.clear(); message_locations.clear();
message_log.clear(); message_log.clear();
cfg_ = config(); //FIXME
pos_ = 0;
} }
bool replay::empty() bool replay::empty()
@ -637,25 +624,20 @@ void replay::add_config(const config& cfg, MARK_SENT mark)
{ {
BOOST_FOREACH(const config &cmd, cfg.child_range("command")) BOOST_FOREACH(const config &cmd, cfg.child_range("command"))
{ {
config &cfg = cfg_.add_child("command", cmd); config &cfg = base_->insert_command(base_->size());
cfg = cmd;
if(mark == MARK_AS_SENT) { if(mark == MARK_AS_SENT) {
cfg["sent"] = true; cfg["sent"] = true;
} }
} }
} }
replay& get_replay_source()
{
return recorder;
}
bool replay::add_start_if_not_there_yet() bool replay::add_start_if_not_there_yet()
{ {
//this method would confuse the value of 'pos' otherwise //this method would confuse the value of 'pos' otherwise
assert(pos_ == 0); assert(base_->get_pos() == 0);
if(at_end() || !cfg_.child("command", pos_).has_child("start")) if(at_end() || !base_->get_command_at(0).has_child("start"))
{ {
cfg_.add_child_at("command",config_of("start", config()),pos_); base_->insert_command(0) = config_of("start", config());
return true; return true;
} }
else else
@ -695,7 +677,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
const int side_num = resources::controller->current_side(); const int side_num = resources::controller->current_side();
while(true) while(true)
{ {
const config *cfg = get_replay_source().get_next_action(); const config *cfg = resources::recorder->get_next_action();
const bool is_synced = (synced_context::get_synced_state() == synced_context::SYNCED); const bool is_synced = (synced_context::get_synced_state() == synced_context::SYNCED);
DBG_REPLAY << "in do replay with is_synced=" << is_synced << "\n"; DBG_REPLAY << "in do replay with is_synced=" << is_synced << "\n";
@ -727,7 +709,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
const std::string &message = child["message"]; const std::string &message = child["message"];
//if (!preferences::parse_should_show_lobby_join(speaker_name, message)) return; //if (!preferences::parse_should_show_lobby_join(speaker_name, message)) return;
bool is_whisper = (speaker_name.find("whisper: ") == 0); bool is_whisper = (speaker_name.find("whisper: ") == 0);
get_replay_source().add_chat_message_location(); resources::recorder->add_chat_message_location();
if (!resources::controller->is_skipping_replay() || is_whisper) { if (!resources::controller->is_skipping_replay() || is_whisper) {
int side = child["side"]; int side = child["side"];
resources::screen->get_chat_manager().add_chat_message(get_time(child), speaker_name, side, message, resources::screen->get_chat_manager().add_chat_message(get_time(child), speaker_name, side, message,
@ -775,7 +757,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
if(is_synced) if(is_synced)
{ {
replay::process_error("found side initialization in replay expecting a user choice\n" ); replay::process_error("found side initialization in replay expecting a user choice\n" );
get_replay_source().revert_action(); resources::recorder->revert_action();
return REPLAY_FOUND_DEPENDENT; return REPLAY_FOUND_DEPENDENT;
} }
else else
@ -790,7 +772,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
if(is_synced) if(is_synced)
{ {
replay::process_error("found turn end in replay while expecting a user choice\n" ); replay::process_error("found turn end in replay while expecting a user choice\n" );
get_replay_source().revert_action(); resources::recorder->revert_action();
return REPLAY_FOUND_DEPENDENT; return REPLAY_FOUND_DEPENDENT;
} }
else else
@ -832,7 +814,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
assert(cfg->all_children_count() == 1); assert(cfg->all_children_count() == 1);
std::string child_name = cfg->all_children_range().first->key; std::string child_name = cfg->all_children_range().first->key;
DBG_REPLAY << "got an dependent action name = " << child_name <<"\n"; DBG_REPLAY << "got an dependent action name = " << child_name <<"\n";
get_replay_source().revert_action(); resources::recorder->revert_action();
return REPLAY_FOUND_DEPENDENT; return REPLAY_FOUND_DEPENDENT;
} }
else else
@ -844,7 +826,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
if(is_synced) if(is_synced)
{ {
replay::process_error("found [" + commandname + "] command in replay expecting a user choice\n" ); replay::process_error("found [" + commandname + "] command in replay expecting a user choice\n" );
get_replay_source().revert_action(); resources::recorder->revert_action();
return REPLAY_FOUND_DEPENDENT; return REPLAY_FOUND_DEPENDENT;
} }
else else
@ -964,7 +946,7 @@ static std::map<int, config> get_user_choice_internal(const std::string &name, c
} }
bool has_local_side = local_side != 0; bool has_local_side = local_side != 0;
bool is_replay_end = get_replay_source().at_end(); bool is_replay_end = resources::recorder->at_end();
if (is_replay_end && has_local_side) if (is_replay_end && has_local_side)
{ {
@ -974,7 +956,7 @@ static std::map<int, config> get_user_choice_internal(const std::string &name, c
DBG_REPLAY << "MP synchronization: local choice\n"; DBG_REPLAY << "MP synchronization: local choice\n";
config cfg = uch.query_user(local_side); config cfg = uch.query_user(local_side);
recorder.user_input(name, cfg, local_side); resources::recorder->user_input(name, cfg, local_side);
retv[local_side]= cfg; retv[local_side]= cfg;
//send data to others. //send data to others.
@ -1001,13 +983,13 @@ static std::map<int, config> get_user_choice_internal(const std::string &name, c
{ {
DBG_REPLAY << "MP synchronization: extracting choice from replay with has_local_side=" << has_local_side << "\n"; DBG_REPLAY << "MP synchronization: extracting choice from replay with has_local_side=" << has_local_side << "\n";
const config *action = get_replay_source().get_next_action(); const config *action = resources::recorder->get_next_action();
assert(action); //action cannot be null because get_replay_source().at_end() returned false. assert(action); //action cannot be null because resources::recorder->at_end() returned false.
if( !action->has_child(name) || !(*action)["dependent"].to_bool()) if( !action->has_child(name) || !(*action)["dependent"].to_bool())
{ {
replay::process_error("[" + name + "] expected but none found\n. found instead:\n" + action->debug()); replay::process_error("[" + name + "] expected but none found\n. found instead:\n" + action->debug());
//We save this action for later //We save this action for later
get_replay_source().revert_action(); resources::recorder->revert_action();
//and let the user try to get the intended result. //and let the user try to get the intended result.
BOOST_FOREACH(int side, sides) BOOST_FOREACH(int side, sides)
{ {

View file

@ -26,6 +26,7 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <set> #include <set>
class replay_recorder_base;
class game_display; class game_display;
class terrain_label; class terrain_label;
class unit_map; class unit_map;
@ -50,11 +51,9 @@ private:
class replay class replay
{ {
public: public:
replay(); explicit replay(replay_recorder_base& base);
explicit replay(const config& cfg);
void append(const config& cfg);
void add_start(); void add_start();
void add_countdown_update(int value,int team); void add_countdown_update(int value,int team);
@ -93,8 +92,6 @@ public:
//ignored by the undo system. //ignored by the undo system.
enum DATA_TYPE { ALL_DATA, NON_UNDO_DATA }; enum DATA_TYPE { ALL_DATA, NON_UNDO_DATA };
config get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type=ALL_DATA); config get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type=ALL_DATA);
config get_last_turn(int num_turns=1);
const config& get_replay_data() const { return cfg_; }
void undo(); void undo();
/* /*
@ -145,16 +142,10 @@ private:
* @return a reference to the added command * @return a reference to the added command
*/ */
config& add_nonundoable_command(); config& add_nonundoable_command();
config cfg_; replay_recorder_base* base_;
int pos_;
std::vector<int> message_locations; std::vector<int> message_locations;
}; };
replay& get_replay_source();
extern replay recorder;
enum REPLAY_RETURN enum REPLAY_RETURN
{ {
REPLAY_RETURN_AT_END, REPLAY_RETURN_AT_END,

View file

@ -97,7 +97,7 @@ void play_replay_level_main_loop(replay_controller & replaycontroller, bool & is
void replay_controller::try_run_to_completion() { void replay_controller::try_run_to_completion() {
for (;;) { for (;;) {
play_slice(); play_slice();
if (recorder.at_end()) { if (resources::recorder->at_end()) {
return; return;
} else { } else {
if (!is_playing_) { if (!is_playing_) {
@ -271,7 +271,7 @@ void replay_controller::replay_ui_playback_should_stop()
if(!replay_ui_has_all_buttons()) if(!replay_ui_has_all_buttons())
return; return;
if(!recorder.at_end()) { if(!resources::recorder->at_end()) {
play_button()->enable(true); play_button()->enable(true);
reset_button()->enable(true); reset_button()->enable(true);
play_turn_button()->enable(true); play_turn_button()->enable(true);
@ -317,7 +317,7 @@ void replay_controller::reset_replay()
it_is_a_new_turn_ = true; it_is_a_new_turn_ = true;
skip_replay_ = false; skip_replay_ = false;
gamestate_.tod_manager_= tod_manager_start_; gamestate_.tod_manager_= tod_manager_start_;
recorder.start_replay(); resources::recorder->start_replay();
saved_game_ = saved_game_start_; saved_game_ = saved_game_start_;
gamestate_.board_ = gameboard_start_; gamestate_.board_ = gameboard_start_;
gui_->change_display_context(&gamestate_.board_); //this doesn't change the pointer value, but it triggers the gui to update the internal terrain builder object, gui_->change_display_context(&gamestate_.board_); //this doesn't change the pointer value, but it triggers the gui to update the internal terrain builder object,
@ -359,14 +359,14 @@ void replay_controller::reset_replay()
// Scenario initialization. (c.f. playsingle_controller::play_scenario()) // Scenario initialization. (c.f. playsingle_controller::play_scenario())
fire_preload(); fire_preload();
{ //block for set_scontext_synced { //block for set_scontext_synced
if(recorder.add_start_if_not_there_yet()) if(resources::recorder->add_start_if_not_there_yet())
{ {
ERR_REPLAY << "inserted missing [start]" << std::endl; ERR_REPLAY << "inserted missing [start]" << std::endl;
} }
config* pstart = recorder.get_next_action(); config* pstart = resources::recorder->get_next_action();
assert(pstart->has_child("start")); assert(pstart->has_child("start"));
/* /*
use this after recorder.add_synced_command use this after resources::recorder->add_synced_command
because set_scontext_synced sets the checkup to the last added command because set_scontext_synced sets the checkup to the last added command
*/ */
set_scontext_synced sync; set_scontext_synced sync;
@ -476,7 +476,7 @@ void replay_controller::replay_skip_animation(){
void replay_controller::play_replay() void replay_controller::play_replay()
{ {
if (recorder.at_end()) if (resources::recorder->at_end())
{ {
} }
@ -495,7 +495,7 @@ void replay_controller::play_replay()
void replay_controller::play_replay_main_loop() void replay_controller::play_replay_main_loop()
{ {
DBG_REPLAY << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n"; DBG_REPLAY << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
for(; !recorder.at_end() && is_playing_; first_player_ = 1) { for(; !resources::recorder->at_end() && is_playing_; first_player_ = 1) {
play_turn(); play_turn();
} }
} }
@ -512,7 +512,7 @@ void replay_controller::play_turn()
bool last_team = false; bool last_team = false;
while ( (!last_team) && (!recorder.at_end()) && is_playing_ ){ while ( (!last_team) && (!resources::recorder->at_end()) && is_playing_ ){
last_team = static_cast<size_t>(player_number_) == gamestate_.board_.teams().size(); last_team = static_cast<size_t>(player_number_) == gamestate_.board_.teams().size();
play_side(); play_side();
play_slice(); play_slice();
@ -624,5 +624,5 @@ void replay_controller::handle_generic_event(const std::string& name)
} }
bool replay_controller::recorder_at_end() { bool replay_controller::recorder_at_end() {
return recorder.at_end(); return resources::recorder->at_end();
} }

View file

@ -0,0 +1,104 @@
#include "replay_recorder_base.hpp"
#include "serialization\binary_or_text.hpp"
replay_recorder_base::replay_recorder_base(void)
: upload_log_()
, commands_()
, pos_(0)
{
}
replay_recorder_base::~replay_recorder_base(void)
{
}
int replay_recorder_base::get_pos() const
{
return pos_;
}
int replay_recorder_base::size() const
{
return commands_.size();
}
config& replay_recorder_base::get_command_at(int pos)
{
assert(pos < size());
return commands_[pos];
}
config& replay_recorder_base::add_child()
{
assert(pos_ <= size());
commands_.insert(commands_.begin() + pos_, new config());
++pos_;
return commands_[pos_ - 1];
}
void replay_recorder_base::set_pos(int pos)
{
assert(pos <= size());
pos_ = pos;
}
void replay_recorder_base::set_to_end()
{
pos_ = size();
}
config& replay_recorder_base::get_upload_log()
{
return upload_log_;
}
void replay_recorder_base::remove_command(int index)
{
assert(index < size());
commands_.erase(commands_.begin() + index);
if(index < pos_)
{
--pos_;
}
}
config& replay_recorder_base::insert_command(int index)
{
assert(index <= size());
if(index < pos_)
{
++pos_;
}
return *commands_.insert(commands_.begin() + index, new config());
}
void replay_recorder_base::append_config(const config& data)
{
if(const config& upload_log = data.child("upload_log"))
{
upload_log_ = upload_log;
}
BOOST_FOREACH(const config& command, data.child_range("command"))
{
commands_.push_back(new config(command));
}
}
void replay_recorder_base::write(config_writer& out) const
{
out.write_child("upload_log", upload_log_);
for(int i = 0; i < pos_; ++i)
{
out.write_child("command", commands_[i]);
}
}
void replay_recorder_base::write(config& out) const
{
out.add_child("upload_log", upload_log_);
for(int i = 0; i < pos_; ++i)
{
out.add_child("command", commands_[i]);
}
}

View file

@ -0,0 +1,39 @@
#pragma once
#include <cassert>
#include <boost/ptr_container/ptr_vector.hpp>
class config_writer;
class replay_recorder_base
{
public:
replay_recorder_base();
~replay_recorder_base();
int get_pos() const;
int size() const;
config& get_command_at(int pos);
config& add_child();
config& get_upload_log();
void remove_command(int index);
config& insert_command(int index);
void set_to_end();
void set_pos(int pos);
void append_config(const config& data);
void write(config_writer& out) const;
void write(config& out) const;
protected:
config upload_log_;
boost::ptr_vector<config> commands_;
int pos_;
};

View file

@ -27,6 +27,7 @@ namespace resources
game_display *screen = NULL; game_display *screen = NULL;
soundsource::manager *soundsources = NULL; soundsource::manager *soundsources = NULL;
std::vector<team> *teams = NULL; std::vector<team> *teams = NULL;
replay *recorder = NULL;
::tod_manager *tod_manager = NULL; ::tod_manager *tod_manager = NULL;
fake_unit_manager *fake_units = NULL; fake_unit_manager *fake_units = NULL;
pathfind::manager *tunnels = NULL; pathfind::manager *tunnels = NULL;

View file

@ -33,6 +33,7 @@ class unit_map;
class persist_manager; class persist_manager;
class game_classification; class game_classification;
struct mp_game_settings; struct mp_game_settings;
class replay;
namespace actions { class undo_list; } namespace actions { class undo_list; }
namespace game_events { class manager; } namespace game_events { class manager; }
@ -59,6 +60,7 @@ namespace resources
extern const mp_game_settings *mp_settings; extern const mp_game_settings *mp_settings;
extern soundsource::manager *soundsources; extern soundsource::manager *soundsources;
extern std::vector<team> *teams; extern std::vector<team> *teams;
extern replay *recorder;
extern fake_unit_manager *fake_units; extern fake_unit_manager *fake_units;
extern ::tod_manager *tod_manager; extern ::tod_manager *tod_manager;
extern pathfind::manager *tunnels; extern pathfind::manager *tunnels;

View file

@ -56,27 +56,27 @@ static lg::log_domain log_engine("engine");
#define DBG_NG LOG_STREAM(debug, log_engine) #define DBG_NG LOG_STREAM(debug, log_engine)
saved_game::saved_game() saved_game::saved_game()
: replay_data() : has_carryover_expanded_(false)
, has_carryover_expanded_(false)
, carryover_(carryover_info().to_config()) , carryover_(carryover_info().to_config())
, replay_start_() , replay_start_()
, classification_() , classification_()
, mp_settings_() , mp_settings_()
, starting_pos_type_(STARTINGPOS_NONE) , starting_pos_type_(STARTINGPOS_NONE)
, starting_pos_() , starting_pos_()
, replay_data_()
{ {
} }
saved_game::saved_game(const config& cfg) saved_game::saved_game(const config& cfg)
: replay_data() : has_carryover_expanded_(false)
, has_carryover_expanded_(false)
, carryover_() , carryover_()
, replay_start_() , replay_start_()
, classification_(cfg) , classification_(cfg)
, mp_settings_(cfg) , mp_settings_(cfg)
, starting_pos_type_(STARTINGPOS_NONE) , starting_pos_type_(STARTINGPOS_NONE)
, starting_pos_() , starting_pos_()
, replay_data_()
{ {
log_scope("read_game"); log_scope("read_game");
@ -94,12 +94,11 @@ saved_game::saved_game(const config& cfg)
//Serversided replays can contain multiple [replay] //Serversided replays can contain multiple [replay]
replay_start_ = cfg.child_or_empty("replay_start"); replay_start_ = cfg.child_or_empty("replay_start");
replay_data = config(); //cfg.child_or_empty("replay");
BOOST_FOREACH(const config& replay, cfg.child_range("replay")) BOOST_FOREACH(const config& replay, cfg.child_range("replay"))
{ {
replay_data.append_children(replay); replay_data_.append_config(replay);
} }
replay_data_.set_to_end();
if(const config& snapshot = cfg.child("snapshot")) if(const config& snapshot = cfg.child("snapshot"))
{ {
this->starting_pos_type_ = STARTINGPOS_SNAPSHOT; this->starting_pos_type_ = STARTINGPOS_SNAPSHOT;
@ -121,14 +120,14 @@ saved_game::saved_game(const config& cfg)
} }
saved_game::saved_game(const saved_game& state) saved_game::saved_game(const saved_game& state)
: replay_data(state.replay_data) : has_carryover_expanded_(state.has_carryover_expanded_)
, has_carryover_expanded_(state.has_carryover_expanded_)
, carryover_(state.carryover_) , carryover_(state.carryover_)
, replay_start_(state.replay_start_) , replay_start_(state.replay_start_)
, classification_(state.classification_) , classification_(state.classification_)
, mp_settings_(state.mp_settings_) , mp_settings_(state.mp_settings_)
, starting_pos_type_(state.starting_pos_type_) , starting_pos_type_(state.starting_pos_type_)
, starting_pos_(state.starting_pos_) , starting_pos_(state.starting_pos_)
, replay_data_(state.replay_data_)
{ {
} }
@ -156,10 +155,9 @@ void saved_game::write_config(config_writer& out) const
{ {
out.write_child("replay_start", replay_start_); out.write_child("replay_start", replay_start_);
} }
if(!this->replay_data.empty()) out.open_child("replay");
{ replay_data_.write(out);
out.write_child("replay", replay_data); out.close_child("replay");
}
write_carryover(out); write_carryover(out);
} }
@ -445,7 +443,7 @@ void saved_game::convert_to_start_save()
sides.rng().rotate_random(); sides.rng().rotate_random();
carryover_ = sides.to_config(); carryover_ = sides.to_config();
has_carryover_expanded_ = false; has_carryover_expanded_ = false;
replay_data = config(); replay_data_ = replay_recorder_base();
replay_start_ = config(); replay_start_ = config();
remove_snapshot(); remove_snapshot();
} }
@ -458,10 +456,8 @@ config saved_game::to_config() const
{ {
r.add_child("replay_start", replay_start_); r.add_child("replay_start", replay_start_);
} }
if(!this->replay_data.empty()) replay_data_.write(r.add_child("replay"));
{
r.add_child("replay", replay_data);
}
if(starting_pos_type_ == STARTINGPOS_SNAPSHOT) if(starting_pos_type_ == STARTINGPOS_SNAPSHOT)
{ {
r.add_child("snapshot", starting_pos_); r.add_child("snapshot", starting_pos_);

View file

@ -5,6 +5,8 @@
#include "config.hpp" #include "config.hpp"
#include "game_classification.hpp" #include "game_classification.hpp"
#include "mp_game_settings.hpp" #include "mp_game_settings.hpp"
#include "replay_recorder_base.hpp"
class config_writer; class config_writer;
@ -94,13 +96,9 @@ public:
/* removes network_ai and network controller types*/ /* removes network_ai and network controller types*/
void unify_controllers(); void unify_controllers();
/**
* If the game is saved mid-level, we have a series of replay steps
* to take the game up to the position it was saved at.
*/
config replay_data;
void set_default_save_id(); void set_default_save_id();
replay_recorder_base& get_replay() { return replay_data_; }
const replay_recorder_base& get_replay() const { return replay_data_; }
private: private:
bool has_carryover_expanded_; bool has_carryover_expanded_;
@ -122,6 +120,8 @@ private:
This can eigher be a [scenario] for a fresh game or a [snapshot] if this is a reloaded game This can eigher be a [scenario] for a fresh game or a [snapshot] if this is a reloaded game
*/ */
config starting_pos_; config starting_pos_;
replay_recorder_base replay_data_;
}; };

View file

@ -481,7 +481,6 @@ void savegame::set_filename(std::string filename)
void savegame::before_save() void savegame::before_save()
{ {
gamestate_.replay_data = recorder.get_replay_data();
} }
bool savegame::save_game(CVideo* video, const std::string& filename) bool savegame::save_game(CVideo* video, const std::string& filename)
@ -609,7 +608,10 @@ void replay_savegame::write_game(config_writer &out) {
gamestate().write_carryover(out); gamestate().write_carryover(out);
out.write_child("replay_start", gamestate().replay_start()); out.write_child("replay_start", gamestate().replay_start());
out.write_child("replay", gamestate().replay_data);
out.open_child("replay");
gamestate().get_replay().write(out);
out.close_child("replay");
} }
@ -693,7 +695,9 @@ void ingame_savegame::write_game(config_writer &out) {
gamestate().write_carryover(out); gamestate().write_carryover(out);
out.write_child("snapshot",gamestate().get_starting_pos()); out.write_child("snapshot",gamestate().get_starting_pos());
out.write_child("replay_start", gamestate().replay_start()); out.write_child("replay_start", gamestate().replay_start());
out.write_child("replay", gamestate().replay_data); out.open_child("replay");
gamestate().get_replay().write(out);
out.close_child("replay");
} }
//changes done during 1.11.0-dev //changes done during 1.11.0-dev

View file

@ -60,7 +60,7 @@ bool synced_context::run(const std::string& commandname, const config& data, boo
assert(use_undo || (!resources::undo_stack->can_redo() && !resources::undo_stack->can_undo())); assert(use_undo || (!resources::undo_stack->can_redo() && !resources::undo_stack->can_undo()));
/* /*
use this after recorder.add_synced_command use this after resources::recorder->add_synced_command
because set_scontext_synced sets the checkup to the last added command because set_scontext_synced sets the checkup to the last added command
*/ */
set_scontext_synced sync; set_scontext_synced sync;
@ -88,12 +88,12 @@ bool synced_context::run(const std::string& commandname, const config& data, boo
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) 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()); assert(resources::recorder->at_end());
recorder.add_synced_command(commandname, data); resources::recorder->add_synced_command(commandname, data);
bool success = run(commandname, data, use_undo, show, error_handler); bool success = run(commandname, data, use_undo, show, error_handler);
if(!success) if(!success)
{ {
recorder.undo(); resources::recorder->undo();
} }
return success; return success;
} }
@ -296,7 +296,7 @@ config synced_context::ask_server_for_seed()
while(true) { while(true) {
do_replay_handle(); do_replay_handle();
bool is_replay_end = get_replay_source().at_end(); bool is_replay_end = resources::recorder->at_end();
if (is_replay_end && !is_mp_game) if (is_replay_end && !is_mp_game)
{ {
@ -305,7 +305,7 @@ config synced_context::ask_server_for_seed()
DBG_REPLAY << "MP synchronization: local server choice\n"; DBG_REPLAY << "MP synchronization: local server choice\n";
config cfg = config_of("new_seed", seed_rng::next_seed_str()); config cfg = config_of("new_seed", seed_rng::next_seed_str());
//-1 for "server" todo: change that. //-1 for "server" todo: change that.
recorder.user_input(name, cfg, -1); resources::recorder->user_input(name, cfg, -1);
return cfg; return cfg;
} }
@ -335,18 +335,18 @@ config synced_context::ask_server_for_seed()
be extracted from the replay. */ be extracted from the replay. */
DBG_REPLAY << "MP synchronization: replay server choice\n"; DBG_REPLAY << "MP synchronization: replay server choice\n";
do_replay_handle(); do_replay_handle();
const config *action = get_replay_source().get_next_action(); const config *action = resources::recorder->get_next_action();
if (!action) if (!action)
{ {
replay::process_error("[" + name + "] expected but none found\n"); replay::process_error("[" + name + "] expected but none found\n");
get_replay_source().revert_action(); resources::recorder->revert_action();
return config_of("new_seed", seed_rng::next_seed_str()); return config_of("new_seed", seed_rng::next_seed_str());
} }
if (!action->has_child(name)) if (!action->has_child(name))
{ {
replay::process_error("[" + name + "] expected but none found, found instead:\n " + action->debug() + "\n"); replay::process_error("[" + name + "] expected but none found, found instead:\n " + action->debug() + "\n");
get_replay_source().revert_action(); resources::recorder->revert_action();
return config_of("new_seed", seed_rng::next_seed_str()); return config_of("new_seed", seed_rng::next_seed_str());
} }
if((*action)["from_side"].str() != "server" || (*action)["side_invalid"].to_bool(false) ) if((*action)["from_side"].str() != "server" || (*action)["side_invalid"].to_bool(false) )
@ -401,7 +401,7 @@ checkup* set_scontext_synced::generate_checkup(const std::string& tagname)
} }
else else
{ {
return new synced_checkup(recorder.get_last_real_command().child_or_add(tagname)); return new synced_checkup(resources::recorder->get_last_real_command().child_or_add(tagname));
} }
} }

View file

@ -707,8 +707,6 @@ static int do_gameloop(const std::vector<std::string>& args)
return 0; return 0;
} }
recorder.clear();
//Start directly a campaign //Start directly a campaign
if(game->goto_campaign() == false){ if(game->goto_campaign() == false){
if (game->jump_to_campaign_id().empty()) if (game->jump_to_campaign_id().empty())