mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-02 04:35:45 +00:00
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:
parent
6e32189599
commit
cf865499b9
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user