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.
This commit is contained in:
Guillaume Melquiond 2010-08-10 17:16:22 +00:00
parent 97c10409be
commit db47ced634
3 changed files with 37 additions and 15 deletions

View File

@ -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();
}

View File

@ -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

View File

@ -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);
}