Skip to content

Commit

Permalink
fix host reassignes side during choice
Browse files Browse the repository at this point in the history
Assume the following situation:
2 Players in the game (A (side 1), B (side 2)), and one Observer (C).
it's A's turn.
A has to do a decision (for example unit advanement), A leaves the game
and B decides to assign side 1 to C.
On Client B: When returning from process_network_data side 1 is now controlled by B,
and then gets then reassigned with the next call of
process_network_data, but that's too late becasue handle_generaic_event
already returned and B does the advancement decision for side 1.
Players C gets the the first [change_controller] (A -> B) and then gets
the second change cntroller (B -> C) before getting B's answer to the
user choice. So when C gets the second [change_controller] he does the
decision himself becasue its C's decision an it has not been received
yet. -> the same decision has been made twice -> OOS.
this fixes this problem, by making the host wait for the servers change
controller in this case
  • Loading branch information
gfgtdf committed Apr 16, 2014
1 parent 6e9c647 commit 028c963
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 6 deletions.
42 changes: 37 additions & 5 deletions src/playmp_controller.cpp
Expand Up @@ -192,7 +192,9 @@ void playmp_controller::play_human_turn(){
config cfg;

if(network_reader_.read(cfg)) {
if (turn_data_->process_network_data(cfg, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
turn_info::PROCESS_DATA_RESULT res = turn_data_->process_network_data(cfg, skip_replay_);
//PROCESS_RESTART_TURN_TEMPORARY_LOCAL should be impossible because that's means the currently active side (that's us) left.
if (res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL)
{
// Clean undo stack if turn has to be restarted (losing control)
if ( undo_stack_->can_undo() )
Expand Down Expand Up @@ -273,7 +275,9 @@ void playmp_controller::play_idle_loop()
try {
config cfg;
if(network_reader_.read(cfg)) {
if (turn_data_->process_network_data(cfg, skip_replay_) == turn_info::PROCESS_RESTART_TURN)
turn_info::PROCESS_DATA_RESULT res = turn_data_->process_network_data(cfg, skip_replay_);

if (res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL)
{
throw end_turn_exception(gui_->playing_side());
}
Expand Down Expand Up @@ -483,7 +487,7 @@ void playmp_controller::play_network_turn(){
//we received a player change/quit during waiting in get_user_choice/synced_context::pull_remote_user_input
return;
}
if (result == turn_info::PROCESS_RESTART_TURN) {
if (result == turn_info::PROCESS_RESTART_TURN || result == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL) {
player_type_changed_ = true;
return;
} else if (result == turn_info::PROCESS_END_TURN) {
Expand Down Expand Up @@ -557,12 +561,40 @@ void playmp_controller::handle_generic_event(const std::string& name){
turn_data.send_data();
}
else if ((name == "ai_gamestate_changed") || (name == "ai_sync_network")){
int expected_controller_changes = 0;
turn_info::PROCESS_DATA_RESULT res = turn_data.sync_network();
assert(res == turn_info::PROCESS_CONTINUE || res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_FOUND_DEPENDENT);
if(res == turn_info::PROCESS_RESTART_TURN)
assert(res != turn_info::PROCESS_END_LINGER);
assert(res != turn_info::PROCESS_END_TURN);
if(res == turn_info::PROCESS_RESTART_TURN || res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL )
{
player_type_changed_ = true;
}
if(res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL || res == turn_info::PROCESS_SIDE_TEMPORARY_LOCAL)
{
expected_controller_changes++;
}
//If we still expect controler changes we cannot return.
//Becasue we might get into the situation that we want to do a decision that has already been name on another client.
//FIXME: if the server failed to process a transfer_side this is an infinite loop.
//as a temporary fix we abort the loop if it runs too long.
time_t time_start = time(NULL);
while((expected_controller_changes != 0) && (difftime(time(NULL), time_start) < 20))
{
playsingle_controller::handle_generic_event("ai_user_interact");
res = turn_data.sync_network();
assert(res != turn_info::PROCESS_END_LINGER);
assert(res != turn_info::PROCESS_END_TURN);
if(res == turn_info::PROCESS_RESTART_TURN)
{
expected_controller_changes--;
}
else if(res == turn_info::PROCESS_RESTART_TURN_TEMPORARY_LOCAL || res == turn_info::PROCESS_SIDE_TEMPORARY_LOCAL)
{
expected_controller_changes++;
}
SDL_Delay(10);
}
turn_data.send_data();
}
else if (name == "host_transfer"){
is_host_ = true;
Expand Down
2 changes: 1 addition & 1 deletion src/playturn.cpp
Expand Up @@ -360,7 +360,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
if (have_leader) leader->rename("ai" + side_drop);
change_controller(side_drop, "ai");
}
return restart?PROCESS_RESTART_TURN:PROCESS_CONTINUE;
return restart ? PROCESS_RESTART_TURN_TEMPORARY_LOCAL : PROCESS_SIDE_TEMPORARY_LOCAL;
}
break;
}
Expand Down
4 changes: 4 additions & 0 deletions src/playturn.hpp
Expand Up @@ -36,6 +36,10 @@ class turn_info
{
PROCESS_CONTINUE,
PROCESS_RESTART_TURN,
/** we wanted to reassign the currently active side to a side, and wait for the server to do so.*/
PROCESS_RESTART_TURN_TEMPORARY_LOCAL,
/** we wanted to reassign a non currently active side to a side, and wait for the server to do so.*/
PROCESS_SIDE_TEMPORARY_LOCAL,
PROCESS_END_TURN,
/** When the host uploaded the next scenario this is returned. */
PROCESS_END_LINGER,
Expand Down

0 comments on commit 028c963

Please sign in to comment.