The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
replay.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Replay control code.
18  *
19  * See http://www.wesnoth.org/wiki/ReplayWML for more info.
20  */
21 
22 #include "global.hpp"
23 #include "replay.hpp"
24 
25 #include "actions/undo.hpp"
26 #include "config_assign.hpp"
27 #include "dialogs.hpp"
28 #include "display_chat_manager.hpp"
29 #include "floating_label.hpp"
30 #include "game_display.hpp"
31 #include "game_preferences.hpp"
32 #include "game_data.hpp"
33 #include "log.hpp"
34 #include "map/label.hpp"
35 #include "map/location.hpp"
36 #include "play_controller.hpp"
37 #include "synced_context.hpp"
38 #include "resources.hpp"
39 #include "statistics.hpp"
40 #include "units/unit.hpp"
41 #include "whiteboard/manager.hpp"
42 #include "replay_recorder_base.hpp"
43 
44 #include <boost/lexical_cast.hpp>
45 #include <set>
46 #include <map>
47 
48 static lg::log_domain log_replay("replay");
49 #define DBG_REPLAY LOG_STREAM(debug, log_replay)
50 #define LOG_REPLAY LOG_STREAM(info, log_replay)
51 #define WRN_REPLAY LOG_STREAM(warn, log_replay)
52 #define ERR_REPLAY LOG_STREAM(err, log_replay)
53 
54 static lg::log_domain log_random("random");
55 #define DBG_RND LOG_STREAM(debug, log_random)
56 #define LOG_RND LOG_STREAM(info, log_random)
57 #define WRN_RND LOG_STREAM(warn, log_random)
58 #define ERR_RND LOG_STREAM(err, log_random)
59 
60 
61 //functions to verify that the unit structure on both machines is identical
62 
63 static void verify(const unit_map& units, const config& cfg) {
64  std::stringstream errbuf;
65  LOG_REPLAY << "verifying unit structure...\n";
66 
67  const size_t nunits = cfg["num_units"].to_size_t();
68  if(nunits != units.size()) {
69  errbuf << "SYNC VERIFICATION FAILED: number of units from data source differ: "
70  << nunits << " according to data source. " << units.size() << " locally\n";
71 
72  std::set<map_location> locs;
73  for (const config &u : cfg.child_range("unit"))
74  {
75  const map_location loc(u);
76  locs.insert(loc);
77 
78  if(units.count(loc) == 0) {
79  errbuf << "data source says there is a unit at "
80  << loc << " but none found locally\n";
81  }
82  }
83 
84  for(unit_map::const_iterator j = units.begin(); j != units.end(); ++j) {
85  if (locs.count(j->get_location()) == 0) {
86  errbuf << "local unit at " << j->get_location()
87  << " but none in data source\n";
88  }
89  }
90  replay::process_error(errbuf.str());
91  errbuf.clear();
92  }
93 
94  for (const config &un : cfg.child_range("unit"))
95  {
96  const map_location loc(un);
97  const unit_map::const_iterator u = units.find(loc);
98  if(u == units.end()) {
99  errbuf << "SYNC VERIFICATION FAILED: data source says there is a '"
100  << un["type"] << "' (side " << un["side"] << ") at "
101  << loc << " but there is no local record of it\n";
102  replay::process_error(errbuf.str());
103  errbuf.clear();
104  }
105 
106  config cfg;
107  u->write(cfg);
108 
109  bool is_ok = true;
110  static const std::string fields[] = {"type","hitpoints","experience","side",""};
111  for(const std::string* str = fields; str->empty() == false; ++str) {
112  if (cfg[*str] != un[*str]) {
113  errbuf << "ERROR IN FIELD '" << *str << "' for unit at "
114  << loc << " data source: '" << un[*str]
115  << "' local: '" << cfg[*str] << "'\n";
116  is_ok = false;
117  }
118  }
119 
120  if(!is_ok) {
121  errbuf << "(SYNC VERIFICATION FAILED)\n";
122  replay::process_error(errbuf.str());
123  errbuf.clear();
124  }
125  }
126 
127  LOG_REPLAY << "verification passed\n";
128 }
129 
130 static time_t get_time(const config &speak)
131 {
132  time_t time;
133  if (!speak["time"].empty())
134  {
135  std::stringstream ss(speak["time"].str());
136  ss >> time;
137  }
138  else
139  {
140  //fallback in case sender uses wesnoth that doesn't send timestamps
141  time = ::time(nullptr);
142  }
143  return time;
144 }
145 
147  : color_()
148  , nick_()
149  , text_(cfg["message"].str())
150 {
151  const std::string& team_name = cfg["team_name"];
152  if(team_name == "")
153  {
154  nick_ = cfg["id"].str();
155  } else {
156  nick_ = "*"+cfg["id"].str()+"*";
157  }
158  int side = cfg["side"].to_int(0);
159  LOG_REPLAY << "side in message: " << side << std::endl;
160  if (side==0) {
161  color_ = "white";//observers
162  } else {
164  }
165  time_ = get_time(cfg);
166  /*
167  } else if (side==1) {
168  color_ = "red";
169  } else if (side==2) {
170  color_ = "blue";
171  } else if (side==3) {
172  color_ = "green";
173  } else if (side==4) {
174  color_ = "purple";
175  }*/
176 }
177 
179 {
180 }
181 
183  : base_(&base)
184  , message_locations()
185 {}
186 
188 {
190 }
191 /*
192  TODO: there should be different types of OOS messages:
193  1)the normal OOS message
194  2) the 'is guarenteed you'll get an assertion error after this and therefore you cannot continur' OOS message
195  3) the 'do you want to overwrite calculated data with the data stored in replay' OOS error message.
196 
197 */
199 {
200  ERR_REPLAY << msg << std::flush;
201 
202  resources::controller->process_oos(msg); // might throw quit_game_exception()
203 }
204 
206 {
207  if(! game_config::mp_debug) {
208  return;
209  }
210  config& cc = cfg.add_child("checksum");
211  loc.write(cc);
213  assert(u.valid());
214  cc["value"] = get_checksum(*u);
215 }
216 
217 
219 {
220  config& cmd = add_command();
222  init_side["side_number"] = resources::controller->current_side();
223  cmd.add_child("init_side", init_side);
224 }
225 
227 {
228  config& cmd = add_command();
229  cmd["sent"] = true;
230  cmd.add_child("start");
231 }
232 
234 {
235  config& cmd = add_command();
236  config val;
237  val["value"] = value;
238  val["team"] = team;
239  cmd.add_child("countdown_update",val);
240 }
241 void replay::add_synced_command(const std::string& name, const config& command)
242 {
243  config& cmd = add_command();
244  cmd.add_child(name,command);
245  cmd["from_side"] = resources::controller->current_side();
246  LOG_REPLAY << "add_synced_command: \n" << cmd.debug() << "\n";
247 }
248 
249 
250 
251 void replay::user_input(const std::string &name, const config &input, int from_side)
252 {
253  config& cmd = add_command();
254  cmd["dependent"] = true;
255  if(from_side == -1)
256  {
257  cmd["from_side"] = "server";
258  }
259  else
260  {
261  cmd["from_side"] = from_side;
262  }
263  cmd.add_child(name, input);
264 }
265 
267 {
268  assert(label);
270  config val;
271 
272  label->write(val);
273 
274  cmd.add_child("label",val);
275 }
276 
277 void replay::clear_labels(const std::string& team_name, bool force)
278 {
280 
281  config val;
282  val["team_name"] = team_name;
283  val["force"] = force;
284  cmd.add_child("clear_labels",val);
285 }
286 
288 {
289  config& cmd = add_command();
290  cmd["async"] = true; // Not undoable, but depends on moves/recruits that are
291  config val;
292  loc.write(val);
293  val["name"] = name;
294  cmd.add_child("rename", val);
295 }
296 
297 
299 {
300  config& cmd = add_command();
301  cmd.add_child("end_turn");
302 }
303 
304 
305 void replay::add_log_data(const std::string &key, const std::string &var)
306 {
307  config& ulog = base_->get_upload_log();
308  ulog[key] = var;
309 }
310 
311 void replay::add_log_data(const std::string &category, const std::string &key, const std::string &var)
312 {
313  config& ulog = base_->get_upload_log();
314  config& cat = ulog.child_or_add(category);
315  cat[key] = var;
316 }
317 
318 void replay::add_log_data(const std::string &category, const std::string &key, const config &c)
319 {
320  config& ulog = base_->get_upload_log();
321  config& cat = ulog.child_or_add(category);
322  cat.add_child(key,c);
323 }
324 
326 {
327  return add_chat_message_location(base_->get_pos() - 1);
328 }
329 
331 {
332  assert(base_->get_command_at(pos).has_child("speak"));
333  if(std::find(message_locations.begin(), message_locations.end(), pos) == message_locations.end()) {
334  message_locations.push_back(pos);
335  return true;
336  }
337  else {
338  return false;
339  }
340 }
341 
342 void replay::speak(const config& cfg)
343 {
344  config& cmd = base_->insert_command(base_->size());
345  cmd["undo"] = false;
346  cmd.add_child("speak",cfg);
348 }
349 
350 void replay::add_chat_log_entry(const config &cfg, std::back_insert_iterator<std::vector<chat_msg> > &i) const
351 {
352  if (!cfg) return;
353 
354  if (!preferences::parse_should_show_lobby_join(cfg["id"], cfg["message"])) return;
355  if (preferences::is_ignored(cfg["id"])) return;
356  *i = chat_msg(cfg);
357 }
358 
360 {
361  base_->remove_command(index);
362  std::vector<int>::reverse_iterator loc_it;
363  for (loc_it = message_locations.rbegin(); loc_it != message_locations.rend() && index < *loc_it;++loc_it)
364  {
365  --(*loc_it);
366  }
367 }
368 
369 // cached message log
370 static std::vector< chat_msg > message_log;
371 
372 
373 const std::vector<chat_msg>& replay::build_chat_log()
374 {
375  message_log.clear();
377  int last_location = 0;
378  std::back_insert_iterator<std::vector < chat_msg > > chat_log_appender( back_inserter(message_log));
379  for (loc_it = message_locations.begin(); loc_it != message_locations.end(); ++loc_it)
380  {
381  last_location = *loc_it;
382 
383  const config &speak = command(last_location).child("speak");
384  assert(speak);
385  add_chat_log_entry(speak, chat_log_appender);
386 
387  }
388  return message_log;
389 }
390 
391 config replay::get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type)
392 {
393  config res;
394 
395  for (int cmd = cmd_start; cmd < cmd_end; ++cmd)
396  {
397  config &c = command(cmd);
398  //prevent creating 'blank' attribute values during checks
399  const config &cc = c;
400  if ((data_type == ALL_DATA || !cc["undo"].to_bool(true)) && !cc["sent"].to_bool(false))
401  {
402  res.add_child("command", c);
403  if (data_type == NON_UNDO_DATA) c["sent"] = true;
404  }
405  }
406 
407  return res;
408 }
409 
410 void replay::redo(const config& cfg)
411 {
412  assert(base_->get_pos() == ncommands());
413  for (const config &cmd : cfg.child_range("command"))
414  {
415  base_->add_child() = cmd;
416  }
417  base_->set_to_end();
418 
419 }
420 
421 
422 
424 {
425  for (int cmd_num = base_->get_pos() - 1; cmd_num >= 0; --cmd_num)
426  {
427  config &c = command(cmd_num);
428  const config &cc = c;
429  if (cc["dependent"].to_bool(false) || !cc["undo"].to_bool(true) || cc["async"].to_bool(false))
430  {
431  continue;
432  }
433  return c;
434  }
435  ERR_REPLAY << "replay::get_last_real_command called with no existent command." << std::endl;
436  assert(false && "replay::get_last_real_command called with no existent command.");
437  throw "replay::get_last_real_command called with no existent command.";
438 }
439 /// fixes a rename command when undoing a earlier command.
440 /// @return: true if the command should be removed.
441 static bool fix_rename_command(const config& c, config& async_child)
442 {
443  if (const config &child = c.child("move"))
444  {
445  // A unit's move is being undone.
446  // Repair unsynced cmds whose locations depend on that unit's location.
447  std::vector<map_location> steps;
448 
449  try {
450  read_locations(child,steps);
451  } catch (bad_lexical_cast &) {
452  WRN_REPLAY << "Warning: Path data contained something which could not be parsed to a sequence of locations:" << "\n config = " << child.debug() << std::endl;
453  }
454 
455  if (steps.empty()) {
456  ERR_REPLAY << "trying to undo a move using an empty path";
457  }
458  else {
459  const map_location &src = steps.front();
460  const map_location &dst = steps.back();
461  map_location aloc(async_child);
462  if (dst == aloc) src.write(async_child);
463  }
464  }
465  else
466  {
467  const config *chld = &c.child("recruit");
468  if (!*chld) chld = &c.child("recall");
469  if (*chld) {
470  // A unit is being un-recruited or un-recalled.
471  // Remove unsynced commands that would act on that unit.
472  map_location src(*chld);
473  map_location aloc(async_child);
474  if (src == aloc) {
475  return true;
476  }
477  }
478  }
479  return false;
480 }
481 
483 {
484  assert(dst.empty());
485  //assert that we are not undoing a command which we didn't execute yet.
486  assert(at_end());
487 
488  //calculate the index of the last synced user action (which we want to undo).
489  int cmd_index = ncommands() - 1;
490  for (; cmd_index >= 0; --cmd_index)
491  {
492  //"undo"=no means speak/label/remove_label, especialy attack, recruits etc. have "undo"=yes
493  //"async"=yes means rename_unit
494  //"dependent"=true means user input
495  const config &c = command(cmd_index);
496 
497  if(c["undo"].to_bool(true) && !c["async"].to_bool(false) && !c["dependent"].to_bool(false))
498  {
499  if(c["sent"].to_bool(false))
500  {
501  ERR_REPLAY << "trying to undo a command that was already sent.\n";
502  return;
503  }
504  else
505  {
506  break;
507  }
508  }
509  }
510 
511  if (cmd_index < 0)
512  {
513  ERR_REPLAY << "trying to undo a command but no command was found.\n";
514  return;
515  }
516  //Fix the [command]s after the undone action. This includes dependent commands for that user actions and async user action.
517  for(int i = ncommands() - 1; i >= cmd_index; --i)
518  {
519  config &c = command(i);
520  const config &cc = c;
521  if(!cc["undo"].to_bool(true))
522  {
523  //Leave these commands on the replay.
524  }
525  else if(cc["async"].to_bool(false))
526  {
527  if(config& rename = c.child("rename"))
528  {
529  if(fix_rename_command(command(cmd_index), rename))
530  {
531  //remove the command from the replay if fix_rename_command requested it.
532  remove_command(i);
533  }
534  }
535  }
536  else if(cc["dependent"].to_bool(false) || i == cmd_index)
537  {
538  //we loop backwars so we must insert new insert at beginning to preserve order.
539  dst.add_child_at("command", config(), 0).swap(c);
540  remove_command(i);
541  }
542  else
543  {
544  ERR_REPLAY << "Coudn't handle command:\n" << cc << "\nwhen undoing.\n";
545  }
546  }
547  set_to_end();
548 }
549 
551 {
552  config dummy;
553  undo_cut(dummy);
554 }
555 
557 {
558  config & retv = base_->get_command_at(n);
559  assert(retv);
560  return retv;
561 }
562 
563 int replay::ncommands() const
564 {
565  return base_->size();
566 }
567 
569 {
570  // If we weren't at the end of the replay we should skip one or more
571  // commands.
572  assert(at_end());
573  config& retv = base_->add_child();
574  set_to_end();
575  return retv;
576 }
577 
579 {
581  r["undo"] = false;
582  base_->set_pos(base_->get_pos() + 1);
583  return r;
584 }
585 
587 {
588  base_->set_pos(0);
589 }
590 
592 {
593 
594  if (base_->get_pos() > 0)
595  base_->set_pos(base_->get_pos() - 1);
596 }
597 
599 {
600  if (at_end())
601  return nullptr;
602 
603  LOG_REPLAY << "up to replay action " << base_->get_pos() + 1 << '/' << ncommands() << '\n';
604 
605  config* retv = &command(base_->get_pos());
606  base_->set_pos(base_->get_pos() + 1);
607  return retv;
608 }
609 
610 
611 bool replay::at_end() const
612 {
613  assert(base_->get_pos() <= ncommands());
614  return base_->get_pos() == ncommands();
615 }
616 
618 {
619  base_->set_to_end();
620 }
621 
623 {
624  return ncommands() == 0;
625 }
626 
627 void replay::add_config(const config& cfg, MARK_SENT mark)
628 {
629  for (const config &cmd : cfg.child_range("command"))
630  {
631  config &cfg = base_->insert_command(base_->size());
632  cfg = cmd;
633  if(mark == MARK_AS_SENT) {
634  cfg["sent"] = true;
635  }
636  }
637 }
639 {
640  //this method would confuse the value of 'pos' otherwise
641  assert(base_->get_pos() == 0);
642  //since pos is 0, at_end() is equivalent to empty()
643  if(at_end() || !base_->get_command_at(0).has_child("start"))
644  {
645  base_->insert_command(0) = config_of("start", config())("sent", true);
646  return true;
647  }
648  else
649  {
650  return false;
651  }
652 }
653 
654 static void show_oos_error_error_function(const std::string& message, bool /*heavy*/)
655 {
656  replay::process_error(message);
657 }
658 
659 REPLAY_RETURN do_replay(bool one_move)
660 {
661  log_scope("do replay");
662 
663  if (!resources::controller->is_skipping_replay()) {
665  }
666 
667  update_locker lock_update(resources::screen->video(), resources::controller->is_skipping_replay());
668  return do_replay_handle(one_move);
669 }
670 /**
671  @returns:
672  if we expect a user choice and found something that prevents us from moving on we return REPLAY_FOUND_DEPENDENT (even if it is not a dependent command)
673  else if we found an [end_turn] we return REPLAY_FOUND_END_TURN
674  else if we found a player action and one_move=true we return REPLAY_FOUND_END_MOVE
675  else (<=> we reached the end of the replay) we return REPLAY_RETURN_AT_END
676 */
678 {
679 
680  //team &current_team = (*resources::teams)[side_num - 1];
681 
682  const int side_num = resources::controller->current_side();
683  while(true)
684  {
686  const bool is_synced = synced_context::is_synced();
687 
688  DBG_REPLAY << "in do replay with is_synced=" << is_synced << "\n";
689 
690  if (cfg != nullptr)
691  {
692  DBG_REPLAY << "Replay data:\n" << *cfg << "\n";
693  }
694  else
695  {
696  DBG_REPLAY << "Replay data at end\n";
697  return REPLAY_RETURN_AT_END;
698  }
699 
700 
701  const config::all_children_itors ch_itors = cfg->all_children_range();
702  //if there is an empty command tag or a start tag
703  if (ch_itors.first == ch_itors.second || cfg->has_child("start"))
704  {
705  //this shouldn't happen anymore because replaycontroller now moves over the [start] with get_next_action
706  //also we removed the the "add empty replay entry at scenario reload" behavior.
707  ERR_REPLAY << "found "<< cfg->debug() <<" in replay" << std::endl;
708  //do nothing
709  }
710  else if (const config &child = cfg->child("speak"))
711  {
712  const std::string &team_name = child["team_name"];
713  const std::string &speaker_name = child["id"];
714  const std::string &message = child["message"];
715  //if (!preferences::parse_should_show_lobby_join(speaker_name, message)) return;
716  bool is_whisper = (speaker_name.find("whisper: ") == 0);
717  if(resources::recorder->add_chat_message_location()) {
718  DBG_REPLAY << "tried to add a chat message twice.\n";
719  if (!resources::controller->is_skipping_replay() || is_whisper) {
720  int side = child["side"];
721  resources::screen->get_chat_manager().add_chat_message(get_time(child), speaker_name, side, message,
722  (team_name.empty() ? events::chat_handler::MESSAGE_PUBLIC
725  }
726  }
727  }
728  else if (const config &child = cfg->child("label"))
729  {
730  terrain_label label(resources::screen->labels(), child);
731 
733  label.text(),
734  label.creator(),
735  label.team_name(),
736  label.color());
737  }
738  else if (const config &child = cfg->child("clear_labels"))
739  {
740  resources::screen->labels().clear(std::string(child["team_name"]), child["force"].to_bool());
741  }
742  else if (const config &child = cfg->child("rename"))
743  {
744  const map_location loc(child);
745  const std::string &name = child["name"];
746 
748  if (u.valid() && !u->unrenamable()) {
749  u->rename(name);
750  } else {
751  // Users can rename units while it's being killed or at another machine.
752  // This since the player can rename units when it's not his/her turn.
753  // There's not a simple way to prevent that so in that case ignore the
754  // rename instead of throwing an OOS.
755  // The same way it is possible that an unrenamable unit moves to a
756  // hex where previously a renamable unit was.
757  WRN_REPLAY << "attempt to rename unit at location: "
758  << loc << (u.valid() ? ", which is unrenamable" : ", where none exists (anymore)") << "\n";
759  }
760  }
761 
762  else if (cfg->child("init_side"))
763  {
764 
765  if(is_synced)
766  {
767  replay::process_error("found side initialization in replay expecting a user choice\n" );
769  return REPLAY_FOUND_DEPENDENT;
770  }
771  else
772  {
774  if (one_move) {
775  return REPLAY_FOUND_INIT_TURN;
776  }
777  }
778  }
779 
780  //if there is an end turn directive
781  else if (cfg->child("end_turn"))
782  {
783  if(is_synced)
784  {
785  replay::process_error("found turn end in replay while expecting a user choice\n" );
787  return REPLAY_FOUND_DEPENDENT;
788  }
789  else
790  {
791  if (const config &child = cfg->child("verify")) {
792  verify(*resources::units, child);
793  }
794 
795  return REPLAY_FOUND_END_TURN;
796  }
797  }
798  else if (const config &child = cfg->child("countdown_update"))
799  {
800  int val = child["value"];
801  int tval = child["team"];
802  if (tval <= 0 || tval > int(resources::teams->size())) {
803  std::stringstream errbuf;
804  errbuf << "Illegal countdown update \n"
805  << "Received update for :" << tval << " Current user :"
806  << side_num << "\n" << " Updated value :" << val;
807 
808  replay::process_error(errbuf.str());
809  } else {
810  (*resources::teams)[tval - 1].set_countdown_time(val);
811  }
812  }
813  else if ((*cfg)["dependent"].to_bool(false))
814  {
815  if(!is_synced)
816  {
817  replay::process_error("found dependent command in replay while is_synced=false\n" );
818  //ignore this command
819  continue;
820  }
821  //this means user choice.
822  // it never makes sense to try to execute a user choice.
823  // but we are called from
824  // the only other option for "dependent" command is checksum wich is already checked.
825  assert(cfg->all_children_count() == 1);
826  std::string child_name = cfg->all_children_range().first->key;
827  DBG_REPLAY << "got an dependent action name = " << child_name <<"\n";
829  return REPLAY_FOUND_DEPENDENT;
830  }
831  else
832  {
833  //we checked for empty commands at the beginning.
834  const std::string & commandname = cfg->ordered_begin()->key;
835  config data = cfg->ordered_begin()->cfg;
836 
837  if(is_synced)
838  {
839  replay::process_error("found [" + commandname + "] command in replay expecting a user choice\n" );
841  return REPLAY_FOUND_DEPENDENT;
842  }
843  else
844  {
845  LOG_REPLAY << "found commandname " << commandname << "in replay";
846 
847  if((*cfg)["from_side"].to_int(0) != resources::controller->current_side()) {
848  ERR_REPLAY << "recieved a synced [command] from side " << (*cfg)["from_side"].to_int(0) << ". Expacted was a [command] from side " << resources::controller->current_side() << "\n";
849  }
850  else if((*cfg)["side_invalid"].to_bool(false)) {
851  ERR_REPLAY << "recieved a synced [command] from side " << (*cfg)["from_side"].to_int(0) << ". Sent from wrong client.\n";
852  }
853  /*
854  we need to use the undo stack during replays in order to make delayed shroud updated work.
855  */
856  synced_context::run(commandname, data, true, !resources::controller->is_skipping_replay(), show_oos_error_error_function);
857  if(resources::controller->is_regular_game_end()) {
858  return REPLAY_FOUND_END_LEVEL;
859  }
860  if (one_move) {
861  return REPLAY_FOUND_END_MOVE;
862  }
863  }
864  }
865 
866  if (const config &child = cfg->child("verify")) {
867  verify(*resources::units, child);
868  }
869  }
870 }
871 
872 replay_network_sender::replay_network_sender(replay& obj) : obj_(obj), upto_(obj_.ncommands())
873 {
874 }
875 
877 {
878  try {
879  commit_and_sync();
880  } catch (...) {}
881 }
882 
884 {
885  if(resources::controller->is_networked_mp()) {
886  resources::whiteboard->send_network_data();
887 
888  config cfg;
890  if(data.empty() == false) {
892  }
893  }
894 }
895 
897 {
898  if(resources::controller->is_networked_mp()) {
899  resources::whiteboard->send_network_data();
900 
901  config cfg;
902  const config& data = cfg.add_child("turn",obj_.get_data_range(upto_,obj_.ncommands()));
903 
904  if(data.empty() == false) {
906  }
907 
908  upto_ = obj_.ncommands();
909  }
910 }
play_controller * controller
Definition: resources.cpp:21
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void delete_upcoming_commands()
Definition: replay.cpp:187
void read_locations(const config &cfg, std::vector< map_location > &locs)
Parse x,y keys of a config into a vector of locations.
Definition: location.cpp:413
DATA_TYPE
Definition: replay.hpp:98
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
replay(replay_recorder_base &base)
Definition: replay.cpp:182
unit_iterator end()
Definition: map.hpp:311
size_t size() const
Definition: map.hpp:314
replay_network_sender(replay &obj)
Definition: replay.cpp:872
void add_countdown_update(int value, int team)
Definition: replay.cpp:233
void end_turn()
Definition: replay.cpp:298
int ncommands() const
Definition: replay.cpp:563
bool empty()
Definition: replay.cpp:622
void speak(const config &cfg)
Definition: replay.cpp:342
bool add_chat_message_location()
adds a chat message if it wasn't added yet.
Definition: replay.cpp:325
size_t count(const map_location &loc) const
Definition: map.hpp:306
const GLfloat * c
Definition: glew.h:12741
int pos
Definition: formula.cpp:800
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
void add_log_data(const std::string &key, const std::string &var)
Definition: replay.cpp:305
static bool run(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
Sets the context to 'synced', initialises random context, and calls the given function.
virtual ~chat_msg()
Definition: replay.cpp:178
config & child_or_add(const std::string &key)
Returns a reference to the first child with the given key.
Definition: config.cpp:734
void add_start()
Definition: replay.cpp:226
game_display * screen
Definition: resources.cpp:27
const map_location & location() const
Definition: label.cpp:459
GLenum GLenum GLenum input
Definition: glew.h:10668
config & insert_command(int index)
void sync_non_undoable()
Definition: replay.cpp:883
unit_iterator begin()
Definition: map.hpp:308
const formula_callable & base_
Definition: formula.cpp:568
GLuint const GLfloat * val
Definition: glew.h:2614
bool message_bell()
#define LOG_REPLAY
Definition: replay.cpp:50
SDL_Color color_
Definition: font.cpp:605
#define WRN_REPLAY
Definition: replay.cpp:51
Replay control code.
GLenum src
Definition: glew.h:2392
REPLAY_RETURN do_replay(bool one_move)
Definition: replay.cpp:659
std::string debug() const
Definition: config.cpp:1438
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
config & get_last_real_command()
Definition: replay.cpp:423
bool empty() const
Definition: config.cpp:1105
tformula< t_string > text_
The text to draw.
Definition: canvas.cpp:1268
#define ERR_REPLAY
Definition: replay.cpp:52
replay_recorder_base * base_
Definition: replay.hpp:151
void add_synced_command(const std::string &name, const config &command)
Definition: replay.cpp:241
int creator() const
Definition: label.cpp:429
MARK_SENT
Definition: replay.hpp:122
static std::string get_side_highlight_pango(int side)
Definition: team.cpp:858
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const SDL_Color color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:145
const std::string & team_name() const
Definition: label.cpp:439
REPLAY_RETURN
Definition: replay.hpp:155
static time_t get_time(const config &speak)
Definition: replay.cpp:130
bool has_child(const std::string &key) const
Determine whether a config has a child or not.
Definition: config.cpp:651
static void show_oos_error_error_function(const std::string &message, bool)
Definition: replay.cpp:654
const SDL_Color & color() const
Definition: label.cpp:464
static void verify(const unit_map &units, const config &cfg)
Definition: replay.cpp:63
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
void init_side()
Definition: replay.cpp:218
void swap(config &cfg)
Definition: config.cpp:1518
const t_string & text() const
Definition: label.cpp:419
void redo(const config &dst)
Definition: replay.cpp:410
int current_side() const
Returns the number of the side whose turn it is.
bool add_start_if_not_there_yet()
Definition: replay.cpp:638
std::vector< team > * teams
Definition: resources.cpp:29
void remove_command(int index)
void clear(const std::string &, bool force)
Definition: label.cpp:217
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.hpp:623
GLsizei const GLfloat * value
Definition: glew.h:1817
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
GLenum GLenum dst
Definition: glew.h:2392
void start_replay()
Definition: replay.cpp:586
time_t time_
Definition: replay.hpp:49
std::pair< all_children_iterator, all_children_iterator > all_children_itors
Definition: config.hpp:665
config & add_child(const std::string &key)
Definition: config.cpp:743
GLhandleARB obj
Definition: glew.h:4486
#define DBG_REPLAY
Definition: replay.cpp:49
void add_rename(const std::string &name, const map_location &loc)
Definition: replay.cpp:287
void set_to_end()
Definition: replay.cpp:617
replay * recorder
Definition: resources.cpp:30
void add_chat_log_entry(const config &speak, std::back_insert_iterator< std::vector< chat_msg > > &i) const
Definition: replay.cpp:350
config & command(int)
Definition: replay.cpp:556
bool is_ignored(const std::string &nick)
void write(config &cfg) const
Definition: location.cpp:205
static std::vector< chat_msg > message_log
Definition: replay.cpp:370
void add_unit_checksum(const map_location &loc, config &cfg)
Definition: replay.cpp:205
config & get_command_at(int pos)
void do_init_side()
Called by replay handler or init_side() to do actual work for turn change.
Encapsulates the map of the game.
Definition: location.hpp:38
static void process_error(const std::string &msg)
Definition: replay.cpp:198
GLuint res
Definition: glew.h:9258
std::string color_
Definition: replay.hpp:46
std::string get_checksum(const unit &u)
Gets a checksum for a unit.
Definition: unit.cpp:2511
GLuint index
Definition: glew.h:1782
#define log_scope(description)
Definition: log.hpp:185
size_t i
Definition: function.cpp:1057
void remove_command(int)
Definition: replay.cpp:359
static std::string flush(std::ostringstream &s)
Definition: reports.cpp:86
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
const std::vector< chat_msg > & build_chat_log()
Definition: replay.cpp:373
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
void write(config &res) const
Definition: label.cpp:405
GLuint const GLchar * name
Definition: glew.h:1782
GLsizeiptr size
Definition: glew.h:1649
display_chat_manager & get_chat_manager()
To store label data Class implements logic for rendering.
Definition: label.hpp:103
GLclampd n
Definition: glew.h:5903
config & add_nonundoable_command()
adds a new command to the command list at the current position.
Definition: replay.cpp:578
static lg::log_domain log_replay("replay")
bool find(E event, F functor)
Tests whether an event handler is available.
void add_config(const config &cfg, MARK_SENT mark=MARK_AS_UNSENT)
Definition: replay.cpp:627
bool at_end() const
Definition: replay.cpp:611
std::vector< int > message_locations
Definition: replay.hpp:152
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
unsigned all_children_count() const
Definition: config.cpp:646
Various functions that implement the undoing (and redoing) of in-game commands.
void undo_cut(config &dst)
Definition: replay.cpp:482
Standard logging facilities (interface).
std::string nick_
Definition: replay.hpp:47
Container associating units to locations.
Definition: map.hpp:90
config * get_next_action()
Definition: replay.cpp:598
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
#define c
Definition: glew.h:12743
void revert_action()
Definition: replay.cpp:591
bool parse_should_show_lobby_join(const std::string &sender, const std::string &message)
boost::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:36
config & add_command()
Adds a new empty command to the command list at the end.
Definition: replay.cpp:568
map_labels & labels()
Definition: display.cpp:2773
config get_data_range(int cmd_start, int cmd_end, DATA_TYPE data_type=ALL_DATA)
Definition: replay.cpp:391
unit_iterator find(size_t id)
Definition: map.cpp:285
static lg::log_domain log_random("random")
bool valid() const
Definition: map.hpp:229
void undo()
Definition: replay.cpp:550
void user_input(const std::string &, const config &, int from_side)
adds a user_input to the replay
Definition: replay.cpp:251
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
void add_chat_message(const time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
static bool fix_rename_command(const config &c, config &async_child)
fixes a rename command when undoing a earlier command.
Definition: replay.cpp:441
REPLAY_RETURN do_replay_handle(bool one_move)
Definition: replay.cpp:677
Thrown when a lexical_cast fails.
void clear_labels(const std::string &, bool)
Definition: replay.cpp:277
static bool is_synced()
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
config & add_child_at(const std::string &key, const config &val, unsigned index)
Definition: config.cpp:773
chat_msg(const config &cfg)
Definition: replay.cpp:146
all_children_iterator ordered_begin() const
Definition: config.cpp:1117
void add_label(const terrain_label *)
Definition: replay.cpp:266