From db47ced634bec2c2322e318340b0781fcabd05d7 Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Tue, 10 Aug 2010 17:16:22 +0000 Subject: [PATCH] Allowed [message] in start events for single player mode. To avoid replay issues, it forcefully quits the game in multiplayer instead of displaying a WML error and continuing. --- src/game_events.cpp | 2 +- src/replay.cpp | 17 +++++++++++++++-- src/replay.hpp | 33 +++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/game_events.cpp b/src/game_events.cpp index bff17d6e24e..60c0d585cb4 100644 --- a/src/game_events.cpp +++ b/src/game_events.cpp @@ -2763,7 +2763,7 @@ WML_HANDLER_FUNCTION(message, event_info, cfg) } else { - config choice = mp_sync::get_user_choice("input", msg); + config choice = mp_sync::get_user_choice("input", msg, 0, true); option_chosen = choice["value"]; text_input_result = choice["text"].str(); } diff --git a/src/replay.cpp b/src/replay.cpp index 5e991215113..3e629d930d3 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -1201,9 +1201,22 @@ void replay_network_sender::commit_and_sync() } } -config mp_sync::get_user_choice(const std::string &name, const user_choice &uch, int side) +config mp_sync::get_user_choice(const std::string &name, const user_choice &uch, + int side, bool force_sp) { - if (resources::state_of_game->phase() == game_state::PLAY) + if (force_sp && network::nconnections() != 0 && + resources::state_of_game->phase() != game_state::PLAY) + { + /* We are in a multiplayer game, during an early event which + prevents synchronization, and the WML is not interested + in a random result. We cannot silently ignore the issue, + since it would lead to a broken replay. To be sure that + the WML does not catch the error and keep the game going, + we use a sticky exception to forcefully quit. */ + ERR_REPLAY << "MP synchronization does not work during prestart and start events."; + throw end_level_exception(QUIT); + } + if (resources::state_of_game->phase() == game_state::PLAY || force_sp) { /* We have to communicate with the player and store the choices in the replay. So a decision will be made on diff --git a/src/replay.hpp b/src/replay.hpp index 94165e33326..fc17b6ac4e7 100644 --- a/src/replay.hpp +++ b/src/replay.hpp @@ -213,23 +213,32 @@ struct user_choice /** * Performs a choice for WML events. + * * The choice is synchronized across all the multiplayer clients and * stored into the replay. The function object is called if the local * client is responsible for making the choice. - * @param name tag used for storing the choice into the replay. - * @param side the number of the side responsible for making the choice. - * - defaults to currently active side. + * @param name Tag used for storing the choice into the replay. + * @param side The number of the side responsible for making the choice. + * If zero, it defaults to the currently active side. + * @param force_sp If true, user choice will happen in prestart and start + * events too. But if used for these events in multiplayer, + * an exception will be thrown instead. * - * @note In order to prevent issues with sync, crash, or infinite loop, a number of precautions - * must be taken when getting a choice from a specific side. - * - The calling function must enter a loop to wait for network sync if the side is non-local. - * - This loop must end when a response is received in the replay. - * - The server must recognize @name replay commands as legal from non-active players. - * - Preferably the server should be notified about which player the data is expected from, - * and discard data from unexpected players. - * - do_replay_handle must ignore the @name replay command when the originating player's turn is reached. + * @note In order to prevent issues with sync, crash, or infinite loop, a + * number of precautions must be taken when getting a choice from a + * specific side. + * - The calling function must enter a loop to wait for network sync + * if the side is non-local. This loop must end when a response is + * received in the replay. + * - The server must recognize @name replay commands as legal from + * non-active players. Preferably the server should be notified + * about which player the data is expected from, and discard data + * from unexpected players. + * - do_replay_handle must ignore the @name replay command when the + * originating player's turn is reached. */ -config get_user_choice(const std::string &name, const user_choice &uch, int side = 0); +config get_user_choice(const std::string &name, const user_choice &uch, + int side = 0, bool force_sp = false); }