bool synced_context::can_undo() { //this method should only works in a synced context. assert(is_synced()); //if we called the rng or if we sended data of this action over the network already, undoing is impossible. return (!is_simultaneously_) && (random_new::generator->get_random_calls() == 0); }
void calculate_conversion(void) { for(i=0; (i < MAX_ENTRIES) && (table[i].state != ENTRY_FULL); ++i); if (i >= MAX_ENTRIES) return; // table is empty local_average = table[i].local_time; offset_average = table[i].time_offset; local_sum = 0; offset_sum = 0; while (++i < MAX_ENTRIES) { if (table[i].state == ENTRY_FULL) { local_sum += (long)(table[i].local_time - local_average) / table_entries; offset_sum += (long)(table[i].time_offset - offset_average) / table_entries; } } local_average += local_sum; offset_average += offset_sum; local_sum = offset_sum = 0; for (i=0; i < MAX_ENTRIES; ++i) { if (table[i].state == ENTRY_FULL) { a = table[i].local_time - local_average; b = table[i].time_offset - offset_average; local_sum += (long long)a * a; offset_sum += (long long)a * b; } } if (local_sum != 0) skew = (float)offset_sum / (float)local_sum; num_entries = table_entries; PRINTF("FTSP conversion calculated: num_entries=%u, is_synced=%u\n", num_entries, is_synced()); }
int synced_context::get_unit_id_diff() { //this method only works in a synced context. assert(is_synced()); return resources::gameboard->unit_id_manager().get_save_id() - last_unit_id_; }
config synced_context::ask_server_choice(const server_choice& sch) { if (!is_synced()) { ERR_REPLAY << "Trying to ask the server for a '" << sch.name() << "' choice in a unsynced context, doing the choice locally. This can cause OOS.\n"; return sch.local_choice(); } set_is_simultaneously(); resources::controller->increase_server_request_number(); const bool is_mp_game = resources::controller->is_networked_mp(); bool did_require = false; DBG_REPLAY << "ask_server for random_seed\n"; /* as soon as random or similar is involved, undoing is impossible. */ resources::undo_stack->clear(); /* there might be speak or similar commands in the replay before the user input. */ while(true) { do_replay_handle(); bool is_replay_end = resources::recorder->at_end(); if (is_replay_end && !is_mp_game) { /* The decision is ours, and it will be inserted into the replay. */ DBG_REPLAY << "MP synchronization: local server choice\n"; leave_synced_context sync; config cfg = sch.local_choice(); //-1 for "server" todo: change that. resources::recorder->user_input(sch.name(), cfg, -1); return cfg; } else if(is_replay_end && is_mp_game) { DBG_REPLAY << "MP synchronization: remote server choice\n"; //here we can get into the situation that the decision has already been made but not received yet. synced_context::pull_remote_user_input(); //FIXME: we should call play_controller::play_silce or the application will freeze while waiting for a remote choice. resources::controller->play_slice(); /* we don't want to send multiple "require_random" to the server. */ if(!did_require) { sch.send_request(); did_require = true; } SDL_Delay(10); continue; } else if (!is_replay_end) { /* The decision has already been made, and must be extracted from the replay. */ DBG_REPLAY << "MP synchronization: replay server choice\n"; do_replay_handle(); const config *action = resources::recorder->get_next_action(); if (!action) { replay::process_error("[" + std::string(sch.name()) + "] expected but none found\n"); resources::recorder->revert_action(); return sch.local_choice(); } if (!action->has_child(sch.name())) { replay::process_error("[" + std::string(sch.name()) + "] expected but none found, found instead:\n " + action->debug() + "\n"); resources::recorder->revert_action(); return sch.local_choice(); } if((*action)["from_side"].str() != "server" || (*action)["side_invalid"].to_bool(false) ) { //we can proceed without getting OOS in this case, but allowing this would allow a "player chan choose their attack results in mp" cheat replay::process_error("wrong from_side or side_invalid this could mean someone wants to cheat\n"); } return action->child(sch.name()); } } }
void add_new_entry(void) { free_item = -1; oldest_item = 0; age = 0; oldest_time = 0; table_entries = 0; time_error = (long)(local_to_global(msg_rcv.local_time) - msg_rcv.global_time); if (is_synced() == FTSP_OK) { PRINTF("FTSP synced, error %ld\n", time_error); if ((time_error > ENTRY_THROWOUT_LIMIT) || (-time_error > ENTRY_THROWOUT_LIMIT)) { PRINTF("(big)\n"); if (++num_errors > 3) { PRINTF("FTSP: num_errors > 3 => clear_table()\n"); clear_table(); } } else { PRINTF("(small)\n"); num_errors = 0; } } else { PRINTF("FTSP not synced\n"); } for (i=0; i < MAX_ENTRIES; ++i) { age = msg_rcv.local_time - table[i].local_time; if (age >= 0x7FFFFFFFL) table[i].state = ENTRY_EMPTY; if (table[i].state == ENTRY_EMPTY) free_item = i; else ++table_entries; if (age >= oldest_time) { oldest_time = age; oldest_item = i; } } if (free_item < 0) free_item = oldest_item; else ++table_entries; table[free_item].state = ENTRY_FULL; table[free_item].local_time = msg_rcv.local_time; table[free_item].time_offset = msg_rcv.global_time - msg_rcv.local_time; }