Make new turn, turn X, side turn and turn refresh synchronous (bug #10603)

This commit is contained in:
Pauli Nieminen 2009-05-25 10:44:00 +00:00
parent edb35a09ff
commit e18bba4b73
12 changed files with 102 additions and 47 deletions

View file

@ -9,6 +9,8 @@ Version 1.7.0+svn:
* Improved the layout algorithm not to show scrollbars when they make the
situation worse
* Add a new transient message dialog
* Engine:
* Make new turn, turn X, side turn and turn refresh synchronous (fr #10603)
* Miscellaneous and bugfixes:
* Add strict compilation to cmake
* Let cmake also use the CXXFLAGS and CFLAGS environment variables

View file

@ -41,6 +41,7 @@
#include "terrain_filter.hpp"
#include "unit_display.hpp"
#include "wml_exception.hpp"
#include "play_controller.hpp"
#include <boost/scoped_ptr.hpp>
#include <algorithm>
@ -1455,6 +1456,13 @@ WML_HANDLER_FUNCTION(set_variable, /*event_info*/, cfg)
// Otherwise get the random value from the replay data
else {
const game_events::resources_t &rsrc = *game_events::resources;
/** @todo FIXME: get player_number_ from the play_controller, not from the WML vars. */
const t_string& side_str = rsrc.state_of_game->get_variable("side_number");
const int side = lexical_cast_default<int>(side_str.base_str(), -1);
do_replay_handle(*rsrc.screen, *rsrc.game_map, *rsrc.units, *rsrc.teams,
side , *rsrc.status_ptr, *rsrc.state_of_game, *rsrc.controller, "random_number");
const config* const action = get_replay_source().get_next_action();
if(action == NULL || action->get_children("random_number").empty()) {
replay::throw_error("random_number expected but none found\n");
@ -3254,7 +3262,7 @@ WML_HANDLER_FUNCTION(message, event_info, cfg)
if(!options.empty()) {
do_replay_handle(*rsrc.screen, *rsrc.game_map, *rsrc.units, *rsrc.teams,
side , *rsrc.status_ptr, *rsrc.state_of_game, "choose");
side , *rsrc.status_ptr, *rsrc.state_of_game, *rsrc.controller, "choose");
const config* action = get_replay_source().get_next_action();
if (!action || !*(action = &action->child("choose"))) {
replay::throw_error("choice expected but none found\n");
@ -3264,7 +3272,7 @@ WML_HANDLER_FUNCTION(message, event_info, cfg)
}
if(has_text_input) {
do_replay_handle(*rsrc.screen, *rsrc.game_map, *rsrc.units, *rsrc.teams,
side , *rsrc.status_ptr, *rsrc.state_of_game, "input");
side , *rsrc.status_ptr, *rsrc.state_of_game, *rsrc.controller, "input");
const config* action = get_replay_source().get_next_action();
if (!action || !*(action = &action->child("input"))) {
replay::throw_error("input expected but none found\n");
@ -3708,7 +3716,8 @@ namespace game_events {
manager::manager(const config& cfg, gamemap& map_,
unit_map& units_,
std::vector<team>& teams_,
game_state& state_of_game_, gamestatus& status)
game_state& state_of_game_, gamestatus& status,
play_controller& controller)
: resources()
, variable_manager(&state_of_game_)
{
@ -3728,6 +3737,7 @@ namespace game_events {
resources.state_of_game = &state_of_game_;
resources.status_ptr = &status;
resources.lua_kernel = new LuaKernel;
resources.controller = &controller;
game_events::resources = &resources;
manager_running = true;
@ -3823,6 +3833,8 @@ namespace game_events {
if(!events_init())
return;
LOG_NG << "fire event: " << event << "\n";
events_queue.push_back(game_events::queued_event(event,loc1,loc2,data));
}

View file

@ -31,6 +31,7 @@ class team;
class t_string;
class unit;
class LuaKernel;
class play_controller;
/**
* @file game_events.hpp
@ -58,6 +59,7 @@ namespace game_events
game_state *state_of_game;
gamestatus *status_ptr;
LuaKernel *lua_kernel;
play_controller *controller;
};
extern const resources_t *resources;
@ -73,7 +75,8 @@ namespace game_events
// and must remain valid for the life of the object.
manager(const config& scenario_cfg, gamemap& map,
unit_map& units, std::vector<team>& teams,
game_state& state_of_game, gamestatus& status);
game_state& state_of_game, gamestatus& status,
play_controller& controller);
~manager();
void set_gui(game_display&);
void set_soundsource(soundsource::manager&);

View file

@ -70,7 +70,7 @@ play_controller::play_controller(const config& level, game_state& state_of_game,
is_host_(true),
skip_replay_(skip_replay),
linger_(false),
first_turn_(true),
previous_turn_(0),
savenames_(),
wml_commands_(),
victory_music_(),
@ -128,7 +128,7 @@ void play_controller::init(CVideo& video){
// as that functions use the manager state_of_game
// Has to be done before registering any events!
events_manager_.reset(new game_events::manager(level_,map_,
units_,teams_, gamestate_,status_));
units_,teams_, gamestate_,status_,*this));
std::set<std::string> seen_save_ids;
@ -397,9 +397,8 @@ void play_controller::fire_start(bool execute){
// start event may modify start turn with WML, reflect any changes.
start_turn_ = status_.turn();
gamestate_.set_variable("turn_number", str_cast<size_t>(start_turn_));
first_turn_ = true;
} else {
first_turn_ = false;
previous_turn_ = status_.turn();
}
}
@ -412,7 +411,7 @@ void play_controller::init_gui(){
}
}
void play_controller::init_side(const unsigned int team_index, bool /*is_replay*/){
void play_controller::init_side(const unsigned int team_index, bool is_replay){
log_scope("player turn");
team& current_team = teams_[team_index];
@ -430,21 +429,37 @@ void play_controller::init_side(const unsigned int team_index, bool /*is_replay*
gamestate_.set_variable("side_number",player_number_str.str());
gamestate_.last_selected = map_location::null_location;
/*
Normally, events must not be actively fired through replays, because
they have been recorded previously and therefore will get executed anyway.
Firing them in the normal code would lead to double execution.
However, the following events are different in that they need to be executed _now_
(before calculation of income and healing) or we will risk OOS errors if we manipulate
these informations inside the events and in the replay have a different order of execution.
*/
/**
* We do this only for local side when we are not replaying.
* For all other sides it is recorded in replay and replay handler has to handle calling do_init_side()
* functions.
**/
if (!current_team.is_local()
|| is_replay)
return;
recorder.init_side();
do_init_side(team_index);
}
/**
* Called by replay handler or init_side() to do actual work for turn change.
*/
void play_controller::do_init_side(const unsigned int team_index){
log_scope("player turn");
team& current_team = teams_[team_index];
bool real_side_change = true;
if(first_turn_) {
first_turn_ = false;
game_events::fire("turn " + str_cast<size_t>(start_turn_));
game_events::fire("new turn");
game_events::fire("side turn");
} else if (int(team_index) + 1 != first_player_ || status_.turn() > start_turn_) {
if (!loading_game_ || int(team_index) + 1 != first_player_ || status_.turn() > start_turn_) {
if (status_.turn() != previous_turn_)
{
std::stringstream event_stream;
event_stream << status_.turn();
const std::string turn_num = event_stream.str();
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
previous_turn_ = status_.turn();
}
// Fire side turn event only if real side change occurs,
// not counting changes from void to a side
game_events::fire("side turn");
@ -529,8 +544,6 @@ void play_controller::finish_turn(){
update_locker lock_display(gui_->video(),recorder.is_skipping());
const std::string turn_num = event_stream.str();
gamestate_.set_variable("turn_number",turn_num);
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
}

View file

@ -73,6 +73,7 @@ public:
virtual void toggle_grid();
virtual void search();
virtual void do_init_side(const unsigned int team_index);
virtual void play_side(const unsigned int team_num, bool save) = 0;
protected:
@ -155,7 +156,7 @@ protected:
bool is_host_;
bool skip_replay_;
bool linger_;
bool first_turn_;
unsigned int previous_turn_;
const std::string& select_victory_music() const;
const std::string& select_defeat_music() const;

View file

@ -149,7 +149,7 @@ void playmp_controller::before_human_turn(bool save){
playsingle_controller::before_human_turn(save);
turn_data_ = new turn_info(gamestate_,status_,
*gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_);
*gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_, *this);
turn_data_->replay_error().attach_handler(this);
turn_data_->host_transfer().attach_handler(this);
}
@ -344,7 +344,7 @@ void playmp_controller::linger(upload_log& log)
player_number_ = first_player_;
turn_data_ = new turn_info(gamestate_, status_,
*gui_,map_, teams_, player_number_,
units_, replay_sender_, undo_stack_);
units_, replay_sender_, undo_stack_,*this);
turn_data_->replay_error().attach_handler(this);
turn_data_->host_transfer().attach_handler(this);
@ -389,7 +389,7 @@ void playmp_controller::wait_for_upload()
const bool set_turn_data = (turn_data_ == 0);
if(set_turn_data) {
turn_data_ = new turn_info(gamestate_,status_,
*gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_);
*gui_,map_,teams_,player_number_,units_,replay_sender_, undo_stack_, *this);
turn_data_->replay_error().attach_handler(this);
turn_data_->host_transfer().attach_handler(this);
}
@ -468,7 +468,7 @@ void playmp_controller::play_network_turn(){
gui_->enable_menu("endturn", false);
turn_info turn_data(gamestate_,status_,*gui_,
map_,teams_,player_number_,units_, replay_sender_, undo_stack_);
map_,teams_,player_number_,units_, replay_sender_, undo_stack_,*this);
turn_data.replay_error().attach_handler(this);
turn_data.host_transfer().attach_handler(this);
@ -544,7 +544,7 @@ void playmp_controller::process_oos(const std::string& err_msg){
void playmp_controller::handle_generic_event(const std::string& name){
turn_info turn_data(gamestate_,status_,*gui_,
map_,teams_,player_number_,units_, replay_sender_, undo_stack_);
map_,teams_,player_number_,units_, replay_sender_, undo_stack_, *this);
if (name == "ai_user_interact"){
playsingle_controller::handle_generic_event(name);

View file

@ -549,7 +549,7 @@ void playsingle_controller::play_turn(bool save)
} catch (end_turn_exception) {
if (current_team().is_network() == false) {
turn_info turn_data(gamestate_, status_, *gui_, map_,
teams_, player_number_, units_, replay_sender_, undo_stack_);
teams_, player_number_, units_, replay_sender_, undo_stack_,*this);
recorder.end_turn();
turn_data.sync_network();
}
@ -560,7 +560,7 @@ void playsingle_controller::play_turn(bool save)
LOG_NG << "doing replay " << player_number_ << "\n";
try {
replaying_ = ::do_replay(*gui_, map_, units_, teams_,
player_number_, status_, gamestate_);
player_number_, status_, gamestate_, *this);
} catch(replay::error&) {
gui2::show_transient_message(gui_->video(),"",_("The file you have tried to load is corrupt"));
@ -572,7 +572,7 @@ void playsingle_controller::play_turn(bool save)
if (current_team().is_human() && side_units(units_, player_number_) == 0)
{
turn_info turn_data(gamestate_, status_, *gui_, map_,
teams_, player_number_, units_, replay_sender_, undo_stack_);
teams_, player_number_, units_, replay_sender_, undo_stack_, *this);
recorder.end_turn();
turn_data.sync_network();
continue;
@ -831,7 +831,7 @@ void playsingle_controller::play_ai_turn(){
const cursor::setter cursor_setter(cursor::WAIT);
turn_info turn_data(gamestate_,status_,*gui_,
map_, teams_, player_number_, units_, replay_sender_, undo_stack_);
map_, teams_, player_number_, units_, replay_sender_, undo_stack_, *this);
try {
ai_manager::play_turn(player_number_, this);

View file

@ -23,6 +23,7 @@
#include "log.hpp"
#include "replay.hpp"
#include "formula_string_utils.hpp"
#include "play_controller.hpp"
static lg::log_domain log_network("network");
#define ERR_NW LOG_STREAM(err, log_network)
@ -30,13 +31,20 @@ static lg::log_domain log_network("network");
turn_info::turn_info(game_state& state_of_game,
const gamestatus& status, game_display& gui, gamemap& map,
std::vector<team>& teams, unsigned int team_num, unit_map& units,
replay_network_sender& replay_sender, undo_list& undo_stack)
replay_network_sender& replay_sender, undo_list& undo_stack, play_controller& controller)
: state_of_game_(state_of_game), status_(status),
gui_(gui), map_(map), teams_(teams), team_num_(team_num),
units_(units), undo_stack_(undo_stack),
replay_sender_(replay_sender), replay_error_("network_replay_error"),
host_transfer_("host_transfer")
{}
host_transfer_("host_transfer"),
controller_(controller)
{
/**
* We do network sync so [init_side] is transfered to network hosts
*/
if(network::nconnections() > 0)
send_data();
}
turn_info::~turn_info(){
undo_stack_.clear();
@ -118,7 +126,7 @@ turn_info::PROCESS_DATA_RESULT turn_info::process_network_data(const config& cfg
try{
turn_end = do_replay(gui_, map_, units_, teams_,
team_num_, status_, state_of_game_, &replay_obj);
team_num_, status_, state_of_game_, controller_, &replay_obj);
}
catch (replay::error& e){
//notify remote hosts of out of sync error

View file

@ -21,6 +21,7 @@ class game_state;
class replay_network_sender;
class team;
class unit;
class play_controller;
#include "global.hpp"
@ -37,7 +38,7 @@ public:
turn_info(game_state& state_of_game,
const gamestatus& status, game_display& gui, gamemap& map,
std::vector<team>& teams, unsigned int team_num, unit_map& units,
replay_network_sender& network_sender, undo_list& undo_stack);
replay_network_sender& network_sender, undo_list& undo_stack, play_controller& controller);
~turn_info();
@ -82,6 +83,7 @@ private:
events::generic_event replay_error_;
events::generic_event host_transfer_;
play_controller &controller_;
};
#endif

View file

@ -32,6 +32,7 @@
#include "log.hpp"
#include "map.hpp"
#include "map_label.hpp"
#include "play_controller.hpp"
#include "replay.hpp"
#include "statistics.hpp"
#include "unit_display.hpp"
@ -359,6 +360,12 @@ void replay::add_rename(const std::string& name, const map_location& loc)
cmd->add_child("rename", val);
}
void replay::init_side()
{
config* const cmd = add_command();
cmd->add_child("init_side");
}
void replay::end_turn()
{
config* const cmd = add_command();
@ -731,7 +738,7 @@ static void check_checksums(game_display& disp,const unit_map& units,const confi
bool do_replay(game_display& disp, const gamemap& map,
unit_map& units, std::vector<team>& teams, int team_num,
const gamestatus& state, game_state& state_of_game, replay* obj)
const gamestatus& state, game_state& state_of_game, play_controller& controller, replay* obj)
{
log_scope("do replay");
@ -746,13 +753,13 @@ bool do_replay(game_display& disp, const gamemap& map,
const rand_rng::set_random_generator generator_setter(&get_replay_source());
update_locker lock_update(disp.video(),get_replay_source().is_skipping());
return do_replay_handle(disp, map, units, teams, team_num, state, state_of_game,
return do_replay_handle(disp, map, units, teams, team_num, state, state_of_game, controller,
std::string(""));
}
bool do_replay_handle(game_display& disp, const gamemap& map,
unit_map& units, std::vector<team>& teams, int team_num,
const gamestatus& state, game_state& state_of_game,
const gamestatus& state, game_state& state_of_game, play_controller& controller,
const std::string& do_untill)
{
//a list of units that have promoted from the last attack
@ -880,6 +887,11 @@ bool do_replay_handle(game_display& disp, const gamemap& map,
}
}
else if (cfg->child("init_side"))
{
controller.do_init_side(team_num - 1);
}
//if there is an end turn directive
else if (cfg->child("end_turn"))
{

View file

@ -29,6 +29,7 @@ class config_writer;
class game_display;
class terrain_label;
class unit_map;
class play_controller;
struct verification_manager
{
@ -59,6 +60,7 @@ public:
void add_label(const terrain_label*);
void clear_labels(const std::string&);
void add_rename(const std::string& name, const map_location& loc);
void init_side();
void end_turn();
void add_event(const std::string& name,
const map_location& loc=map_location::null_location);
@ -153,11 +155,11 @@ extern replay recorder;
//returns true if it got to the end of the turn without data running out
bool do_replay(game_display& disp, const gamemap& map,
unit_map& units, std::vector<team>& teams, int team_num,
const gamestatus& state, game_state& state_of_game, replay* obj=NULL);
const gamestatus& state, game_state& state_of_game, play_controller& controller, replay* obj=NULL);
bool do_replay_handle(game_display& disp, const gamemap& map,
unit_map& units, std::vector<team>& teams, int team_num,
const gamestatus& state, game_state& state_of_game,
const gamestatus& state, game_state& state_of_game, play_controller& controller,
const std::string& do_untill);
//an object which can be made to undo a recorded move

View file

@ -177,7 +177,7 @@ void replay_controller::reset_replay(){
// failure)
events_manager_.reset();
events_manager_.reset(new game_events::manager(level_,map_,
units_,teams_, gamestate_,status_));
units_,teams_, gamestate_,status_, *this));
events_manager_->set_gui(*gui_);
events_manager_->set_soundsource(*soundsources_manager_);
}
@ -338,7 +338,7 @@ void replay_controller::play_side(const unsigned int /*team_index*/, bool){
DBG_REPLAY << "doing replay " << player_number_ << "\n";
try {
::do_replay(*gui_, map_, units_, teams_,
player_number_, status_, gamestate_);
player_number_, status_, gamestate_, *this);
} catch(replay::error&) {
if(!continue_replay()) {
throw;