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_helper.cpp
replay_controller.cpp
replay_recorder_base.cpp
resources.cpp
save_blocker.cpp
save_index.cpp

View file

@ -529,6 +529,7 @@ wesnoth_sources = Split("""
replay.cpp
replay_helper.cpp
replay_controller.cpp
replay_recorder_base.cpp
resources.cpp
save_blocker.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.
*/
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;
size_t r = move_unit_internal(undo_stack, show_move, interrupted, mover);
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);
// Bookkeeping.
recorder.undo_cut(action->get_replay_data());
resources::recorder->undo_cut(action->get_replay_data());
redos_.push_back(action.release());
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
// action instead.
recorder.undo();
resources::recorder->undo();
undos.undo();
// 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);
// Shroud actions never get moved to the redo stack, so claim an error.
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
// action instead.
recorder.undo();
resources::recorder->undo();
undos.undo();
// 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);
// 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";
return false;
}
recorder.redo(replay_data);
resources::recorder->redo(replay_data);
replay_data.clear();
current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
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);
if ( msg.empty() ) {
recorder.redo(replay_data);
resources::recorder->redo(replay_data);
replay_data.clear();
set_scontext_synced sync;
recall_unit(id, current_team, loc, from, true, false);
@ -979,7 +979,7 @@ bool undo_list::recruit_action::redo(int side)
if ( msg.empty() ) {
//MP_COUNTDOWN: restore recruitment bonus
current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
recorder.redo(replay_data);
resources::recorder->redo(replay_data);
replay_data.clear();
set_scontext_synced sync;
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());
recorder.redo(replay_data);
resources::recorder->redo(replay_data);
replay_data.clear();
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
{
//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(),
resources::tod_manager->get_time_of_day()));
set_scontext_synced sync;

View file

@ -1368,8 +1368,8 @@ private:
const terrain_label *res;
res = gui->labels().set_label(location, text, team_name, color);
if (res)
recorder.add_label(res);
if (res && resources::recorder)
resources::recorder->add_label(res);
}
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["gold"] = _gold;
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()
{
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)
{
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) {
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;
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;
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","ai_id"+str_cast(side),ai::manager::get_active_ai_identifier_for_side(side));
resources::recorder->add_log_data("ai_log","faction"+str_cast(side),tm->name());
///@todo 1.9: add information about ai_config
}
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()
{
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()));
for (std::vector<team>::const_iterator tm = resources::teams->begin(); tm != resources::teams->end(); ++tm) {
int side = tm-resources::teams->begin()+1;
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_gold"+str_cast(side),str_cast(tm->gold()));
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;
res = gui->labels().set_label(location, text, team_name, color);
if (res)
recorder.add_label(res);
if (res && resources::recorder)
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.
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;
} else {

View file

@ -497,7 +497,7 @@ wml_action::wml_action(const std::string & tag, handler function)
/// @todo Finish experimenting.
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);
}

View file

@ -476,8 +476,6 @@ static void enter_wait_mode(game_display& disp, const config& game_config,
case mp::ui::PLAY:
play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_CLIENT,
preferences::skip_mp_replay() && observe, true, preferences::blindfold_replay() && observe);
recorder.clear();
break;
case mp::ui::QUIT:
default:
@ -520,8 +518,6 @@ static bool enter_connect_mode(game_display& disp, const config& game_config,
case mp::ui::PLAY:
play_game(disp, state, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false,
!local_players_only);
recorder.clear();
break;
case mp::ui::CREATE:
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 = "";
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;
for(unsigned int i = 0; i < repeat; i++){
saved_game state_copy(state);
play_game(disp, state_copy, game_config, game_config_manager::get()->terrain_types(), IO_SERVER, false, false);
}
recorder.clear();
}
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,
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.
// this call also might expand [scenario] in case thatt there is no replay_start
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);
recorder.clear();
gamestate.replay_data.clear();
return res;
} catch(game::load_game_failed& e) {
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,
bool network_game, bool blindfold_replay, bool is_unit_test)
{
recorder = replay(gamestate.replay_data);
recorder.set_to_end();
gamestate.get_replay().set_to_end();
gamestate.expand_scenario();
@ -333,7 +329,6 @@ LEVEL_RESULT play_game(game_display& disp, saved_game& gamestate,
}
gamestate.convert_to_start_save();
recorder.clear();
//If there is no next scenario we're done now.
if(gamestate.get_scenario_id().empty())

View file

@ -65,6 +65,7 @@
#include "preferences_display.hpp"
#include "replay.hpp"
#include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "save_index.hpp"
#include "scripting/game_lua_kernel.hpp"
@ -513,9 +514,9 @@ void menu_handler::show_chat_log()
{
config c;
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());
//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);
}
@ -935,7 +936,7 @@ void menu_handler::rename_unit()
const std::string label(N_("Name:"));
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);
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);
if (res)
recorder.add_label(res);
resources::recorder->add_label(res);
}
}
@ -1215,7 +1216,7 @@ void menu_handler::clear_labels()
&& !board().is_observer())
{
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,
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::tunnels = NULL;
resources::undo_stack = NULL;
resources::recorder = NULL;
resources::units = NULL;
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)))
, statistics_context_(new statistics::scenario_context(level["name"]))
, 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)
, player_number_(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::tod_manager = &gamestate_.tod_manager_;
resources::undo_stack = undo_stack_.get();
resources::recorder = replay_.get();
resources::units = &gamestate_.board_.units_;
resources::filter_con = &gamestate_;
@ -364,7 +367,7 @@ void play_controller::maybe_do_init_side()
return;
}
recorder.init_side();
resources::recorder->init_side();
do_init_side();
}

View file

@ -35,7 +35,7 @@ class game_data;
class team;
class unit;
class wmi_pager;
class replay;
class saved_game;
struct mp_game_settings;
class game_classification;
@ -273,6 +273,7 @@ protected:
/// undo_list can be an incomplete type at this point (which reduces the
/// number of files that depend on actions/undo.hpp).
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
//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 "playturn.hpp"
#include "preferences.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "sound.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_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";
@ -454,7 +455,7 @@ void playmp_controller::process_network_data()
}
turn_info::PROCESS_DATA_RESULT res = turn_info::PROCESS_CONTINUE;
config cfg;
if(!recorder.at_end()) {
if(!resources::recorder->at_end()) {
res = turn_info::replay_to_process_data_result(do_replay());
}
else if(network_reader_.read(cfg)) {

View file

@ -40,6 +40,7 @@
#include "playturn.hpp"
#include "random_new_deterministic.hpp"
#include "replay_helper.hpp"
#include "resources.hpp"
#include "savegame.hpp"
#include "sound.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)
, cursor_setter(cursor::NORMAL)
, textbox_info_()
, replay_sender_(recorder)
, replay_sender_(*resources::recorder)
, network_reader_()
, turn_data_(replay_sender_, network_reader_)
, end_turn_(END_TURN_NONE)
@ -207,13 +208,13 @@ void playsingle_controller::play_scenario_init() {
fire_preload();
assert(recorder.at_end());
assert(resources::recorder->at_end());
if(!loading_game_ )
{
assert(recorder.empty());
recorder.add_start();
recorder.get_next_action();
assert(resources::recorder->empty());
resources::recorder->add_start();
resources::recorder->get_next_action();
//we can only use a set_scontext_synced with a non empty recorder.
set_scontext_synced sync;
@ -839,7 +840,7 @@ void playsingle_controller::sync_end_turn()
assert(synced_context::synced_state() == synced_context::UNSYNCED);
if(end_turn_ == END_TURN_REQUIRED && current_team().is_local())
{
recorder.end_turn();
resources::recorder->end_turn();
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! */
//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());
return retv;
}
@ -132,7 +132,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
assert(network::nconnections() <= 1);
assert(cfg.all_children_count() == 1);
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;
}

View file

@ -38,6 +38,7 @@
#include "statistics.hpp"
#include "unit.hpp"
#include "whiteboard/manager.hpp"
#include "replay_recorder_base.hpp"
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
@ -142,11 +143,6 @@ static time_t get_time(const config &speak)
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)
: color_()
, nick_()
@ -183,22 +179,10 @@ chat_msg::~chat_msg()
{
}
replay::replay() :
cfg_(),
pos_(0),
message_locations()
replay::replay(replay_recorder_base& base)
: base_(&base)
, 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:
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)
{
config& ulog = cfg_.child_or_add("upload_log");
config& ulog = base_->get_upload_log();
ulog[key] = 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);
cat[key] = var;
}
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);
cat.add_child(key,c);
}
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)
@ -355,7 +339,7 @@ void replay::add_chat_log_entry(const config &cfg, std::back_insert_iterator<std
void replay::remove_command(int index)
{
cfg_.remove_child("command", index);
base_->remove_command(index);
std::vector<int>::reverse_iterator 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)
{
//we set pos_ = ncommands(), if we recorded something else in the meantime it doesn't make sense to redo an action.
assert(pos_ == ncommands());
assert(base_->get_pos() == ncommands());
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()
{
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);
const config &cc = c;
@ -445,8 +428,8 @@ config& replay::get_last_real_command()
void replay::undo_cut(config& dst)
{
assert(dst.empty());
//pos_ < ncommands() could mean that we try to undo commands that haven't been executed yet.
assert(pos_ == ncommands());
//assert that we are not undoing a command which we didn't execute yet.
assert(at_end());
std::vector<async_cmd> async_cmds;
// Remember commands not yet synced and skip over them.
// 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;
//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.
for(int cmd_2 = cmd + 1; cmd_2 < ncommands(); ++cmd_2)
{
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"))
{
map_location aloc(async_child, resources::gamedata);
if (src == aloc) remove_command(ac.num);
if (src == aloc) {
remove_command(ac.num);
}
}
}
}
}
remove_command(cmd);
pos_ = ncommands();
set_to_end();
}
void replay::undo()
@ -560,72 +545,74 @@ void replay::undo()
config &replay::command(int n)
{
config & retv = cfg_.child("command", n);
config & retv = base_->get_command_at(n);
assert(retv);
return retv;
}
int replay::ncommands() const
{
return cfg_.child_count("command");
return base_->size();
}
config& replay::add_command()
{
//pos_ != ncommands() means that there is a command on the replay which would be skipped.
assert(pos_ == ncommands());
pos_ = ncommands()+1;
return cfg_.add_child("command");
//if we werent at teh end of teh replay we sould skip one or mutiple commands.
assert(at_end());
config& retv = base_->add_child();
set_to_end();
return retv;
}
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;
++pos_;
base_->set_pos(base_->get_pos() + 1);
return r;
}
void replay::start_replay()
{
pos_ = 0;
base_->set_pos(0);
}
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()
{
if (pos_ >= ncommands())
if (at_end())
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_);
++pos_;
config* retv = &command(base_->get_pos());
base_->set_pos(base_->get_pos() + 1);
return retv;
}
bool replay::at_end() const
{
return pos_ >= ncommands();
assert(base_->get_pos() <= ncommands());
return base_->get_pos() == ncommands();
}
void replay::set_to_end()
{
pos_ = ncommands();
base_->set_to_end();
}
void replay::clear()
{
message_locations.clear();
message_log.clear();
cfg_ = config();
pos_ = 0;
//FIXME
}
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"))
{
config &cfg = cfg_.add_child("command", cmd);
config &cfg = base_->insert_command(base_->size());
cfg = cmd;
if(mark == MARK_AS_SENT) {
cfg["sent"] = true;
}
}
}
replay& get_replay_source()
{
return recorder;
}
bool replay::add_start_if_not_there_yet()
{
//this method would confuse the value of 'pos' otherwise
assert(pos_ == 0);
if(at_end() || !cfg_.child("command", pos_).has_child("start"))
assert(base_->get_pos() == 0);
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;
}
else
@ -695,7 +677,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
const int side_num = resources::controller->current_side();
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);
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"];
//if (!preferences::parse_should_show_lobby_join(speaker_name, message)) return;
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) {
int side = child["side"];
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)
{
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;
}
else
@ -790,7 +772,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
if(is_synced)
{
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;
}
else
@ -832,7 +814,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
assert(cfg->all_children_count() == 1);
std::string child_name = cfg->all_children_range().first->key;
DBG_REPLAY << "got an dependent action name = " << child_name <<"\n";
get_replay_source().revert_action();
resources::recorder->revert_action();
return REPLAY_FOUND_DEPENDENT;
}
else
@ -844,7 +826,7 @@ REPLAY_RETURN do_replay_handle(bool one_move)
if(is_synced)
{
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;
}
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 is_replay_end = get_replay_source().at_end();
bool is_replay_end = resources::recorder->at_end();
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";
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;
//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";
const config *action = get_replay_source().get_next_action();
assert(action); //action cannot be null because get_replay_source().at_end() returned false.
const config *action = resources::recorder->get_next_action();
assert(action); //action cannot be null because resources::recorder->at_end() returned false.
if( !action->has_child(name) || !(*action)["dependent"].to_bool())
{
replay::process_error("[" + name + "] expected but none found\n. found instead:\n" + action->debug());
//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.
BOOST_FOREACH(int side, sides)
{

View file

@ -26,6 +26,7 @@
#include <deque>
#include <map>
#include <set>
class replay_recorder_base;
class game_display;
class terrain_label;
class unit_map;
@ -50,11 +51,9 @@ private:
class replay
{
public:
replay();
explicit replay(const config& cfg);
void append(const config& cfg);
explicit replay(replay_recorder_base& base);
void add_start();
void add_countdown_update(int value,int team);
@ -93,8 +92,6 @@ public:
//ignored by the undo system.
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_last_turn(int num_turns=1);
const config& get_replay_data() const { return cfg_; }
void undo();
/*
@ -145,16 +142,10 @@ private:
* @return a reference to the added command
*/
config& add_nonundoable_command();
config cfg_;
int pos_;
replay_recorder_base* base_;
std::vector<int> message_locations;
};
replay& get_replay_source();
extern replay recorder;
enum REPLAY_RETURN
{
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() {
for (;;) {
play_slice();
if (recorder.at_end()) {
if (resources::recorder->at_end()) {
return;
} else {
if (!is_playing_) {
@ -271,7 +271,7 @@ void replay_controller::replay_ui_playback_should_stop()
if(!replay_ui_has_all_buttons())
return;
if(!recorder.at_end()) {
if(!resources::recorder->at_end()) {
play_button()->enable(true);
reset_button()->enable(true);
play_turn_button()->enable(true);
@ -317,7 +317,7 @@ void replay_controller::reset_replay()
it_is_a_new_turn_ = true;
skip_replay_ = false;
gamestate_.tod_manager_= tod_manager_start_;
recorder.start_replay();
resources::recorder->start_replay();
saved_game_ = saved_game_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,
@ -359,14 +359,14 @@ void replay_controller::reset_replay()
// Scenario initialization. (c.f. playsingle_controller::play_scenario())
fire_preload();
{ //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;
}
config* pstart = recorder.get_next_action();
config* pstart = resources::recorder->get_next_action();
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
*/
set_scontext_synced sync;
@ -476,7 +476,7 @@ void replay_controller::replay_skip_animation(){
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()
{
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();
}
}
@ -512,7 +512,7 @@ void replay_controller::play_turn()
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();
play_side();
play_slice();
@ -624,5 +624,5 @@ void replay_controller::handle_generic_event(const std::string& name)
}
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;
soundsource::manager *soundsources = NULL;
std::vector<team> *teams = NULL;
replay *recorder = NULL;
::tod_manager *tod_manager = NULL;
fake_unit_manager *fake_units = NULL;
pathfind::manager *tunnels = NULL;

View file

@ -33,6 +33,7 @@ class unit_map;
class persist_manager;
class game_classification;
struct mp_game_settings;
class replay;
namespace actions { class undo_list; }
namespace game_events { class manager; }
@ -59,6 +60,7 @@ namespace resources
extern const mp_game_settings *mp_settings;
extern soundsource::manager *soundsources;
extern std::vector<team> *teams;
extern replay *recorder;
extern fake_unit_manager *fake_units;
extern ::tod_manager *tod_manager;
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)
saved_game::saved_game()
: replay_data()
, has_carryover_expanded_(false)
: has_carryover_expanded_(false)
, carryover_(carryover_info().to_config())
, replay_start_()
, classification_()
, mp_settings_()
, starting_pos_type_(STARTINGPOS_NONE)
, starting_pos_()
, replay_data_()
{
}
saved_game::saved_game(const config& cfg)
: replay_data()
, has_carryover_expanded_(false)
: has_carryover_expanded_(false)
, carryover_()
, replay_start_()
, classification_(cfg)
, mp_settings_(cfg)
, starting_pos_type_(STARTINGPOS_NONE)
, starting_pos_()
, replay_data_()
{
log_scope("read_game");
@ -94,12 +94,11 @@ saved_game::saved_game(const config& cfg)
//Serversided replays can contain multiple [replay]
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"))
{
replay_data.append_children(replay);
replay_data_.append_config(replay);
}
replay_data_.set_to_end();
if(const config& snapshot = cfg.child("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)
: replay_data(state.replay_data)
, has_carryover_expanded_(state.has_carryover_expanded_)
: has_carryover_expanded_(state.has_carryover_expanded_)
, carryover_(state.carryover_)
, replay_start_(state.replay_start_)
, classification_(state.classification_)
, mp_settings_(state.mp_settings_)
, starting_pos_type_(state.starting_pos_type_)
, 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_);
}
if(!this->replay_data.empty())
{
out.write_child("replay", replay_data);
}
out.open_child("replay");
replay_data_.write(out);
out.close_child("replay");
write_carryover(out);
}
@ -445,7 +443,7 @@ void saved_game::convert_to_start_save()
sides.rng().rotate_random();
carryover_ = sides.to_config();
has_carryover_expanded_ = false;
replay_data = config();
replay_data_ = replay_recorder_base();
replay_start_ = config();
remove_snapshot();
}
@ -458,10 +456,8 @@ config saved_game::to_config() const
{
r.add_child("replay_start", replay_start_);
}
if(!this->replay_data.empty())
{
r.add_child("replay", replay_data);
}
replay_data_.write(r.add_child("replay"));
if(starting_pos_type_ == STARTINGPOS_SNAPSHOT)
{
r.add_child("snapshot", starting_pos_);

View file

@ -5,6 +5,8 @@
#include "config.hpp"
#include "game_classification.hpp"
#include "mp_game_settings.hpp"
#include "replay_recorder_base.hpp"
class config_writer;
@ -94,13 +96,9 @@ public:
/* removes network_ai and network controller types*/
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();
replay_recorder_base& get_replay() { return replay_data_; }
const replay_recorder_base& get_replay() const { return replay_data_; }
private:
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
*/
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()
{
gamestate_.replay_data = recorder.get_replay_data();
}
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);
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);
out.write_child("snapshot",gamestate().get_starting_pos());
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

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()));
/*
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
*/
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)
{
assert(recorder.at_end());
recorder.add_synced_command(commandname, data);
assert(resources::recorder->at_end());
resources::recorder->add_synced_command(commandname, data);
bool success = run(commandname, data, use_undo, show, error_handler);
if(!success)
{
recorder.undo();
resources::recorder->undo();
}
return success;
}
@ -296,7 +296,7 @@ config synced_context::ask_server_for_seed()
while(true) {
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)
{
@ -305,7 +305,7 @@ config synced_context::ask_server_for_seed()
DBG_REPLAY << "MP synchronization: local server choice\n";
config cfg = config_of("new_seed", seed_rng::next_seed_str());
//-1 for "server" todo: change that.
recorder.user_input(name, cfg, -1);
resources::recorder->user_input(name, cfg, -1);
return cfg;
}
@ -335,18 +335,18 @@ config synced_context::ask_server_for_seed()
be extracted from the replay. */
DBG_REPLAY << "MP synchronization: replay server choice\n";
do_replay_handle();
const config *action = get_replay_source().get_next_action();
const config *action = resources::recorder->get_next_action();
if (!action)
{
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());
}
if (!action->has_child(name))
{
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());
}
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
{
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;
}
recorder.clear();
//Start directly a campaign
if(game->goto_campaign() == false){
if (game->jump_to_campaign_id().empty())