Skip to content

Commit

Permalink
fix #3532 : oos error in mp campaigns.
Browse files Browse the repository at this point in the history
previously it would cause oos when a player that has not yet advanced to
the currrent scenario sends random seed requests.
  • Loading branch information
gfgtdf committed Sep 11, 2018
1 parent 6806bc4 commit 8650889
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 2 deletions.
27 changes: 25 additions & 2 deletions src/server/game.cpp
Expand Up @@ -117,6 +117,7 @@ game::game(player_connections& player_connections,
, num_turns_(0)
, all_observers_muted_(false)
, bans_()
, players_not_advanced_()
, termination_()
, save_replays_(save_replays)
, replay_save_path_(replay_save_path)
Expand Down Expand Up @@ -1228,16 +1229,24 @@ void game::handle_controller_choice(const simple_wml::node& req)

void game::handle_choice(const simple_wml::node& data, const socket_ptr& user)
{

if(!started_) {
return;
}

// note, that during end turn events, it's side=1 for the server but side= side_count() on the clients.

// Otherwise we allow observers to cause OOS for the playing clients by sending
// server choice requests based on incompatible local changes. To solve this we block
// server choice requests from observers.
if(!started_) {
if(user != owner_ && !is_player(user)) {
return;
}

if(user != owner_ && !is_player(user)) {
// since we reset the last_choice_request_id_ when a new scenario is loaded,
// the code would otherwise wrongly accept these requests from client in old
// scenarios. which would result on oos.
if(players_not_advanced_.find(user) != players_not_advanced_.end()) {
return;
}

Expand Down Expand Up @@ -1465,6 +1474,7 @@ bool game::remove_player(const socket_ptr& player, const bool disconnect, const

players_.erase(std::remove(players_.begin(), players_.end(), player), players_.end());
observers_.erase(std::remove(observers_.begin(), observers_.end(), player), observers_.end());
players_not_advanced_.erase(player);

const bool game_ended = players_.empty() || (host && !started_);

Expand Down Expand Up @@ -1591,6 +1601,17 @@ void game::send_user_list(const socket_ptr& exclude) const
send_data(cfg, exclude);
}

void game::new_scenario(const socket_ptr& sender)
{
assert(sender == owner_);
players_not_advanced_.clear();
for(const socket_ptr& user_ptr : all_game_users()) {
if(user_ptr != sender) {
players_not_advanced_.insert(user_ptr);
}
}
}

void game::load_next_scenario(const socket_ptr& user)
{
send_server_message_to_all(player_connections_.find(user)->info().name() + " advances to the next scenario", user);
Expand Down Expand Up @@ -1634,6 +1655,8 @@ void game::load_next_scenario(const socket_ptr& user)
send_to_player(user, cfg_scenario);
send_to_player(user, doc_controllers);

players_not_advanced_.erase(user);

// Send the player the history of the game to-date.
send_history(user);

Expand Down
7 changes: 7 additions & 0 deletions src/server/game.hpp
Expand Up @@ -77,6 +77,9 @@ class game
/** Checks whether the connection's ip address is banned. */
bool player_is_banned(const socket_ptr& player) const;

/** when the host sends the new scenario of a mp campaign */
void new_scenario(const socket_ptr& player);

bool level_init() const
{
return level_.child("snapshot") || level_.child("scenario");
Expand Down Expand Up @@ -500,6 +503,10 @@ class game
bool all_observers_muted_;

std::vector<std::string> bans_;
/// in multiplayer campaigns it can happen that some players are still in the previousl scenario
/// keep track of those players because processing certain
/// input from those side wil lead to error (oos)
std::set<socket_ptr> players_not_advanced_;

std::string termination_;

Expand Down
1 change: 1 addition & 0 deletions src/server/server.cpp
Expand Up @@ -1629,6 +1629,7 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
return;
}

g.new_scenario(socket);
g.save_replay();
g.reset_last_synced_context_id();

Expand Down

0 comments on commit 8650889

Please sign in to comment.