The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
playmp_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2016 by Joerg Hinrichs <[email protected]>
3  wesnoth playlevel Copyright (C) 2003 by David White <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "playmp_controller.hpp"
17 
18 #include "dialogs.hpp"
19 
20 #include "actions/undo.hpp"
21 #include "display_chat_manager.hpp"
22 #include "game_end_exceptions.hpp"
23 #include "gettext.hpp"
25 #include "log.hpp"
26 #include "mp_ui_alerts.hpp"
27 #include "playturn.hpp"
28 #include "preferences.hpp"
30 #include "resources.hpp"
31 #include "savegame.hpp"
32 #include "sound.hpp"
33 #include "formula/string_utils.hpp"
34 #include "units/animation.hpp"
35 #include "whiteboard/manager.hpp"
36 #include "countdown_clock.hpp"
37 #include "synced_context.hpp"
38 #include "replay_helper.hpp"
39 #include "wesnothd_connection.hpp"
40 
41 static lg::log_domain log_engine("engine");
42 #define LOG_NG LOG_STREAM(info, log_engine)
43 
45  saved_game& state_of_game, const config& game_config,
46  const tdata_cache & tdata, CVideo& video,
47  mp_campaign_info* mp_info)
48  : playsingle_controller(level, state_of_game,
49  game_config, tdata, video, mp_info && mp_info->skip_replay_until_turn != 0) //this || means that if blindfold is enabled, quick replays will be on.
50  , network_processing_stopped_(false)
51  , blindfold_(*gui_, mp_info && mp_info->skip_replay_blindfolded)
52  , mp_info_(mp_info)
53 {
54  hotkey_handler_.reset(new hotkey_handler(*this, saved_game_)); //upgrade hotkey handler to the mp (network enabled) version
55 
56  //turn_data_.set_host(is_host);
58  // We stop quick replay if play isn't yet past turn 1
60  {
61  skip_replay_ = false;
62  }
63 }
64 
66  //halt and cancel the countdown timer
67  try {
69  } catch (...) {}
70 }
71 
74  LOG_NG << "network processing activated again";
75 }
76 
79  LOG_NG << "network processing stopped";
80 }
81 
83 {
84  mp_ui_alerts::turn_changed(current_team().current_player());
85  // Proceed with the parent function.
87 }
88 
91 }
92 
94  if (gui_->is_blindfolded()) {
96  LOG_NG << "Taking off the blindfold now " << std::endl;
97  gui_->redraw_everything();
98  }
99 }
100 
102 {
103  if (is_host()) {
104  end_turn_enable(true);
105  }
106 
107  while(end_turn_ == END_TURN_NONE) {
108  config cfg;
109  if(network_reader_.read(cfg)) {
111  {
112  end_turn();
113  }
114  }
115  play_slice();
116  gui_->draw();
117  }
118 }
119 
121 {
122  LOG_NG << "playmp::play_human_turn...\n";
123  assert(!linger_);
125  boost::scoped_ptr<countdown_clock> timer;
127  timer.reset(new countdown_clock(current_team()));
128  }
130  if(undo_stack().can_undo()) {
131  // If we reload a networked mp game we cannot undo moves made before the save
132  // Becasue other players already received them
133  if(!current_team().auto_shroud_updates()) {
135  }
136  undo_stack().clear();
137  }
139  execute_gotos();
140  }
141 
142  end_turn_enable(true);
143  while(!should_return_to_play_side()) {
144  try {
147  {
148  // Clean undo stack if turn has to be restarted (losing control)
149  if ( undo_stack().can_undo() )
150  {
151  font::floating_label flabel(_("Undoing moves not yet transmitted to the server."));
152 
153  SDL_Color color = {255,255,255,SDL_ALPHA_OPAQUE};
154  flabel.set_color(color);
155  SDL_Rect rect = gui_->map_area();
156  flabel.set_position(rect.w/2, rect.h/2);
157  flabel.set_lifetime(150);
158  flabel.set_clip_rect(rect);
159 
160  font::add_floating_label(flabel);
161  }
162 
163  while( undo_stack().can_undo() )
164  undo_stack().undo();
165 
166  }
169  if(timer)
170  {
171  bool time_left = timer->update();
172  if(!time_left)
173  {
175  }
176  }
177  }
178  catch(...)
179  {
181  throw;
182  }
184 
185  gui_->draw();
186  }
187 }
188 
190 {
191  LOG_NG << "playmp::play_human_turn...\n";
192 
194 
195  while (!should_return_to_play_side())
196  {
197  try
198  {
201  SDL_Delay(1);
202  gui_->draw();
203  }
204  catch(...)
205  {
207  throw;
208  }
210  }
211 }
212 
214 {
215  // Modify the end-turn button
216  if (! is_host()) {
217  std::shared_ptr<gui::button> btn_end = gui_->find_action_button("button-endturn");
218  btn_end->enable(false);
219  }
220  gui_->get_theme().refresh_title2("button-endturn", "title2");
221  gui_->invalidate_theme();
222  gui_->redraw_everything();
223 }
224 
226 {
227  // revert the end-turn button text to its normal label
228  gui_->get_theme().refresh_title2("button-endturn", "title");
229  gui_->invalidate_theme();
230  gui_->redraw_everything();
231  gui_->set_game_mode(game_display::RUNNING);
232 }
233 
235 {
236  LOG_NG << "beginning end-of-scenario linger\n";
237  linger_ = true;
238  // If we need to set the status depending on the completion state
239  // we're needed here.
240  gui_->set_game_mode(game_display::LINGER);
241  // End all unit moves
243 
245  assert(is_regular_game_end());
246  if ( get_end_level_data_const().transient.reveal_map ) {
247  // Change the view of all players and observers
248  // to see the whole map regardless of shroud and fog.
249  update_gui_to_player(gui_->viewing_team(), true);
250  }
251  bool quit;
252  do {
253  quit = true;
254  try {
255  // reimplement parts of play_side()
260  LOG_NG << "finished human turn" << std::endl;
261  } catch (game::load_game_exception&) {
262  LOG_NG << "caught load-game-exception" << std::endl;
263  // this should not happen, the option to load a game is disabled
264  throw;
265  } catch (ingame_wesnothd_error&) {
266  LOG_NG << "caught network-error-exception" << std::endl;
267  quit = false;
268  }
269  } while (!quit);
270 
272 
273  LOG_NG << "ending end-of-scenario linger\n";
274 }
275 
277 {
278  // If the host is here we'll never leave since we wait for the host to
279  // upload the next scenario.
280  assert(!is_host());
281 
282  config cfg;
284  while(true) {
285  try {
287  gui_->video(), _("Waiting for next scenario..."), cfg, mp_info_->wesnothd_connection);
288 
289  if(res) {
291  break;
292  }
293  }
294  else
295  {
297  }
298 
299  } catch(const quit_game_exception&) {
300  network_reader_.set_source([this](config& cfg) { return recieve_from_wesnothd(cfg);});
302  throw;
303  }
304  }
305  network_reader_.set_source([this](config& cfg) { return recieve_from_wesnothd(cfg);});
306 }
307 
310  {
311  //time_left + turn_bonus + (action_bouns * number of actions done)
312  const int new_time_in_secs = (current_team().countdown_time() / 1000)
315  const int new_time = 1000 * std::min<int>(new_time_in_secs, saved_game_.mp_settings().mp_countdown_reservoir_time);
316 
318  current_team().set_countdown_time(new_time);
320  }
321  LOG_NG << "playmp::after_human_turn...\n";
322 
323  // Normal post-processing for human turns (clear undos, end the turn, etc.)
325  //send one more time to make sure network is up-to-date.
327 
328 }
329 
331  LOG_NG << "is networked...\n";
332 
333  end_turn_enable(false);
335 
337  {
341  skip_replay_ = false;
342  }
343  }
344 
348  }
349 
350  gui_->draw();
351  }
352 
353  LOG_NG << "finished networked...\n";
354 }
355 
356 
357 void playmp_controller::process_oos(const std::string& err_msg) const {
358  // Notify the server of the oos error.
359  config cfg;
360  config& info = cfg.add_child("info");
361  info["type"] = "termination";
362  info["condition"] = "out of sync";
363  send_to_wesnothd(cfg);
364 
365  std::stringstream temp_buf;
366  std::vector<std::string> err_lines = utils::split(err_msg,'\n');
367  temp_buf << _("The game is out of sync, and cannot continue. There are a number of reasons this could happen: this can occur if you or another player have modified their game settings. This may mean one of the players is attempting to cheat. It could also be due to a bug in the game, but this is less likely.\n\nDo you want to save an error log of your game?");
368  if(!err_msg.empty()) {
369  temp_buf << " \n \n"; //and now the "Details:"
370  for(std::vector<std::string>::iterator i=err_lines.begin(); i!=err_lines.end(); ++i)
371  {
372  temp_buf << *i << '\n';
373  }
374  temp_buf << " \n";
375  }
376  scoped_savegame_snapshot snapshot(*this);
378  save.save_game_interactive(gui_->video(), temp_buf.str(), gui::YES_NO);
379 }
380 
383 
384  if (name == "ai_user_interact")
385  {
388  }
389  else if (name == "ai_gamestate_changed")
390  {
392  }
393  else if (name == "host_transfer"){
394  assert(mp_info_);
395  mp_info_->is_host = true;
396  if (linger_){
397  end_turn_enable(true);
398  gui_->invalidate_theme();
399  }
400  }
401 }
403 {
404  return !mp_info_ || mp_info_->is_host;
405 }
406 
408 {
409  gui_->get_chat_manager().add_chat_message(time(nullptr), "", 0,
410  _("This side is in an idle state. To proceed with the game, it must be assigned to another controller. You may use :droid, :control or :give_control for example."),
412 }
413 
415 {
416  // mouse_handler expects at least one team for linger mode to work.
417  assert(is_regular_game_end());
418  if (!get_end_level_data_const().transient.linger_mode || gamestate().board_.teams().empty() || gui_->video().faked()) {
419  const bool has_next_scenario = !gamestate().gamedata_.next_scenario().empty() && gamestate().gamedata_.next_scenario() != "null";
420  if(!is_host() && has_next_scenario) {
421  // If we continue without lingering we need to
422  // make sure the host uploads the next scenario
423  // before we attempt to download it.
424  wait_for_upload();
425  }
426  } else {
427  linger();
428  }
429 }
430 
432 {
433  // when using a remote user choice undoing must be impossible because that network traffic cannot be undone
434  // Also turn_data_.sync_network() (which calls turn_data_.send_data()) won't work if the undo stack isn't empty because undoable moves won't be sended
435  // Also undo_stack()clear() must be called synced so we cannot do that here.
436  assert(!current_team().is_local() || !undo_stack().can_undo());
438  assert(res != turn_info::PROCESS_END_LINGER);
439  assert(res != turn_info::PROCESS_END_TURN);
441  {
442  player_type_changed_ = true;
443  }
444 }
445 
447 {
448  // when using a remote user choice undoing must be impossible because that network traffic cannot be undone
449  // Also turn_data_.send_data() won't work if the undo stack isn't empty because undoable moves won't be sended
450  // Also undo_stack()clear() must be called synced so we cannot do that here.
451  assert(!undo_stack().can_undo());
453 }
454 
456 {
458  return;
459  }
461  config cfg;
462  if(!resources::recorder->at_end()) {
464  }
465  else if(network_reader_.read(cfg)) {
466  res = turn_data_.process_network_data(cfg);
467  }
468 
469  if (res == turn_info::PROCESS_RESTART_TURN) {
470  player_type_changed_ = true;
471  }
472  else if (res == turn_info::PROCESS_END_TURN) {
474  }
475  else if (res == turn_info::PROCESS_END_LEVEL) {
476  }
477  else if (res == turn_info::PROCESS_END_LINGER) {
478  replay::process_error("Received unexpected next_scenario during the game");
479  }
480 }
482 {
483  return mp_info_ != nullptr;
484 }
485 
487 {
488  if (mp_info_ != nullptr) {
490  }
491 }
493 {
494  if (mp_info_ != nullptr) {
496  }
497  else {
498  return false;
499  }
500 }
bool disable_auto_moves()
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:226
void set_all_units_user_end_turn()
Definition: game_board.cpp:85
static bool run_and_store(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
events::generic_event & host_transfer()
Definition: playturn.hpp:60
void turn_changed(const std::string &player_name)
const end_level_data & get_end_level_data_const() const
void add_countdown_update(int value, int team)
Definition: replay.cpp:233
static lg::log_domain log_engine("engine")
GLint level
Definition: glew.h:1220
void set_clip_rect(const SDL_Rect &r)
virtual void handle_generic_event(const std::string &name)
logger & info()
Definition: log.cpp:91
bool is_networked_mp() const override
Definition: video.hpp:58
void send_to_wesnothd(const config &cfg, const std::string &packet_type="unknown") const override
virtual void play_linger_turn()
bool save_game_interactive(CVideo &video, const std::string &message, gui::DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:366
bool receive_data(config &result)
PROCESS_DATA_RESULT sync_network()
Definition: playturn.cpp:64
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:362
And endturn was required eigher by the player, by the ai or by [end_turn].
void play_slice(bool is_delay_enabled=true)
void set_countdown_time(const int amount) const
Definition: team.hpp:216
boost::scoped_ptr< hotkey_handler > hotkey_handler_
REPLAY_RETURN do_replay(bool one_move)
Definition: replay.cpp:659
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
PROCESS_DATA_RESULT process_network_data_from_reader()
Definition: playturn.cpp:111
no linger overlay, show fog and shroud.
void set_source(source_type source)
void send_data()
Definition: playturn.cpp:83
static config get_update_shroud()
Records that the player has manually updated fog/shroud.
PROCESS_DATA_RESULT
Definition: playturn.hpp:37
virtual void play_side_impl()
void send_data(const configr_of &request)
-file pathfind.hpp
saved_game & saved_game_
void end_turn_enable(bool enable)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
void throw_quit_game_exception()
void wait_for_upload()
Wait for the host to upload the next scenario.
int current_side() const
Returns the number of the side whose turn it is.
bool is_regular_game_end() const
void set_lifetime(int lifetime)
We received invalid data from wesnothd during a game This means we cannot continue with the game but ...
virtual void play_human_turn()
virtual void do_idle_notification()
Will handle sending a networked notification in descendent classes.
void unblind()
Definition: display.hpp:1193
void set_color(const SDL_Color &color)
mp_campaign_info * mp_info_
config & add_child(const std::string &key)
Definition: config.cpp:743
virtual void play_idle_loop()
void set_position(double xpos, double ypos)
replay * recorder
Definition: resources.cpp:30
twesnothd_connection & wesnothd_connection
GLuint color
Definition: glew.h:5801
static bool quit()
Shows the quit confirmation if needed.
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
static void process_error(const std::string &msg)
Definition: replay.cpp:198
bool network_receive_dialog(CVideo &video, const std::string &msg, config &cfg, twesnothd_connection &wesnothd_connection)
Definition: dialogs.cpp:1179
GLuint res
Definition: glew.h:9258
An extension of playsingle_controller::hotkey_handler, which has support for MP wesnoth features like...
virtual bool attach_handler(observer *obs)
PROCESS_DATA_RESULT process_network_data(const config &cfg)
Definition: playturn.cpp:126
When the host uploaded the next scenario this is returned.
Definition: playturn.hpp:43
#define LOG_NG
We foudn a player action in the replay that caused the game to end.
Definition: playturn.hpp:47
void process_oos(const std::string &err_msg) const
Asks the user whether to continue on an OOS error.
Game configuration data as global variables.
Definition: build_info.cpp:38
const std::string & next_scenario() const
Definition: game_data.hpp:89
Exception used to signal that the user has decided to abort a game, and to load another game instead...
Definition: game_errors.hpp:62
size_t i
Definition: function.cpp:1057
int add_floating_label(const floating_label &flabel)
add a label floating on the screen above everything else.
static source_type get_source_from_config(config &src)
game_data gamedata_
Definition: game_state.hpp:48
actions::undo_list & undo_stack()
static void save(LexState *ls, int c)
Definition: llex.cpp:51
GLuint const GLchar * name
Definition: glew.h:1782
virtual void on_not_observer()
virtual void after_human_turn()
playmp_controller(const config &level, saved_game &state_of_game, const config &game_config, const tdata_cache &tdata, CVideo &video, mp_campaign_info *mp_info)
int countdown_time() const
Definition: team.hpp:215
game_state & gamestate()
virtual void handle_generic_event(const std::string &name)
playturn_network_adapter network_reader_
game_board board_
Definition: game_state.hpp:49
Various functions that implement the undoing (and redoing) of in-game commands.
void set_action_bonus_count(const int count)
Definition: team.hpp:219
Standard logging facilities (interface).
virtual bool detach_handler(observer *obs)
static PROCESS_DATA_RESULT replay_to_process_data_result(REPLAY_RETURN replayreturn)
Definition: playturn.cpp:382
boost::scoped_ptr< game_display > gui_
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
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
virtual bool should_return_to_play_side()
int action_bonus_count() const
Definition: team.hpp:218
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:58
virtual void play_network_turn()
Will handle networked turns in descendent classes.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
bool recieve_from_wesnothd(config &cfg) const override
bool can_undo() const
An [end_turn] was added to the replay.