Make AI manager a singleton

Fixes #2372.

It turned out that the AI kept dangling references to the old Lua state,
and crashed while destroying AI contexts for destroyed sides.

The best way to avoid it is to ensure that game_state, which already owns
the Lua state, also owns the AI. That way, the AI will be destroyed before
the Lua state and a dangling reference can't stay.
This commit is contained in:
Jyrki Vesterinen 2018-01-23 23:26:34 +02:00 committed by Charles Dang
parent 671cebe035
commit c8b0833b1a
23 changed files with 184 additions and 154 deletions

View file

@ -161,7 +161,7 @@ bool action_result::is_execution() const
game_info& action_result::get_info() const
{
return manager::get_active_ai_info_for_side(get_side());
return manager::get_singleton().get_active_ai_info_for_side(get_side());
}
team& action_result::get_my_team() const
@ -311,7 +311,7 @@ void attack_result::do_execute()
get_info().recent_attacks.insert(defender_loc_);
//end of ugly hack
try {
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
throw;
@ -510,7 +510,7 @@ void move_result::do_execute()
if (is_gamestate_changed()) {
try {
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
throw;
@ -660,7 +660,7 @@ void recall_result::do_execute()
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
throw;
@ -807,7 +807,7 @@ void recruit_result::do_execute()
set_gamestate_changed();
try {
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
throw;
@ -907,12 +907,12 @@ void stopunit_result::do_execute()
if (remove_movement_){
un->remove_movement_ai();
set_gamestate_changed();
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
}
if (remove_attacks_){
un->remove_attacks_ai();
set_gamestate_changed();
manager::raise_gamestate_changed();//to be on the safe side
manager::get_singleton().raise_gamestate_changed();//to be on the safe side
}
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
@ -972,7 +972,7 @@ void synced_command_result::do_execute()
try {
set_gamestate_changed();
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
} catch (...) {
if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
throw;
@ -1114,7 +1114,7 @@ std::map<int,std::string> actions::error_names_;
void sim_gamestate_changed(action_result *result, bool gamestate_changed){
if(gamestate_changed){
result->set_gamestate_changed();
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
}
}

View file

@ -46,14 +46,15 @@ aspect::aspect(readonly_context &context, const config &cfg, const std::string &
aspect::~aspect()
{
manager& manager = manager::get_singleton();
if (invalidate_on_turn_start_) {
manager::remove_turn_started_observer(this);
manager.remove_turn_started_observer(this);
}
if (invalidate_on_tod_change_) {
manager::remove_tod_changed_observer(this);
manager.remove_tod_changed_observer(this);
}
if (invalidate_on_gamestate_change_) {
manager::remove_gamestate_observer(this);
manager.remove_gamestate_observer(this);
}
if (invalidate_on_minor_gamestate_change_) {
///@todo 1.9 add minor_gamestate_change_observer
@ -72,14 +73,16 @@ void aspect::on_create()
bool aspect::redeploy(const config &cfg, const std::string& /*id*/)
{
manager& manager = manager::get_singleton();
if (invalidate_on_turn_start_) {
manager::remove_turn_started_observer(this);
manager.remove_turn_started_observer(this);
}
if (invalidate_on_tod_change_) {
manager::remove_tod_changed_observer(this);
manager.remove_tod_changed_observer(this);
}
if (invalidate_on_gamestate_change_) {
manager::remove_gamestate_observer(this);
manager.remove_gamestate_observer(this);
}
if (invalidate_on_minor_gamestate_change_) {
///@todo 1.9 add minor_gamestate_change_observer
@ -99,13 +102,13 @@ bool aspect::redeploy(const config &cfg, const std::string& /*id*/)
id_ = cfg["id"].str();
DBG_AI_ASPECT << "redeploying aspect: engine=["<<engine_<<"], name=["<<name_<<"], id=["<<id_<<"]"<< std::endl;
if (invalidate_on_turn_start_) {
manager::add_turn_started_observer(this);
manager.add_turn_started_observer(this);
}
if (invalidate_on_tod_change_) {
manager::add_tod_changed_observer(this);
manager.add_tod_changed_observer(this);
}
if (invalidate_on_gamestate_change_) {
manager::add_gamestate_observer(this);
manager.add_gamestate_observer(this);
}
if (invalidate_on_minor_gamestate_change_) {
///@todo 1.9 add minor_gamestate_change_observer

View file

@ -92,13 +92,13 @@ int readwrite_context_impl::get_recursion_count() const
void readonly_context_impl::raise_user_interact() const
{
manager::raise_user_interact();
manager::get_singleton().raise_user_interact();
}
void readwrite_context_impl::raise_gamestate_changed() const
{
manager::raise_gamestate_changed();
manager::get_singleton().raise_gamestate_changed();
}
@ -227,7 +227,7 @@ readonly_context_impl::readonly_context_impl(side_context &context, const config
villages_per_scout_()
{
init_side_context_proxy(context);
manager::add_gamestate_observer(this);
manager::get_singleton().add_gamestate_observer(this);
add_known_aspect("advancements", advancements_);
add_known_aspect("aggression",aggression_);
@ -305,7 +305,7 @@ config readonly_context_impl::to_readonly_context_config() const
readonly_context_impl::~readonly_context_impl()
{
manager::remove_gamestate_observer(this);
manager::get_singleton().remove_gamestate_observer(this);
}
void readonly_context_impl::handle_generic_event(const std::string& /*event_name*/)
@ -315,12 +315,12 @@ void readonly_context_impl::handle_generic_event(const std::string& /*event_name
const game_info& readonly_context_impl::get_info() const{
return manager::get_active_ai_info_for_side(get_side());
return manager::get_singleton().get_active_ai_info_for_side(get_side());
}
game_info& readwrite_context_impl::get_info_w(){
return manager::get_active_ai_info_for_side(get_side());
return manager::get_singleton().get_active_ai_info_for_side(get_side());
}
void readonly_context_impl::diagnostic(const std::string& msg)
@ -950,15 +950,15 @@ keeps_cache::keeps_cache()
: map_(nullptr)
, keeps_()
{
ai::manager::add_turn_started_observer(this);
ai::manager::add_map_changed_observer(this);
ai::manager::get_singleton().add_turn_started_observer(this);
ai::manager::get_singleton().add_map_changed_observer(this);
}
keeps_cache::~keeps_cache()
{
ai::manager::remove_turn_started_observer(this);
ai::manager::remove_map_changed_observer(this);
ai::manager::get_singleton().remove_turn_started_observer(this);
ai::manager::get_singleton().remove_map_changed_observer(this);
}
void keeps_cache::clear()

View file

@ -139,7 +139,7 @@ void aspect_attacks_base::do_attack_analysis(
{
// This function is called fairly frequently, so interact with the user here.
ai::manager::raise_user_interact();
ai::manager::get_singleton().raise_user_interact();
const int default_attack_depth = 5;
if(cur_analysis.movements.size() >= size_t(default_attack_depth)) {
//std::cerr << "ANALYSIS " << cur_analysis.movements.size() << " >= " << get_attack_depth() << "\n";

View file

@ -262,7 +262,7 @@ void attack_analysis::analyze(const gamemap& map, unit_map& units,
bool attack_analysis::attack_close(const map_location& loc) const
{
std::set<map_location> &attacks = manager::get_ai_info().recent_attacks;
std::set<map_location> &attacks = manager::get_singleton().get_ai_info().recent_attacks;
for(std::set<map_location>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
if(distance_between(*i,loc) < 4) {
return true;

View file

@ -354,7 +354,7 @@ void move_leader_to_goals_phase::remove_goal(const std::string &id)
mod_ai["side"] = get_side();
mod_ai["path"] = "aspect[leader_goal].facet["+id+"]";
mod_ai["action"] = "delete";
manager::modify_active_ai_for_side(get_side(),mod_ai);
manager::get_singleton().modify_active_ai_for_side(get_side(), mod_ai);
}
//==============================================================

View file

@ -1756,8 +1756,8 @@ void recruitment::update_scouts_wanted() {
*/
recruitment::recruit_situation_change_observer::recruit_situation_change_observer()
: recruit_list_changed_(false), gamestate_changed_(0) {
manager::add_recruit_list_changed_observer(this);
manager::add_gamestate_observer(this);
manager::get_singleton().add_recruit_list_changed_observer(this);
manager::get_singleton().add_gamestate_observer(this);
}
void recruitment::recruit_situation_change_observer::handle_generic_event(
@ -1771,8 +1771,8 @@ void recruitment::recruit_situation_change_observer::handle_generic_event(
}
recruitment::recruit_situation_change_observer::~recruit_situation_change_observer() {
manager::remove_recruit_list_changed_observer(this);
manager::remove_gamestate_observer(this);
manager::get_singleton().remove_recruit_list_changed_observer(this);
manager::get_singleton().remove_gamestate_observer(this);
}
bool recruitment::recruit_situation_change_observer::recruit_list_changed() {

View file

@ -164,7 +164,7 @@ void candidate_action_evaluation_loop::remove_completed_cas()
cfg["path"] = path;
cfg["action"] = "delete";
ai::manager::modify_active_ai_for_side(this->get_side(), cfg); // we remove the CA
ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg); // we remove the CA
}
@ -181,7 +181,7 @@ void candidate_action_evaluation_loop::remove_completed_cas()
// cfg["path"] = path;
// cfg["action"] = "delete";
//
// ai::manager::modify_active_ai_for_side(this->get_side(), cfg);
// ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg);
// }
// else
// {

View file

@ -694,11 +694,11 @@ bool formula_ai::execute_candidate_action(ca_ptr fai_ca)
formula_ai::gamestate_change_observer::gamestate_change_observer() :
set_var_counter_(), set_unit_var_counter_(), continue_counter_()
{
ai::manager::add_gamestate_observer(this);
ai::manager::get_singleton().add_gamestate_observer(this);
}
formula_ai::gamestate_change_observer::~gamestate_change_observer() {
ai::manager::remove_gamestate_observer(this);
ai::manager::get_singleton().remove_gamestate_observer(this);
}
void formula_ai::gamestate_change_observer::handle_generic_event(const std::string& /*event_name*/) {

View file

@ -27,13 +27,13 @@ namespace ai {
gamestate_observer::gamestate_observer()
: gamestate_change_counter_(0)
{
ai::manager::add_gamestate_observer(this);
ai::manager::get_singleton().add_gamestate_observer(this);
}
gamestate_observer::~gamestate_observer()
{
ai::manager::remove_gamestate_observer(this);
ai::manager::get_singleton().remove_gamestate_observer(this);
}

View file

@ -21,7 +21,6 @@
#include "config.hpp" // for config, etc
#include "game_events/pump.hpp"
#include "generic_event.hpp" // for generic_event, etc
#include "log.hpp"
#include "map/location.hpp" // for map_location
#include "resources.hpp"
@ -303,17 +302,22 @@ component* holder::get_component(component *root, const std::string &path) {
// =======================================================================
manager::AI_map_of_stacks manager::ai_map_;
std::unique_ptr<game_info> manager::ai_info_;
events::generic_event manager::user_interact_("ai_user_interact");
events::generic_event manager::sync_network_("ai_sync_network");
events::generic_event manager::tod_changed_("ai_tod_changed");
events::generic_event manager::gamestate_changed_("ai_gamestate_changed");
events::generic_event manager::turn_started_("ai_turn_started");
events::generic_event manager::recruit_list_changed_("ai_recruit_list_changed");
events::generic_event manager::map_changed_("ai_map_changed");
int manager::last_interact_ = 0;
int manager::num_interact_ = 0;
manager::manager()
: user_interact_("ai_user_interact")
, sync_network_("ai_sync_network")
, tod_changed_("ai_tod_changed")
, gamestate_changed_("ai_gamestate_changed")
, turn_started_("ai_turn_started")
, recruit_list_changed_("ai_recruit_list_changed")
, map_changed_("ai_map_changed")
, last_interact_(0)
, num_interact_(0)
{
singleton_ = this;
}
manager* manager::singleton_ = nullptr;
void manager::set_ai_info(const game_info& i)
@ -480,7 +484,7 @@ const std::string manager::evaluate_command( side_number side, const std::string
}
bool manager::should_intercept( const std::string& str )
bool manager::should_intercept( const std::string& str ) const
{
if (str.length()<1) {
return false;
@ -495,9 +499,6 @@ bool manager::should_intercept( const std::string& str )
}
std::deque< command_history_item > manager::history_;
long manager::history_item_counter_ = 1;
//this is stub code to allow testing of basic 'history', 'repeat-last-command', 'add/remove/replace ai' capabilities.
//yes, it doesn't look nice. but it is usable.
//to be refactored at earliest opportunity
@ -703,13 +704,13 @@ void manager::append_active_ai_for_side(side_number side, const config& cfg)
get_active_ai_holder_for_side(side).append_ai(cfg);
}
std::string manager::get_active_ai_overview_for_side( side_number side)
std::string manager::get_active_ai_overview_for_side( side_number side )
{
return get_active_ai_holder_for_side(side).get_ai_overview();
}
std::string manager::get_active_ai_structure_for_side( side_number side)
std::string manager::get_active_ai_structure_for_side( side_number side )
{
return get_active_ai_holder_for_side(side).get_ai_structure();
}
@ -720,7 +721,7 @@ std::string manager::get_active_ai_identifier_for_side( side_number side )
return get_active_ai_holder_for_side(side).get_ai_identifier();
}
ai::holder& manager::get_active_ai_holder_for_side_dbg(side_number side)
ai::holder& manager::get_active_ai_holder_for_side_dbg( side_number side )
{
if (!game_config::debug)
{
@ -737,13 +738,13 @@ config manager::to_config( side_number side )
}
game_info& manager::get_active_ai_info_for_side( side_number /*side*/ )
game_info& manager::get_active_ai_info_for_side( side_number /*side*/ ) const
{
return *ai_info_;
}
game_info& manager::get_ai_info()
game_info& manager::get_ai_info() const
{
return *ai_info_;
}

View file

@ -21,8 +21,9 @@
#pragma once
#include "config.hpp" // for config, etc
#include "ai/game_info.hpp" // for side_number, ai_ptr
#include "ai/game_info.hpp" // for side_number, ai_ptr
#include "config.hpp" // for config, etc
#include "generic_event.hpp" // for generic_event, etc
#include <deque> // for deque
#include <map> // for map, map<>::value_compare
@ -112,8 +113,7 @@ private:
/**
* Class that manages AIs for all sides and manages AI redeployment.
* This class is responsible for managing the AI lifecycle
* It can be accessed like this: ai::manager::foo(...);
* This class is responsible for managing the AI lifecycle.
*/
class manager
{
@ -134,6 +134,26 @@ public:
static const std::string AI_TYPE_DEFAULT;
// =======================================================================
// CONSTRUCTORS AND DESTRUCTORS
// =======================================================================
manager();
/* The singleton can't be set to null in the destructor because member objects
(which access the singleton) are destroyed *after* the destructor has been run. */
~manager() = default;
// =======================================================================
// ACCESS TO MANAGER
// =======================================================================
static manager& get_singleton()
{
assert(singleton_ != nullptr);
return *singleton_;
}
// =======================================================================
// LIFECYCLE
// =======================================================================
@ -142,40 +162,40 @@ public:
* Sets AI information.
* @param info ai_information to be set.
*/
static void set_ai_info(const game_info& info);
void set_ai_info(const game_info& info);
/**
* Clears AI information.
* Should be called in playsingle_controller 's destructor.
*/
static void clear_ai_info();
void clear_ai_info();
/**
* Adds observer of game events.
* Should be called in playsingle_controller 's constructor.
*/
static void add_observer( events::observer* event_observer);
void add_observer( events::observer* event_observer);
/**
* Removes an observer of game events.
* Should be called in playsingle_controller 's destructor.
*/
static void remove_observer( events::observer* event_observer );
void remove_observer( events::observer* event_observer );
/**
* Adds observer of game events except ai_user_interact event and ai_sync_network event
*/
static void add_gamestate_observer( events::observer* event_observer);
void add_gamestate_observer( events::observer* event_observer);
/**
* Removes an observer of game events except ai_user_interact event and ai_sync_network event
*/
static void remove_gamestate_observer( events::observer* event_observer );
void remove_gamestate_observer( events::observer* event_observer );
/**
@ -185,97 +205,92 @@ public:
* doesn't occur too often, so there is no problem with calling it very
* regularly.
*/
static void raise_user_interact();
void raise_user_interact();
/**
* Notifies all observers of 'ai_sync_network' event.
* Basically a request from the AI to sync the network.
*/
static void raise_sync_network();
void raise_sync_network();
/**
* Notifies all observers of 'ai_gamestate_changed' event.
*/
static void raise_gamestate_changed();
void raise_gamestate_changed();
/**
* Notifies all observers of 'ai_tod_changed' event.
*/
static void raise_tod_changed();
void raise_tod_changed();
/**
* Notifies all observers of 'ai_recruit_list_changed' event.
*/
static void raise_recruit_list_changed();
void raise_recruit_list_changed();
/**
* Notifies all observers of 'ai_turn_started' event.
*/
static void raise_turn_started();
void raise_turn_started();
/**
* Notifies all observers of 'ai_map_changed' event.
*/
static void raise_map_changed();
void raise_map_changed();
/**
* Adds an observer of 'ai_map_changed' event.
*/
static void add_map_changed_observer( events::observer* event_observer );
void add_map_changed_observer( events::observer* event_observer );
/**
* Adds an observer of 'ai_recruit_list_changed' event.
*/
static void add_recruit_list_changed_observer( events::observer* event_observer );
void add_recruit_list_changed_observer( events::observer* event_observer );
/**
* Adds an observer of 'ai_turn_started' event.
*/
static void add_turn_started_observer( events::observer* event_observer );
void add_turn_started_observer( events::observer* event_observer );
/**
* Adds an observer of 'ai_tod_changed' event.
*/
static void add_tod_changed_observer( events::observer* event_observer );
void add_tod_changed_observer( events::observer* event_observer );
/**
* Deletes an observer of 'ai_map_changed' event.
*/
static void remove_map_changed_observer( events::observer* event_observer );
void remove_map_changed_observer( events::observer* event_observer );
/**
* Deletes an observer of 'ai_recruit_list_changed' event.
*/
static void remove_recruit_list_changed_observer( events::observer* event_observer );
void remove_recruit_list_changed_observer( events::observer* event_observer );
/**
* Deletes an observer of 'ai_turn_started' event.
*/
static void remove_turn_started_observer( events::observer* event_observer );
void remove_turn_started_observer( events::observer* event_observer );
/**
* Deletes an observer of 'ai_tod_changed' event.
*/
static void remove_tod_changed_observer( events::observer* event_observer );
private:
manager();
void remove_tod_changed_observer( events::observer* event_observer );
public:
@ -293,7 +308,7 @@ public:
* @param str string to evaluate.
* @return string result of evaluation.
*/
static const std::string evaluate_command( side_number side, const std::string& str );
const std::string evaluate_command( side_number side, const std::string& str );
// =======================================================================
@ -309,7 +324,7 @@ public:
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
*/
static bool add_ai_for_side_from_file( side_number side, const std::string& file, bool replace = true );
bool add_ai_for_side_from_file( side_number side, const std::string& file, bool replace = true );
/**
@ -321,7 +336,7 @@ public:
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
*/
static bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace = true);
bool add_ai_for_side_from_config(side_number side, const config &cfg, bool replace = true);
/**
@ -333,7 +348,7 @@ public:
* @param replace should new ai replace the current ai or 'be placed on top of it'.
* @return true if successful.
*/
static bool add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace = true);
bool add_ai_for_side( side_number side, const std::string& ai_algorithm_type, bool replace = true);
// =======================================================================
@ -346,7 +361,7 @@ public:
* by manager.
* @param side side number (1-based, as in game_info).
*/
static void remove_ai_for_side( side_number side );
void remove_ai_for_side( side_number side );
/**
@ -355,7 +370,7 @@ public:
* by manager.
* @param side side number (1-based, as in game_info).
*/
static void remove_all_ais_for_side( side_number side );
void remove_all_ais_for_side( side_number side );
/**
@ -365,7 +380,7 @@ public:
* playsingle_controller. It is necessary to do this if any of the
* info structures used by the AI goes out of scope.
*/
static void clear_ais();
void clear_ais();
// =======================================================================
// GET active AI parameters
@ -377,7 +392,7 @@ public:
* @param side side number (1-based).
* @return a reference to active AI info.
*/
static game_info& get_active_ai_info_for_side( side_number side );
game_info& get_active_ai_info_for_side( side_number side ) const;
/**
@ -385,7 +400,7 @@ public:
* @param side side number (1-based)
* @return an ai overview
*/
static std::string get_active_ai_overview_for_side( side_number side);
std::string get_active_ai_overview_for_side( side_number side );
/**
@ -393,14 +408,14 @@ public:
* @param side side number (1-based)
* @return an ai structure
*/
static std::string get_active_ai_structure_for_side( side_number side);
std::string get_active_ai_structure_for_side( side_number side );
/**
* Gets AI algorithm identifier for active AI of the given @a side.
* @param side side number (1-based).
* @return ai identifier for the active AI
*/
static std::string get_active_ai_identifier_for_side( side_number side );
std::string get_active_ai_identifier_for_side( side_number side );
/**
* Gets the active AI holder for debug purposes.
@ -408,21 +423,21 @@ public:
* @param side side number(1-based)
* @return debug ? active holder : empty holder
*/
static ai::holder& get_active_ai_holder_for_side_dbg(side_number side);
ai::holder& get_active_ai_holder_for_side_dbg(side_number side);
/**
* Gets AI config for active AI of the given @a side.
* @param side side number (1-based).
* @return a config object for the active AI
*/
static config to_config( side_number side );
config to_config( side_number side );
/**
* Gets global AI-game info
* @return a reference to the AI-game info.
*/
static game_info& get_ai_info();
game_info& get_ai_info() const;
// =======================================================================
@ -436,7 +451,7 @@ public:
* @param cfg - content of [modify_ai] tag
*/
static void modify_active_ai_for_side( ai::side_number side, const config &cfg );
void modify_active_ai_for_side( ai::side_number side, const config &cfg );
/**
* Appends AI parameters to active AI of the given @a side.
@ -445,7 +460,7 @@ public:
* @param cfg - content of [modify_side][ai] tag
*/
static void append_active_ai_for_side( ai::side_number side, const config &cfg );
void append_active_ai_for_side( ai::side_number side, const config &cfg );
// =======================================================================
// PROXY
@ -455,27 +470,30 @@ public:
* Plays a turn for the specified side using its active AI.
* @param side side number (1-based, as in game_info).
*/
static void play_turn(side_number side);
void play_turn(side_number side);
private:
typedef std::map< side_number, std::stack< holder > > AI_map_of_stacks;
static AI_map_of_stacks ai_map_;
static std::deque< command_history_item > history_;
static long history_item_counter_;
static std::unique_ptr<game_info> ai_info_;
static events::generic_event map_changed_;
static events::generic_event recruit_list_changed_;
static events::generic_event user_interact_;
static events::generic_event sync_network_;
static events::generic_event tod_changed_;
static events::generic_event gamestate_changed_;
static events::generic_event turn_started_;
static int last_interact_;
static int num_interact_;
std::deque< command_history_item > history_;
long history_item_counter_;
std::unique_ptr<game_info> ai_info_;
events::generic_event map_changed_;
events::generic_event recruit_list_changed_;
events::generic_event user_interact_;
events::generic_event sync_network_;
events::generic_event tod_changed_;
events::generic_event gamestate_changed_;
events::generic_event turn_started_;
int last_interact_;
int num_interact_;
AI_map_of_stacks ai_map_;
static manager* singleton_;
// =======================================================================
@ -489,14 +507,14 @@ private:
* @return string result of evaluation.
* @todo 1.9 rewrite this function to use a fai or lua parser.
*/
static const std::string internal_evaluate_command( side_number side, const std::string& str );
const std::string internal_evaluate_command( side_number side, const std::string& str );
/**
* Determines if the command should be intercepted and evaluated as internal command.
* @param str command string to check.
* @return true if the command should be intercepted and evaluated.
*/
static bool should_intercept( const std::string& str );
bool should_intercept( const std::string& str ) const;
// =======================================================================
// AI STACKS
@ -506,7 +524,7 @@ private:
/**
* Gets the AI stack for the specified side, create it if it doesn't exist.
*/
static std::stack< holder >& get_or_create_ai_stack_for_side(side_number side);
std::stack< holder >& get_or_create_ai_stack_for_side(side_number side);
// =======================================================================
// AI HOLDERS
@ -516,7 +534,7 @@ private:
/**
* Gets active holder for specified @a side.
*/
static holder& get_active_ai_holder_for_side( side_number side );
holder& get_active_ai_holder_for_side( side_number side );
// =======================================================================
// AI POINTERS
@ -530,7 +548,7 @@ private:
* @return a reference to the active AI.
* @note This reference may become invalid after specific manager operations.
*/
static ai_composite& get_active_ai_for_side( side_number side );
ai_composite& get_active_ai_for_side( side_number side );
};

View file

@ -90,9 +90,9 @@ void ai_testing::log_game_start()
{
for (std::vector<team>::const_iterator tm = resources::gameboard->teams().begin(); tm != resources::gameboard->teams().end(); ++tm) {
int side = tm-resources::gameboard->teams().begin()+1;
LOG_AI_TESTING << "AI_IDENTIFIER " <<side << ": " << ai::manager::get_active_ai_identifier_for_side(side) << std::endl;
LOG_AI_TESTING << "AI_IDENTIFIER " << side << ": " << ai::manager::get_singleton().get_active_ai_identifier_for_side(side) << std::endl;
LOG_AI_TESTING << "TEAM " << side << ": " << tm->side() << std::endl;
resources::recorder->add_log_data("ai_log","ai_id"+std::to_string(side),ai::manager::get_active_ai_identifier_for_side(side));
resources::recorder->add_log_data("ai_log", "ai_id" + std::to_string(side), ai::manager::get_singleton().get_active_ai_identifier_for_side(side));
///@todo 1.9: add information about ai_config
}
LOG_AI_TESTING << "VERSION: " << game_config::revision << std::endl;

View file

@ -636,7 +636,7 @@ WML_HANDLER_FUNCTION(replace_map,, cfg)
resources::screen->reload_map();
resources::screen->needs_rebuild(true);
ai::manager::raise_map_changed();
ai::manager::get_singleton().raise_map_changed();
}
/// Experimental data persistence

View file

@ -49,6 +49,7 @@ game_state::game_state(const config & level, play_controller & pc, const ter_dat
pathfind_manager_(new pathfind::manager(level)),
reports_(new reports()),
lua_kernel_(new game_lua_kernel(*this, pc, *reports_)),
ai_manager_(),
events_manager_(new game_events::manager()),
//TODO: this construct units (in dimiss undo action) but resrouces:: are not available yet,
// so we might want to move the innitialisation of undo_stack_ to game_state::init
@ -75,6 +76,7 @@ game_state::game_state(const config & level, play_controller & pc, game_board& b
pathfind_manager_(new pathfind::manager(level)),
reports_(new reports()),
lua_kernel_(new game_lua_kernel(*this, pc, *reports_)),
ai_manager_(),
events_manager_(new game_events::manager()),
player_number_(level["playing_team"].to_int() + 1),
end_level_data_(),

View file

@ -16,6 +16,7 @@
class config;
#include "ai/manager.hpp"
#include "filter_context.hpp"
#include "game_board.hpp"
#include "game_data.hpp"
@ -47,6 +48,7 @@ public:
std::unique_ptr<pathfind::manager> pathfind_manager_;
const std::unique_ptr<reports> reports_;
std::unique_ptr<game_lua_kernel> lua_kernel_;
ai::manager ai_manager_;
const std::unique_ptr<game_events::manager> events_manager_;
/// undo_stack_ is never nullptr. It is implemented as a pointer so that
/// undo_list can be an incomplete type at this point (which reduces the

View file

@ -699,7 +699,7 @@ void team_mode_controller::show_list(tree_view_node& node, int side)
void team_mode_controller::show_ai(tree_view_node& node, int side)
{
model().set_data(ai::manager::get_active_ai_overview_for_side(side));
model().set_data(ai::manager::get_singleton().get_active_ai_overview_for_side(side));
if(node.count_children() > 0) {
return;
@ -743,7 +743,7 @@ void team_mode_controller::show_ai_components(tree_view_node& node, int side)
if(label* lbl = dynamic_cast<label*>(w)) {
std::string tag = lbl->get_label();
tag.pop_back();
model().set_data(config_to_string(ai::manager::to_config(side), tag));
model().set_data(config_to_string(ai::manager::get_singleton().to_config(side), tag));
}
}
@ -774,7 +774,7 @@ void team_mode_controller::show_recall_unit(tree_view_node& node, int side)
void team_mode_controller::show_ai_tree(tree_view_node&, int side)
{
model().set_data(ai::manager::get_active_ai_structure_for_side(side));
model().set_data(ai::manager::get_singleton().get_active_ai_structure_for_side(side));
}
void team_mode_controller::show_units(tree_view_node&, int side)

View file

@ -1948,7 +1948,7 @@ void console_handler::do_whiteboard_options()
void menu_handler::do_ai_formula(const std::string& str, int side_num, mouse_handler& /*mousehandler*/)
{
try {
add_chat_message(time(nullptr), "wfl", 0, ai::manager::evaluate_command(side_num, str));
add_chat_message(time(nullptr), "wfl", 0, ai::manager::get_singleton().evaluate_command(side_num, str));
} catch(wfl::formula_error&) {
} catch(...) {
add_chat_message(time(nullptr), "wfl", 0, "UNKNOWN ERROR IN FORMULA");

View file

@ -306,6 +306,10 @@ void play_controller::reset_gamestate(const config& level, int replay_pos)
gui_->labels().set_team(nullptr);
/* First destroy the old game state, then create the new one.
This is necessary to ensure that while the old AI manager is being destroyed,
all its member objects access the old manager instead of the new. */
gamestate_.reset();
gamestate_.reset(new game_state(level, *this, tdata_));
resources::gameboard = &gamestate().board_;
resources::gamedata = &gamestate().gamedata_;
@ -956,7 +960,7 @@ void play_controller::check_victory()
if (gui_->video().non_interactive()) {
LOG_AIT << "winner: ";
for (unsigned l : not_defeated) {
std::string ai = ai::manager::get_active_ai_identifier_for_side(l);
std::string ai = ai::manager::get_singleton().get_active_ai_identifier_for_side(l);
if (ai.empty()) ai = "default ai";
LOG_AIT << l << " (using " << ai << ") ";
}

View file

@ -86,8 +86,8 @@ playsingle_controller::playsingle_controller(const config& level,
linger_ = this->is_regular_game_end();
ai::game_info ai_info;
ai::manager::set_ai_info(ai_info);
ai::manager::add_observer(this) ;
ai::manager::get_singleton().set_ai_info(ai_info);
ai::manager::get_singleton().add_observer(this);
plugins_context_->set_accessor_string("level_result", std::bind(&playsingle_controller::describe_result, this));
plugins_context_->set_accessor_int("turn", std::bind(&play_controller::turn, this));
@ -108,8 +108,8 @@ std::string playsingle_controller::describe_result() const
playsingle_controller::~playsingle_controller()
{
ai::manager::remove_observer(this) ;
ai::manager::clear_ais() ;
ai::manager::get_singleton().remove_observer(this);
ai::manager::get_singleton().clear_ais();
}
void playsingle_controller::init_gui()
@ -552,7 +552,7 @@ void playsingle_controller::play_ai_turn()
try {
try {
if (!should_return_to_play_side()) {
ai::manager::play_turn(current_side());
ai::manager::get_singleton().play_turn(current_side());
}
}
catch (return_to_play_side_exception&) {

View file

@ -2970,7 +2970,7 @@ static int intf_modify_ai(lua_State *L, const char* action)
"path", path
};
if(strcmp(action, "delete") == 0) {
ai::manager::modify_active_ai_for_side(side_num, cfg);
ai::manager::get_singleton().modify_active_ai_for_side(side_num, cfg);
return 0;
}
config component = luaW_checkconfig(L, 3);
@ -2980,7 +2980,7 @@ static int intf_modify_ai(lua_State *L, const char* action)
len = open_brak - dot - 1;
}
cfg.add_child(path.substr(dot + 1, len), component);
ai::manager::modify_active_ai_for_side(side_num, cfg);
ai::manager::get_singleton().modify_active_ai_for_side(side_num, cfg);
return 0;
}
@ -2989,13 +2989,13 @@ static int intf_switch_ai(lua_State *L)
int side_num = luaL_checkinteger(L, 1);
if(lua_isstring(L, 2)) {
std::string file = luaL_checkstring(L, 2);
if(!ai::manager::add_ai_for_side_from_file(side_num, file)) {
if(!ai::manager::get_singleton().add_ai_for_side_from_file(side_num, file)) {
std::string err = formatter() << "Could not load AI for side " << side_num << " from file " << file;
lua_pushlstring(L, err.c_str(), err.length());
return lua_error(L);
}
} else {
ai::manager::add_ai_for_side_from_config(side_num, luaW_checkconfig(L, 2));
ai::manager::get_singleton().add_ai_for_side_from_config(side_num, luaW_checkconfig(L, 2));
}
return 0;
}
@ -3020,7 +3020,7 @@ static int intf_append_ai(lua_State *L)
}
}
}
ai::manager::append_active_ai_for_side(side_num, cfg.child("ai"));
ai::manager::get_singleton().append_active_ai_for_side(side_num, cfg.child("ai"));
return 0;
}
@ -3348,7 +3348,7 @@ static int intf_modify_ai_old(lua_State *L)
luaW_toconfig(L, 1, cfg);
int side = cfg["side"];
WRN_LUA << "wesnoth.modify_ai is deprecated\n";
ai::manager::modify_active_ai_for_side(side, cfg);
ai::manager::get_singleton().modify_active_ai_for_side(side, cfg);
return 0;
}
@ -3461,7 +3461,7 @@ static int intf_debug_ai(lua_State *L)
int side = lua_tointeger(L, 1);
lua_pop(L, 1);
ai::component* c = ai::manager::get_active_ai_holder_for_side_dbg(side).get_component(nullptr, "");
ai::component* c = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(nullptr, "");
// Bad, but works
std::vector<ai::component*> engines = c->get_children("engine");
@ -3475,7 +3475,7 @@ static int intf_debug_ai(lua_State *L)
}
// Better way, but doesn't work
//ai::component* e = ai::manager::get_active_ai_holder_for_side_dbg(side).get_component(c, "engine[lua]");
//ai::component* e = ai::manager::get_singleton().get_active_ai_holder_for_side_dbg(side).get_component(c, "engine[lua]");
//ai::engine_lua* lua_engine = dynamic_cast<ai::engine_lua *>(e);
if (lua_engine == nullptr)

View file

@ -204,9 +204,9 @@ void team::team_info::read(const config& cfg)
user_team_name = t_string::from_serialized(user_team_name);
if(cfg.has_attribute("ai_config")) {
ai::manager::add_ai_for_side_from_file(side, cfg["ai_config"], true);
ai::manager::get_singleton().add_ai_for_side_from_file(side, cfg["ai_config"], true);
} else {
ai::manager::add_ai_for_side_from_config(side, cfg, true);
ai::manager::get_singleton().add_ai_for_side_from_config(side, cfg, true);
}
std::vector<std::string> recruits = utils::split(cfg["recruit"]);
@ -322,7 +322,7 @@ void team::team_info::write(config& cfg) const
cfg.add_child("variables", variables);
}
cfg.add_child("ai", ai::manager::to_config(side));
cfg.add_child("ai", ai::manager::get_singleton().to_config(side));
}
team::team()
@ -458,14 +458,14 @@ void team::set_recruits(const std::set<std::string>& recruits)
{
info_.can_recruit = recruits;
info_.minimum_recruit_price = 0;
ai::manager::raise_recruit_list_changed();
ai::manager::get_singleton().raise_recruit_list_changed();
}
void team::add_recruit(const std::string& recruit)
{
info_.can_recruit.insert(recruit);
info_.minimum_recruit_price = 0;
ai::manager::raise_recruit_list_changed();
ai::manager::get_singleton().raise_recruit_list_changed();
}
int team::minimum_recruit_price() const

View file

@ -1129,7 +1129,7 @@ void unit::set_recruits(const std::vector<std::string>& recruits)
recruit_list_ = recruits;
//TODO crab
//info_.minimum_recruit_price = 0;
//ai::manager::raise_recruit_list_changed();
//ai::manager::get_singleton().raise_recruit_list_changed();
}
const std::vector<std::string> unit::advances_to_translated() const