00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "global.hpp"
00024
00025 #include "dialogs.hpp"
00026 #include "foreach.hpp"
00027 #include "game_display.hpp"
00028 #include "game_end_exceptions.hpp"
00029 #include "game_events.hpp"
00030 #include "game_preferences.hpp"
00031 #include "gamestatus.hpp"
00032 #include "log.hpp"
00033 #include "map_label.hpp"
00034 #include "map_location.hpp"
00035 #include "play_controller.hpp"
00036 #include "replay.hpp"
00037 #include "resources.hpp"
00038 #include "rng.hpp"
00039 #include "statistics.hpp"
00040 #include "whiteboard/manager.hpp"
00041
00042 static lg::log_domain log_replay("replay");
00043 #define DBG_REPLAY LOG_STREAM(debug, log_replay)
00044 #define LOG_REPLAY LOG_STREAM(info, log_replay)
00045 #define WRN_REPLAY LOG_STREAM(warn, log_replay)
00046 #define ERR_REPLAY LOG_STREAM(err, log_replay)
00047
00048 static lg::log_domain log_random("random");
00049 #define DBG_RND LOG_STREAM(debug, log_random)
00050 #define LOG_RND LOG_STREAM(info, log_random)
00051 #define WRN_RND LOG_STREAM(warn, log_random)
00052 #define ERR_RND LOG_STREAM(err, log_random)
00053
00054
00055
00056
00057 static void verify(const unit_map& units, const config& cfg) {
00058 std::stringstream errbuf;
00059 LOG_REPLAY << "verifying unit structure...\n";
00060
00061 const size_t nunits = lexical_cast_default<size_t>(cfg["num_units"]);
00062 if(nunits != units.size()) {
00063 errbuf << "SYNC VERIFICATION FAILED: number of units from data source differ: "
00064 << nunits << " according to data source. " << units.size() << " locally\n";
00065
00066 std::set<map_location> locs;
00067 foreach (const config &u, cfg.child_range("unit"))
00068 {
00069 const map_location loc(u, resources::state_of_game);
00070 locs.insert(loc);
00071
00072 if(units.count(loc) == 0) {
00073 errbuf << "data source says there is a unit at "
00074 << loc << " but none found locally\n";
00075 }
00076 }
00077
00078 for(unit_map::const_iterator j = units.begin(); j != units.end(); ++j) {
00079 if (locs.count(j->get_location()) == 0) {
00080 errbuf << "local unit at " << j->get_location()
00081 << " but none in data source\n";
00082 }
00083 }
00084 replay::process_error(errbuf.str());
00085 errbuf.clear();
00086 }
00087
00088 foreach (const config &un, cfg.child_range("unit"))
00089 {
00090 const map_location loc(un, resources::state_of_game);
00091 const unit_map::const_iterator u = units.find(loc);
00092 if(u == units.end()) {
00093 errbuf << "SYNC VERIFICATION FAILED: data source says there is a '"
00094 << un["type"] << "' (side " << un["side"] << ") at "
00095 << loc << " but there is no local record of it\n";
00096 replay::process_error(errbuf.str());
00097 errbuf.clear();
00098 }
00099
00100 config cfg;
00101 u->write(cfg);
00102
00103 bool is_ok = true;
00104 static const std::string fields[] = {"type","hitpoints","experience","side",""};
00105 for(const std::string* str = fields; str->empty() == false; ++str) {
00106 if (cfg[*str] != un[*str]) {
00107 errbuf << "ERROR IN FIELD '" << *str << "' for unit at "
00108 << loc << " data source: '" << un[*str]
00109 << "' local: '" << cfg[*str] << "'\n";
00110 is_ok = false;
00111 }
00112 }
00113
00114 if(!is_ok) {
00115 errbuf << "(SYNC VERIFICATION FAILED)\n";
00116 replay::process_error(errbuf.str());
00117 errbuf.clear();
00118 }
00119 }
00120
00121 LOG_REPLAY << "verification passed\n";
00122 }
00123
00124
00125
00126
00127 replay recorder;
00128
00129 chat_msg::chat_msg(const config &cfg)
00130 : color_()
00131 , nick_()
00132 , text_(cfg["message"].str())
00133 {
00134 const std::string& team_name = cfg["team_name"];
00135 if(team_name == "")
00136 {
00137 nick_ = cfg["id"].str();
00138 } else {
00139 nick_ = str_cast("*")+cfg["id"].str()+"*";
00140 }
00141 int side = lexical_cast_default<int>(cfg["side"],0);
00142 LOG_REPLAY << "side in message: " << side << std::endl;
00143 if (side==0) {
00144 color_ = "white";
00145 } else {
00146 color_ = team::get_side_highlight_pango(side-1);
00147 }
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158 }
00159
00160 chat_msg::~chat_msg()
00161 {
00162 }
00163
00164 replay::replay() :
00165 cfg_(),
00166 pos_(0),
00167 current_(NULL),
00168 skip_(false),
00169 message_locations(),
00170 expected_advancements_()
00171 {}
00172
00173 replay::replay(const config& cfg) :
00174 cfg_(cfg),
00175 pos_(0),
00176 current_(NULL),
00177 skip_(false),
00178 message_locations(),
00179 expected_advancements_()
00180 {}
00181
00182 void replay::append(const config& cfg)
00183 {
00184 cfg_.append(cfg);
00185 }
00186
00187 void replay::process_error(const std::string& msg)
00188 {
00189 ERR_REPLAY << msg;
00190
00191 resources::controller->process_oos(msg);
00192 }
00193
00194 void replay::set_skip(bool skip)
00195 {
00196 skip_ = skip;
00197 }
00198
00199 bool replay::is_skipping() const
00200 {
00201 return skip_;
00202 }
00203
00204 void replay::add_unit_checksum(const map_location& loc,config* const cfg)
00205 {
00206 if(! game_config::mp_debug) {
00207 return;
00208 }
00209 config& cc = cfg->add_child("checksum");
00210 loc.write(cc);
00211 unit_map::const_iterator u = resources::units->find(loc);
00212 assert(u.valid());
00213 cc["value"] = get_checksum(*u);
00214 }
00215
00216 void replay::add_start()
00217 {
00218 config* const cmd = add_command(true);
00219 cmd->add_child("start");
00220 }
00221
00222 void replay::add_recruit(int value, const map_location& loc, const map_location& from)
00223 {
00224 config* const cmd = add_command();
00225
00226 config val;
00227 val["value"] = value;
00228 loc.write(val);
00229 config& leader_position = val.add_child("from");
00230 from.write(leader_position);
00231
00232 cmd->add_child("recruit",val);
00233 }
00234
00235 void replay::add_recall(const std::string& unit_id, const map_location& loc, const map_location& from)
00236 {
00237 config* const cmd = add_command();
00238
00239 config val;
00240 val["value"] = unit_id;
00241 loc.write(val);
00242 config& leader_position = val.add_child("from");
00243 from.write(leader_position);
00244
00245 cmd->add_child("recall",val);
00246 }
00247
00248 void replay::add_disband(const std::string& unit_id)
00249 {
00250 config* const cmd = add_command();
00251
00252 config val;
00253
00254 val["value"] = unit_id;
00255
00256 cmd->add_child("disband",val);
00257 }
00258
00259 void replay::add_countdown_update(int value, int team)
00260 {
00261 config* const cmd = add_command();
00262 config val;
00263 val["value"] = value;
00264 val["team"] = team;
00265 cmd->add_child("countdown_update",val);
00266 }
00267
00268
00269 void replay::add_movement(const std::vector<map_location>& steps)
00270 {
00271 if(steps.empty()) {
00272 return;
00273 }
00274
00275 config* const cmd = add_command();
00276
00277 config move;
00278 write_locations(steps, move);
00279
00280 cmd->add_child("move",move);
00281 }
00282
00283 void replay::add_attack(const map_location& a, const map_location& b,
00284 int att_weapon, int def_weapon, const std::string& attacker_type_id,
00285 const std::string& defender_type_id, int attacker_lvl,
00286 int defender_lvl, const size_t turn, const time_of_day &t)
00287 {
00288 add_pos("attack",a,b);
00289 config &cfg = current_->child("attack");
00290
00291 cfg["weapon"] = att_weapon;
00292 cfg["defender_weapon"] = def_weapon;
00293 cfg["attacker_type"] = attacker_type_id;
00294 cfg["defender_type"] = defender_type_id;
00295 cfg["attacker_lvl"] = attacker_lvl;
00296 cfg["defender_lvl"] = defender_lvl;
00297 cfg["turn"] = int(turn);
00298 cfg["tod"] = t.id;
00299 add_unit_checksum(a,current_);
00300 add_unit_checksum(b,current_);
00301 }
00302
00303 void replay::add_seed(const char* child_name, int seed)
00304 {
00305 LOG_REPLAY << "Setting seed for child type " << child_name << ": " << seed << "\n";
00306 random()->child(child_name)["seed"] = seed;
00307 }
00308
00309 void replay::add_pos(const std::string& type,
00310 const map_location& a, const map_location& b)
00311 {
00312 config* const cmd = add_command();
00313
00314 config move, src, dst;
00315 a.write(src);
00316 b.write(dst);
00317
00318 move.add_child("source",src);
00319 move.add_child("destination",dst);
00320 cmd->add_child(type,move);
00321 }
00322
00323 void replay::user_input(const std::string &name, const config &input)
00324 {
00325 add_command()->add_child(name, input);
00326 }
00327
00328 void replay::add_label(const terrain_label* label)
00329 {
00330 assert(label);
00331 config* const cmd = add_command(false);
00332
00333 (*cmd)["undo"] = false;
00334
00335 config val;
00336
00337 label->write(val);
00338
00339 cmd->add_child("label",val);
00340 }
00341
00342 void replay::clear_labels(const std::string& team_name, bool force)
00343 {
00344 config* const cmd = add_command(false);
00345
00346 (*cmd)["undo"] = false;
00347 config val;
00348 val["team_name"] = team_name;
00349 val["force"] = force;
00350 cmd->add_child("clear_labels",val);
00351 }
00352
00353 void replay::add_rename(const std::string& name, const map_location& loc)
00354 {
00355 config* const cmd = add_command(false);
00356 (*cmd)["async"] = true;
00357 config val;
00358 loc.write(val);
00359 val["name"] = name;
00360 cmd->add_child("rename", val);
00361 }
00362
00363 void replay::init_side()
00364 {
00365 config* const cmd = add_command();
00366 config init_side;
00367 if(!lg::debug.dont_log("network")) init_side["side_number"] = resources::controller->current_side();
00368 cmd->add_child("init_side", init_side);
00369 }
00370
00371 void replay::end_turn()
00372 {
00373 config* const cmd = add_command();
00374 cmd->add_child("end_turn");
00375 }
00376
00377 void replay::add_event(const std::string& name, const map_location& loc)
00378 {
00379 config* const cmd = add_command();
00380 config& ev = cmd->add_child("fire_event");
00381 ev["raise"] = name;
00382 if(loc.valid()) {
00383 config& source = ev.add_child("source");
00384 loc.write(source);
00385 }
00386 (*cmd)["undo"] = false;
00387 }
00388
00389 void replay::add_log_data(const std::string &key, const std::string &var)
00390 {
00391 config& ulog = cfg_.child_or_add("upload_log");
00392 ulog[key] = var;
00393 }
00394
00395 void replay::add_log_data(const std::string &category, const std::string &key, const std::string &var)
00396 {
00397 config& ulog = cfg_.child_or_add("upload_log");
00398 config& cat = ulog.child_or_add(category);
00399 cat[key] = var;
00400 }
00401
00402 void replay::add_log_data(const std::string &category, const std::string &key, const config &c)
00403 {
00404 config& ulog = cfg_.child_or_add("upload_log");
00405 config& cat = ulog.child_or_add(category);
00406 cat.add_child(key,c);
00407 }
00408
00409 void replay::add_checksum_check(const map_location& loc)
00410 {
00411 if(! game_config::mp_debug || ! (resources::units->find(loc).valid()) ) {
00412 return;
00413 }
00414 config* const cmd = add_command();
00415 add_unit_checksum(loc,cmd);
00416 }
00417
00418 void replay::add_expected_advancement(const map_location& loc)
00419 {
00420 expected_advancements_.push_back(loc);
00421 }
00422
00423 const std::deque<map_location>& replay::expected_advancements() const
00424 {
00425 return expected_advancements_;
00426 }
00427
00428 void replay::pop_expected_advancement()
00429 {
00430 expected_advancements_.pop_front();
00431 }
00432
00433 void replay::add_advancement(const map_location& loc)
00434 {
00435 config* const cmd = add_command(false);
00436
00437 config val;
00438 (*cmd)["undo"] = false;
00439 loc.write(val);
00440 cmd->add_child("advance_unit",val);
00441 DBG_REPLAY << "added an explicit advance\n";
00442 }
00443
00444 void replay::add_chat_message_location()
00445 {
00446 message_locations.push_back(pos_-1);
00447 }
00448
00449 void replay::speak(const config& cfg)
00450 {
00451 config* const cmd = add_command(false);
00452 if(cmd != NULL) {
00453 cmd->add_child("speak",cfg);
00454 (*cmd)["undo"] = false;
00455 add_chat_message_location();
00456 }
00457 }
00458
00459 void replay::add_chat_log_entry(const config &cfg, std::back_insert_iterator<std::vector<chat_msg> > &i) const
00460 {
00461 if (!cfg) return;
00462
00463 if (!preferences::parse_should_show_lobby_join(cfg["id"], cfg["message"])) return;
00464 if (preferences::is_ignored(cfg["id"])) return;
00465 *i = chat_msg(cfg);
00466 }
00467
00468 void replay::remove_command(int index)
00469 {
00470 cfg_.remove_child("command", index);
00471 std::vector<int>::reverse_iterator loc_it;
00472 for (loc_it = message_locations.rbegin(); loc_it != message_locations.rend() && index < *loc_it;++loc_it)
00473 {
00474 --(*loc_it);
00475 }
00476 }
00477
00478
00479 std::vector< chat_msg > message_log;
00480
00481
00482 const std::vector<chat_msg>& replay::build_chat_log()
00483 {
00484 std::vector<int>::iterator loc_it;
00485 int last_location = 0;
00486 std::back_insert_iterator<std::vector < chat_msg > > chat_log_appender( back_inserter(message_log));
00487 for (loc_it = message_locations.begin(); loc_it != message_locations.end(); ++loc_it)
00488 {
00489 last_location = *loc_it;
00490 const config &speak = command(last_location).child("speak");
00491 add_chat_log_entry(speak, chat_log_appender);
00492
00493 }
00494 message_locations.clear();
00495 return message_log;
00496 }
00497
00498 config replay::get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type)
00499 {
00500 config res;
00501
00502 for (int cmd = cmd_start; cmd < cmd_end; ++cmd)
00503 {
00504 config &c = command(cmd);
00505 if ((data_type == ALL_DATA || c["undo"] == "no") && c["sent"] != "yes")
00506 {
00507 res.add_child("command", c);
00508 if (data_type == NON_UNDO_DATA) c["sent"] = true;
00509 }
00510 }
00511
00512 return res;
00513 }
00514
00515 struct async_cmd
00516 {
00517 config *cfg;
00518 int num;
00519 };
00520
00521 void replay::undo()
00522 {
00523 std::vector<async_cmd> async_cmds;
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533 int cmd;
00534 for (cmd = ncommands() - 1; cmd >= 0; --cmd)
00535 {
00536 config &c = command(cmd);
00537 if (c["undo"] != "no" && c["async"] != "yes" && c["sent"] != "yes") break;
00538 if (c["async"] == "yes") {
00539 async_cmd ac = { &c, cmd };
00540 async_cmds.push_back(ac);
00541 }
00542 }
00543
00544 if (cmd < 0) return;
00545 config &c = command(cmd);
00546
00547 if (const config &child = c.child("move"))
00548 {
00549
00550
00551 const std::vector<map_location> steps =
00552 parse_location_range(child["x"], child["y"]);
00553
00554 if (steps.empty()) {
00555 ERR_REPLAY << "trying to undo a move using an empty path";
00556 return;
00557 }
00558
00559 const map_location &src = steps.front();
00560 const map_location &dst = steps.back();
00561
00562 foreach (const async_cmd &ac, async_cmds)
00563 {
00564 if (config &async_child = ac.cfg->child("rename")) {
00565 map_location aloc(async_child, resources::state_of_game);
00566 if (dst == aloc) src.write(async_child);
00567 }
00568 }
00569 }
00570 else
00571 {
00572 const config *chld = &c.child("recruit");
00573 if (!*chld) chld = &c.child("recall");
00574 if (*chld) {
00575
00576
00577 map_location src(*chld, resources::state_of_game);
00578 foreach (const async_cmd &ac, async_cmds)
00579 {
00580 if (config &async_child = ac.cfg->child("rename"))
00581 {
00582 map_location aloc(async_child, resources::state_of_game);
00583 if (src == aloc) remove_command(ac.num);
00584 }
00585 }
00586 }
00587 }
00588
00589 remove_command(cmd);
00590 current_ = NULL;
00591 set_random(NULL);
00592 }
00593
00594 config &replay::command(int n)
00595 {
00596 return cfg_.child("command", n);
00597 }
00598
00599 int replay::ncommands() const
00600 {
00601 return cfg_.child_count("command");
00602 }
00603
00604 config* replay::add_command(bool update_random_context)
00605 {
00606 pos_ = ncommands()+1;
00607 current_ = &cfg_.add_child("command");
00608 if(update_random_context)
00609 set_random(current_);
00610
00611 return current_;
00612 }
00613
00614 void replay::start_replay()
00615 {
00616 pos_ = 0;
00617 }
00618
00619 void replay::revert_action()
00620 {
00621 if (pos_ > 0)
00622 --pos_;
00623 }
00624
00625 config* replay::get_next_action()
00626 {
00627 if (pos_ >= ncommands())
00628 return NULL;
00629
00630 LOG_REPLAY << "up to replay action " << pos_ + 1 << '/' << ncommands() << '\n';
00631
00632 current_ = &command(pos_);
00633 set_random(current_);
00634 ++pos_;
00635 return current_;
00636 }
00637
00638 void replay::pre_replay()
00639 {
00640 if (rng::random() == NULL && ncommands() > 0) {
00641 if (at_end())
00642 {
00643 add_command(true);
00644 }
00645 else
00646 {
00647 set_random(&command(pos_));
00648 }
00649 }
00650 }
00651
00652 bool replay::at_end() const
00653 {
00654 return pos_ >= ncommands();
00655 }
00656
00657 void replay::set_to_end()
00658 {
00659 pos_ = ncommands();
00660 current_ = NULL;
00661 set_random(NULL);
00662 }
00663
00664 void replay::clear()
00665 {
00666 message_locations.clear();
00667 message_log.clear();
00668 cfg_ = config();
00669 pos_ = 0;
00670 current_ = NULL;
00671 set_random(NULL);
00672 skip_ = 0;
00673 }
00674
00675 bool replay::empty()
00676 {
00677 return ncommands() == 0;
00678 }
00679
00680 void replay::add_config(const config& cfg, MARK_SENT mark)
00681 {
00682 foreach (const config &cmd, cfg.child_range("command"))
00683 {
00684 config &cfg = cfg_.add_child("command", cmd);
00685 if (cfg.child("speak"))
00686 {
00687 pos_ = ncommands();
00688 add_chat_message_location();
00689 }
00690 if(mark == MARK_AS_SENT) {
00691 cfg["sent"] = true;
00692 }
00693 }
00694 }
00695
00696 namespace {
00697
00698 replay* replay_src = NULL;
00699
00700 struct replay_source_manager
00701 {
00702 replay_source_manager(replay* o) : old_(replay_src)
00703 {
00704 replay_src = o;
00705 }
00706
00707 ~replay_source_manager()
00708 {
00709 replay_src = old_;
00710 }
00711
00712 private:
00713 replay* const old_;
00714 };
00715
00716 }
00717
00718 replay& get_replay_source()
00719 {
00720 if(replay_src != NULL) {
00721 return *replay_src;
00722 } else {
00723 return recorder;
00724 }
00725 }
00726
00727 static void check_checksums(const config &cfg)
00728 {
00729 if(! game_config::mp_debug) {
00730 return;
00731 }
00732 foreach (const config &ch, cfg.child_range("checksum"))
00733 {
00734 map_location loc(ch, resources::state_of_game);
00735 unit_map::const_iterator u = resources::units->find(loc);
00736 if (!u.valid()) {
00737 std::stringstream message;
00738 message << "non existent unit to checksum at " << loc.x+1 << "," << loc.y+1 << "!";
00739 resources::screen->add_chat_message(time(NULL), "verification", 1, message.str(),
00740 events::chat_handler::MESSAGE_PRIVATE, false);
00741 continue;
00742 }
00743 if (get_checksum(*u) != ch["value"]) {
00744 std::stringstream message;
00745 message << "checksum mismatch at " << loc.x+1 << "," << loc.y+1 << "!";
00746 resources::screen->add_chat_message(time(NULL), "verification", 1, message.str(),
00747 events::chat_handler::MESSAGE_PRIVATE, false);
00748 }
00749 }
00750 }
00751
00752 bool do_replay(int side_num, replay *obj)
00753 {
00754 log_scope("do replay");
00755
00756 const replay_source_manager replaymanager(obj);
00757
00758
00759
00760 if (!get_replay_source().is_skipping()){
00761 resources::screen->recalculate_minimap();
00762 }
00763
00764 const rand_rng::set_random_generator generator_setter(&get_replay_source());
00765
00766 update_locker lock_update(resources::screen->video(),get_replay_source().is_skipping());
00767 return do_replay_handle(side_num, "");
00768 }
00769
00770 bool do_replay_handle(int side_num, const std::string &do_untill)
00771 {
00772
00773 std::deque<map_location> advancing_units;
00774
00775 team ¤t_team = (*resources::teams)[side_num - 1];
00776
00777
00778 for(;;) {
00779 const config *cfg = get_replay_source().get_next_action();
00780
00781
00782
00783 bool fix_shroud = false;
00784 if (cfg)
00785 {
00786 DBG_REPLAY << "Replay data:\n" << *cfg << "\n";
00787 }
00788 else
00789 {
00790 DBG_REPLAY << "Replay data at end\n";
00791 }
00792
00793
00794 if(cfg == NULL) {
00795
00796 return false;
00797 }
00798
00799
00800 if (!get_replay_source().expected_advancements().empty()) {
00801
00802
00803
00804 if (const config &child = cfg->child("choose")) {
00805 int val = child["value"];
00806 map_location loc = get_replay_source().expected_advancements().front();
00807 dialogs::animate_unit_advancement(loc, val);
00808 get_replay_source().pop_expected_advancement();
00809
00810 DBG_REPLAY << "advanced unit " << val << " at " << loc << '\n';
00811
00812
00813
00814 if(advancing_units.empty()) {
00815 resources::controller->check_victory();
00816 }
00817
00818 if (do_untill == "choose") {
00819 get_replay_source().revert_action();
00820 return false;
00821 }
00822
00823 continue;
00824 }
00825 }
00826
00827
00828 if (!do_untill.empty() && cfg->child(do_untill))
00829 {
00830 get_replay_source().revert_action();
00831 return false;
00832 }
00833
00834 config::all_children_itors ch_itors = cfg->all_children_range();
00835
00836 if (ch_itors.first == ch_itors.second || cfg->child("start"))
00837 {
00838
00839 }
00840 else if (const config &child = cfg->child("speak"))
00841 {
00842 const std::string &team_name = child["team_name"];
00843 const std::string &speaker_name = child["id"];
00844 const std::string &message = child["message"];
00845
00846 bool is_whisper = (speaker_name.find("whisper: ") == 0);
00847 get_replay_source().add_chat_message_location();
00848 if (!get_replay_source().is_skipping() || is_whisper) {
00849 int side = child["side"];
00850 resources::screen->add_chat_message(time(NULL), speaker_name, side, message,
00851 (team_name.empty() ? events::chat_handler::MESSAGE_PUBLIC
00852 : events::chat_handler::MESSAGE_PRIVATE),
00853 preferences::message_bell());
00854 }
00855 }
00856 else if (const config &child = cfg->child("label"))
00857 {
00858 terrain_label label(resources::screen->labels(), child);
00859
00860 resources::screen->labels().set_label(label.location(),
00861 label.text(),
00862 label.team_name(),
00863 label.color());
00864 }
00865 else if (const config &child = cfg->child("clear_labels"))
00866 {
00867 resources::screen->labels().clear(std::string(child["team_name"]), child["force"].to_bool());
00868 }
00869 else if (const config &child = cfg->child("rename"))
00870 {
00871 const map_location loc(child, resources::state_of_game);
00872 const std::string &name = child["name"];
00873
00874 unit_map::iterator u = resources::units->find(loc);
00875 if (u.valid()) {
00876 if (u->unrenamable()) {
00877 std::stringstream errbuf;
00878 errbuf << "renaming unrenamable unit " << u->id() << '\n';
00879 replay::process_error(errbuf.str());
00880 continue;
00881 }
00882 u->rename(name);
00883 } else {
00884
00885
00886
00887
00888 WRN_REPLAY << "attempt to rename unit at location: "
00889 << loc << ", where none exists (anymore).\n";
00890 }
00891 }
00892
00893 else if (cfg->child("init_side"))
00894 {
00895 resources::controller->do_init_side(side_num - 1, true);
00896 }
00897
00898
00899 else if (cfg->child("end_turn"))
00900 {
00901 if (const config &child = cfg->child("verify")) {
00902 verify(*resources::units, child);
00903 }
00904
00905 return true;
00906 }
00907
00908 else if (const config &child = cfg->child("recruit"))
00909 {
00910 int val = child["value"];
00911
00912 map_location loc(child, resources::state_of_game);
00913 map_location from(child.child_or_empty("from"), resources::state_of_game);
00914
00915 unit_map::unit_iterator u = resources::units->find(from);
00916
00917 std::set<std::string> recruits = current_team.recruits();
00918
00919
00920
00921
00922 if (u != resources::units->end())
00923 recruits.insert((u->recruits()).begin(), (u->recruits()).end());
00924
00925 if(val < 0 || static_cast<size_t>(val) >= recruits.size()) {
00926 std::stringstream errbuf;
00927 errbuf << "recruitment index is illegal: " << val
00928 << " while this side only has " << recruits.size()
00929 << " units available for recruitment\n";
00930 replay::process_error(errbuf.str());
00931 continue;
00932 }
00933
00934 std::set<std::string>::const_iterator itor = recruits.begin();
00935 std::advance(itor,val);
00936 const unit_type *u_type = unit_types.find(*itor);
00937 if (!u_type) {
00938 std::stringstream errbuf;
00939 errbuf << "recruiting illegal unit: '" << *itor << "'\n";
00940 replay::process_error(errbuf.str());
00941 continue;
00942 }
00943
00944 const std::string res = find_recruit_location(side_num, loc, from, u_type->id());
00945 const unit new_unit(u_type, side_num, true);
00946 if (res.empty()) {
00947 place_recruit(new_unit, loc, from, false, !get_replay_source().is_skipping());
00948 } else {
00949 std::stringstream errbuf;
00950 errbuf << "cannot recruit unit: " << res << "\n";
00951 replay::process_error(errbuf.str());
00952 }
00953
00954 if (u_type->cost() > current_team.gold()) {
00955 std::stringstream errbuf;
00956 errbuf << "unit '" << u_type->id() << "' is too expensive to recruit: "
00957 << u_type->cost() << "/" << current_team.gold() << "\n";
00958 replay::process_error(errbuf.str());
00959 }
00960 LOG_REPLAY << "recruit: team=" << side_num << " '" << u_type->id() << "' at (" << loc
00961 << ") cost=" << u_type->cost() << " from gold=" << current_team.gold() << ' ';
00962
00963
00964 statistics::recruit_unit(new_unit);
00965
00966 current_team.spend_gold(u_type->cost());
00967 LOG_REPLAY << "-> " << (current_team.gold()) << "\n";
00968 fix_shroud = true;
00969 check_checksums(*cfg);
00970 }
00971
00972 else if (const config &child = cfg->child("recall"))
00973 {
00974 const std::string& unit_id = child["value"];
00975 map_location loc(child, resources::state_of_game);
00976 map_location from(child.child_or_empty("from"), resources::state_of_game);
00977
00978 std::vector<unit>::iterator recall_unit =
00979 find_if_matches_id(current_team.recall_list(), unit_id);
00980
00981 if (recall_unit != current_team.recall_list().end()) {
00982 statistics::recall_unit(*recall_unit);
00983 place_recruit(*recall_unit, loc, from, true, !get_replay_source().is_skipping());
00984 current_team.recall_list().erase(recall_unit);
00985 current_team.spend_gold(current_team.recall_cost());
00986 fix_shroud = true;
00987 } else {
00988 replay::process_error("illegal recall: unit_id '" + unit_id + "' could not be found within the recall list.\n");
00989 }
00990 check_checksums(*cfg);
00991 }
00992
00993 else if (const config &child = cfg->child("disband"))
00994 {
00995 const std::string& unit_id = child["value"];
00996 std::vector<unit>::iterator disband_unit =
00997 find_if_matches_id(current_team.recall_list(), unit_id);
00998
00999 if(disband_unit != current_team.recall_list().end()) {
01000 current_team.recall_list().erase(disband_unit);
01001 } else {
01002 replay::process_error("illegal disband\n");
01003 }
01004 }
01005 else if (const config &child = cfg->child("countdown_update"))
01006 {
01007 int val = child["value"];
01008 int tval = child["team"];
01009 if (tval <= 0 || tval > int(resources::teams->size())) {
01010 std::stringstream errbuf;
01011 errbuf << "Illegal countdown update \n"
01012 << "Received update for :" << tval << " Current user :"
01013 << side_num << "\n" << " Updated value :" << val;
01014
01015 replay::process_error(errbuf.str());
01016 } else {
01017 (*resources::teams)[tval - 1].set_countdown_time(val);
01018 }
01019 }
01020 else if (const config &child = cfg->child("move"))
01021 {
01022 const std::string& x = child["x"];
01023 const std::string& y = child["y"];
01024 std::vector<map_location> steps = parse_location_range(x,y);
01025
01026 if(steps.empty()) {
01027 WRN_REPLAY << "Warning: Missing path data found in [move]\n";
01028 continue;
01029 }
01030
01031 map_location src = steps.front();
01032 map_location dst = steps.back();
01033
01034 if (src == dst) {
01035 WRN_REPLAY << "Warning: Move with identical source and destination. Skipping...";
01036 continue;
01037 }
01038
01039 unit_map::iterator u = resources::units->find(dst);
01040 if (u.valid()) {
01041 std::stringstream errbuf;
01042 errbuf << "destination already occupied: "
01043 << dst << '\n';
01044 replay::process_error(errbuf.str());
01045 continue;
01046 }
01047 u = resources::units->find(src);
01048 if (!u.valid()) {
01049 std::stringstream errbuf;
01050 errbuf << "unfound location for source of movement: "
01051 << src << " -> " << dst << '\n';
01052 replay::process_error(errbuf.str());
01053 continue;
01054 }
01055
01056 bool show_move = preferences::show_ai_moves() || !(current_team.is_ai() || current_team.is_network_ai());
01057 ::move_unit(NULL, steps, NULL, NULL, show_move, NULL, true, true, true);
01058
01059
01060
01061
01062 if (side_num != 1 && resources::teams->front().fog_or_shroud() && !resources::teams->front().fogged(dst)
01063 && (current_team.is_ai() || current_team.is_network_ai()))
01064 {
01065
01066
01067 game_events::fire("sighted",dst);
01068 }
01069 }
01070
01071 else if (const config &child = cfg->child("attack"))
01072 {
01073 const config &destination = child.child("destination");
01074 const config &source = child.child("source");
01075 check_checksums(*cfg);
01076
01077 if (!destination || !source) {
01078 replay::process_error("no destination/source found in attack\n");
01079 continue;
01080 }
01081
01082
01083
01084 const map_location src(source, resources::state_of_game);
01085 const map_location dst(destination, resources::state_of_game);
01086
01087 int weapon_num = child["weapon"];
01088 int def_weapon_num = child["defender_weapon"].to_int(-2);
01089 if (def_weapon_num == -2) {
01090
01091 WRN_REPLAY << "Old data, having to guess weapon\n";
01092 def_weapon_num = -1;
01093 }
01094
01095 unit_map::iterator u = resources::units->find(src);
01096 if (!u.valid()) {
01097 replay::process_error("unfound location for source of attack\n");
01098 continue;
01099 }
01100
01101 if (size_t(weapon_num) >= u->attacks().size()) {
01102 replay::process_error("illegal weapon type in attack\n");
01103 continue;
01104 }
01105
01106 unit_map::const_iterator tgt = resources::units->find(dst);
01107
01108 if (!tgt.valid()) {
01109 std::stringstream errbuf;
01110 errbuf << "unfound defender for attack: " << src << " -> " << dst << '\n';
01111 replay::process_error(errbuf.str());
01112 continue;
01113 }
01114
01115 if (def_weapon_num >= static_cast<int>(tgt->attacks().size())) {
01116
01117 replay::process_error("illegal defender weapon type in attack\n");
01118 continue;
01119 }
01120
01121 int seed = child["seed"];
01122 rand_rng::set_seed(child["seed"]);
01123 LOG_REPLAY << "Replaying attack with seed " << seed << "\n";
01124
01125 DBG_REPLAY << "Attacker XP (before attack): " << u->experience() << "\n";
01126
01127 attack_unit(src, dst, weapon_num, def_weapon_num, !get_replay_source().is_skipping());
01128
01129 u = resources::units->find(src);
01130 tgt = resources::units->find(dst);
01131
01132 if(u.valid()){
01133 DBG_REPLAY << "Attacker XP (after attack): " << u->experience() << "\n";
01134 if (u->advances()) {
01135 get_replay_source().add_expected_advancement(u->get_location());
01136 }
01137 }
01138
01139 DBG_REPLAY << "expected_advancements.size: " << get_replay_source().expected_advancements().size() << "\n";
01140 if (tgt.valid() && tgt->advances()) {
01141 get_replay_source().add_expected_advancement(tgt->get_location());
01142 }
01143
01144
01145
01146 if(get_replay_source().expected_advancements().empty()) {
01147 resources::controller->check_victory();
01148 }
01149 fix_shroud = true;
01150 }
01151 else if (const config &child = cfg->child("fire_event"))
01152 {
01153 foreach (const config &v, child.child_range("set_variable")) {
01154 resources::state_of_game->set_variable(v["name"], v["value"]);
01155 }
01156 const std::string &event = child["raise"];
01157 if (const config &source = child.child("source")) {
01158 game_events::fire(event, map_location(source, resources::state_of_game));
01159 } else {
01160 game_events::fire(event);
01161 }
01162
01163 }
01164 else if (const config &child = cfg->child("advance_unit"))
01165 {
01166 const map_location loc(child, resources::state_of_game);
01167 get_replay_source().add_expected_advancement(loc);
01168 DBG_REPLAY << "got an explicit advance\n";
01169
01170 } else if (cfg->child("global_variable")) {
01171 } else {
01172 if(! cfg->child("checksum")) {
01173 replay::process_error("unrecognized action:\n" + cfg->debug());
01174 } else {
01175 check_checksums(*cfg);
01176 }
01177 }
01178
01179
01180
01181 if ( fix_shroud && !get_replay_source().is_skipping() ) {
01182 if ( clear_shroud(side_num) && !recorder.is_skipping() )
01183 resources::screen->draw();
01184 }
01185
01186 if (const config &child = cfg->child("verify")) {
01187 verify(*resources::units, child);
01188 }
01189 }
01190 }
01191
01192 replay_network_sender::replay_network_sender(replay& obj) : obj_(obj), upto_(obj_.ncommands())
01193 {
01194 }
01195
01196 replay_network_sender::~replay_network_sender()
01197 {
01198 commit_and_sync();
01199 }
01200
01201 void replay_network_sender::sync_non_undoable()
01202 {
01203 if(network::nconnections() > 0) {
01204 resources::whiteboard->send_network_data();
01205
01206 config cfg;
01207 const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands(),replay::NON_UNDO_DATA));
01208 if(data.empty() == false) {
01209 network::send_data(cfg, 0);
01210 }
01211 }
01212 }
01213
01214 void replay_network_sender::commit_and_sync()
01215 {
01216 if(network::nconnections() > 0) {
01217 resources::whiteboard->send_network_data();
01218
01219 config cfg;
01220 const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands()));
01221 if(data.empty() == false) {
01222 network::send_data(cfg, 0);
01223 }
01224
01225 upto_ = obj_.ncommands();
01226 }
01227 }
01228
01229 config mp_sync::get_user_choice(const std::string &name, const user_choice &uch,
01230 int side, bool force_sp)
01231 {
01232 if (force_sp && network::nconnections() != 0 &&
01233 resources::state_of_game->phase() != game_state::PLAY)
01234 {
01235
01236
01237
01238
01239
01240
01241 ERR_REPLAY << "MP synchronization does not work during prestart and start events.";
01242 throw end_level_exception(QUIT);
01243 }
01244 if (resources::state_of_game->phase() == game_state::PLAY || force_sp)
01245 {
01246
01247
01248
01249
01250
01251 if (unsigned(side - 1) >= resources::teams->size())
01252 side = resources::controller->current_side();
01253 assert(1 <= side && side <= static_cast<int>(resources::teams->size()));
01254
01255 int active_side = side;
01256 if ((*resources::teams)[active_side - 1].is_local() &&
01257 get_replay_source().at_end())
01258 {
01259
01260
01261 DBG_REPLAY << "MP synchronization: local choice\n";
01262 config cfg = uch.query_user();
01263 recorder.user_input(name, cfg);
01264 return cfg;
01265
01266 } else {
01267
01268
01269 DBG_REPLAY << "MP synchronization: remote choice\n";
01270 do_replay_handle(active_side, name);
01271 const config *action = get_replay_source().get_next_action();
01272 if (!action || !*(action = &action->child(name))) {
01273 replay::process_error("[" + name + "] expected but none found\n");
01274 return config();
01275 }
01276 return *action;
01277 }
01278 }
01279 else
01280 {
01281
01282
01283
01284
01285 DBG_REPLAY << "MP synchronization: synchronized choice\n";
01286 return uch.random_choice(resources::state_of_game->rng());
01287 }
01288 }