fix host reassignes side during choice

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
This commit is contained in:
gfgtdf 2014-04-13 22:53:14 +02:00
parent 6e32189599
commit cf865499b9
3 changed files with 42 additions and 6 deletions

View File

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

View File

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

View File

@ -36,6 +36,10 @@ public:
{
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,