From 275c9f542dcf81d97f0241f468817b85f1e48540 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:34:35 -0600 Subject: [PATCH 01/21] [sim_controllers] Initial implementation. --- engine/sim/sim.cpp | 21 +++++++++ engine/sim/sim.hpp | 103 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 095c93a8e17..3a884761304 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -4728,3 +4728,24 @@ bool sim_t::rethrow_exception_queue() return false; } + +sim_controller_data_t::sim_controller_data_t() +{ +} + +sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) +{ +} + +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) +{ +} + +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) + : mutex(), data( data ) +{ +} + +sim_controller_t::sim_controller_t( sim_t* sim ) : parent( sim->parent ), sim( sim ) +{ +} diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index d449957600d..4adf640f746 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -11,6 +11,7 @@ #include "progress_bar.hpp" #include "sim_ostream.hpp" #include "sim/option.hpp" +#include "interfaces/sc_js.hpp" #include "util/concurrency.hpp" #include "util/rng.hpp" #include "util/sample_data.hpp" @@ -19,6 +20,7 @@ #include #include +#include struct actor_target_data_t; struct buff_t; @@ -45,10 +47,73 @@ namespace report::json class report_configuration_t; } -namespace profileset{ +namespace profileset +{ class profilesets_t; } +struct sim_controller_data_t; +namespace +{ +template +struct data_wrapper_t +{ + T& data; + + data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) + { + } + +private: + std::scoped_lock lock; +}; + +struct sim_controller_data_wrapper_t +{ + std::mutex mutex; + std::shared_ptr data; + + sim_controller_data_wrapper_t(); + sim_controller_data_wrapper_t( std::shared_ptr data ); + + // disallow copy, as that would introduce additional mutexes for a single controller name + sim_controller_data_wrapper_t( sim_controller_data_wrapper_t& ) = delete; + sim_controller_data_wrapper_t( const sim_controller_data_wrapper_t& ) = delete; +}; +} // namespace + +struct sim_controller_data_t +{ + sim_controller_data_t(); + sim_controller_data_t( sim_controller_data_t& data ); +}; + +struct sim_controller_t +{ + using data_t = sim_controller_data_t; + +private: + sim_t* parent; + +public: + sim_t* sim; + + sim_controller_t( sim_t* sim ); + virtual ~sim_controller_t() = default; + + virtual const std::string name() const = 0; + virtual int evaluate() = 0; + virtual void report_json_profileset( js::JsonOutput& ) = 0; + virtual void report_json_options( js::JsonOutput& ) = 0; + virtual void report_html( std::ostream& ) = 0; + +protected: + template + data_wrapper_t get_data(); + template + bool set_data( T&& data ); +}; + struct sim_progress_t { int current_iterations; @@ -622,6 +687,13 @@ struct sim_t : private sc_thread_t double scaling_normalized; bool merge_enemy_priority_dmg; + // sim control +private: + friend sim_controller_t; + std::vector> sim_controllers; + std::map sim_controller_data; + +public: // Multi-Threading mutex_t merge_mutex; int threads; @@ -796,6 +868,19 @@ struct sim_t : private sc_thread_t { return _rng; } double averaged_range( double min, double max ); + template , bool>> + bool register_sim_controller( Args&&... args ) + { + sim_controllers.emplace_back( std::make_unique( this, std::forward( args )... ) ); + typedef typename TBase::data_t data_t; + return parent->sim_controller_data + .emplace( std::make_pair( sim_controllers.back()->name(), std::make_shared() ) ) + .second; + } + bool evaluate_sim_controller_post_init(); + bool evaluate_sim_controller_post_iter(); + // Thread id of this sim_t object #ifndef SC_NO_THREADING std::thread::id thread_id() const @@ -849,3 +934,19 @@ struct sim_t : private sc_thread_t void disable_debug_seed(); bool requires_cleanup() const; }; + +template +data_wrapper_t sim_controller_t::get_data() +{ + auto& data = parent->sim_controller_data.at( name() ); + return { *std::static_pointer_cast( data.data ), data.mutex }; +} + +template +bool sim_controller_t::set_data( T&& data ) +{ + auto& scd = parent->sim_controller_data; + assert( scd.find( name() ) != scd.end() ); + scd[ name() ].data = std::make_shared( data ); + return true; +} From ea6d1221a8954e2adff3581f07eb361a44f9bad3 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:09:39 -0600 Subject: [PATCH 02/21] [sim_controllers] Fix a few implementation details and implement basic example. --- engine/class_modules/monk/sc_monk.cpp | 7 +++++ engine/class_modules/monk/sc_monk.hpp | 2 ++ engine/sim/sim.cpp | 41 ++++++++++++++++++++++++--- engine/sim/sim.hpp | 23 +++++++-------- engine/sim/sim_controller.cpp | 37 ++++++++++++++++++++++++ engine/sim/sim_controller.hpp | 23 +++++++++++++++ source_files/QT_engine.pri | 2 ++ source_files/VS_engine.props | 2 ++ source_files/cmake_engine.txt | 2 ++ source_files/engine_make | 1 + 10 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 engine/sim/sim_controller.cpp create mode 100644 engine/sim/sim_controller.hpp diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 53d5ddc3d42..05e86d51b8b 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6889,6 +6889,13 @@ bool monk_t::validate_fight_style( fight_style_e style ) const return true; } +void monk_t::init() +{ + base_t::init(); + + sim->register_sim_controller( this, STAT_CRIT_RATING, 100 ); +} + // monk_t::init_spells ====================================================== void monk_t::init_spells() { diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 5c730300b43..03af810b65e 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -16,6 +16,7 @@ #include "sc_enums.hpp" #include "sc_stagger.hpp" #include "sim/proc.hpp" +#include "sim/sim_controller.hpp" #include "util/timeline.hpp" #include @@ -1476,6 +1477,7 @@ struct monk_t : public stagger_t bool validate_fight_style( fight_style_e style ) const override; // Init / Reset + void init() override; void create_pets() override; void init_spells() override; void init_background_actions() override; diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 3a884761304..e83681d92af 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1546,6 +1546,8 @@ sim_t::sim_t() count_overheal_as_heal( false ), scaling_normalized( 1.0 ), merge_enemy_priority_dmg( false ), + sim_controllers(), + sim_controller_data(), // Multi-Threading threads( 0 ), thread_index( 0 ), @@ -3059,6 +3061,12 @@ bool sim_t::iterate() progress_bar.init(); + if ( profileset_enabled && parent && evaluate_sim_controller_post_init() ) + { + cancel(); + return true; + } + try { activate_actors(); @@ -3076,6 +3084,9 @@ bool sim_t::iterate() progress_bar.output( false ); } + if ( profileset_enabled && parent && evaluate_sim_controller_post_iter() ) + cancel(); + do_pause(); auto old_active = current_index; if ( !canceled ) @@ -4729,6 +4740,15 @@ bool sim_t::rethrow_exception_queue() return false; } +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) +{ +} + +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) + : mutex(), data( data ) +{ +} + sim_controller_data_t::sim_controller_data_t() { } @@ -4737,15 +4757,28 @@ sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) { } -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) +sim_controller_t::sim_controller_t( sim_t* sim ) : parent( sim->parent ), sim( sim ) { } -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) - : mutex(), data( data ) +bool sim_t::evaluate_sim_controller_post_init() { + if ( !profileset_enabled && parent != nullptr ) + return false; + + for ( auto &sc : sim_controllers ) + if ( sc->evaluate_post_init() ) + return true; + return false; } -sim_controller_t::sim_controller_t( sim_t* sim ) : parent( sim->parent ), sim( sim ) +bool sim_t::evaluate_sim_controller_post_iter() { + if ( !profileset_enabled && parent != nullptr ) + return false; + + for ( auto &sc : sim_controllers ) + if ( sc->evaluate_post_iter() ) + return true; + return false; } diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 4adf640f746..ef4bf2bf247 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -53,16 +53,12 @@ namespace profileset } struct sim_controller_data_t; -namespace -{ template struct data_wrapper_t { T& data; - data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) - { - } + data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) {} private: std::scoped_lock lock; @@ -80,7 +76,6 @@ struct sim_controller_data_wrapper_t sim_controller_data_wrapper_t( sim_controller_data_wrapper_t& ) = delete; sim_controller_data_wrapper_t( const sim_controller_data_wrapper_t& ) = delete; }; -} // namespace struct sim_controller_data_t { @@ -102,7 +97,8 @@ struct sim_controller_t virtual ~sim_controller_t() = default; virtual const std::string name() const = 0; - virtual int evaluate() = 0; + virtual bool evaluate_post_init() = 0; + virtual bool evaluate_post_iter() = 0; virtual void report_json_profileset( js::JsonOutput& ) = 0; virtual void report_json_options( js::JsonOutput& ) = 0; virtual void report_html( std::ostream& ) = 0; @@ -872,11 +868,14 @@ struct sim_t : private sc_thread_t typename = typename std::enable_if_t, bool>> bool register_sim_controller( Args&&... args ) { - sim_controllers.emplace_back( std::make_unique( this, std::forward( args )... ) ); - typedef typename TBase::data_t data_t; - return parent->sim_controller_data - .emplace( std::make_pair( sim_controllers.back()->name(), std::make_shared() ) ) + if ( profileset_enabled && parent != nullptr ) + { + sim_controllers.emplace_back( std::make_unique( this, std::forward( args )... ) ); + return parent->sim_controller_data + .emplace( sim_controllers.back()->name(), std::make_shared() ) .second; + } + return false; } bool evaluate_sim_controller_post_init(); bool evaluate_sim_controller_post_iter(); @@ -948,5 +947,5 @@ bool sim_controller_t::set_data( T&& data ) auto& scd = parent->sim_controller_data; assert( scd.find( name() ) != scd.end() ); scd[ name() ].data = std::make_shared( data ); - return true; + return true; // TODO: this RV is stupid } diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp new file mode 100644 index 00000000000..cbf0fcf110e --- /dev/null +++ b/engine/sim/sim_controller.cpp @@ -0,0 +1,37 @@ +#include "sim_controller.hpp" + +// struct min_player_stat_t : sim_controller_t +// { +// // no using as this is a unary sim controller with no paired data + +// player_t* target_player; +// rating_e rating; +// double min_rating; + +// min_player_stat_t( sim_t*, player_t*, rating_e, double ); +// const std::string name() const override { return { "min_player_stat" }; }; +// bool evaluate_post_init() override; +// bool evaluate_post_iter() override { return true; } +// void report_json_profileset( js::JsonOutput& ) override; +// void report_json_options( js::JsonOutput& ) override; +// void report_html( std::ostream& ) override; +// }; + +min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) + : sim_controller_t( sim ), target_player( target_player ), rating( rating ), min_rating( amount ) +{} + +bool min_player_stat_t::evaluate_post_iter() +{ + return false; +} + +bool min_player_stat_t::evaluate_post_init() +{ + // return false; + return target_player->get_stat_value( rating ) < min_rating; +} + +void min_player_stat_t::report_json_profileset( js::JsonOutput& ){} +void min_player_stat_t::report_json_options( js::JsonOutput& ){} +void min_player_stat_t::report_html( std::ostream& ){} diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp new file mode 100644 index 00000000000..072760fcecd --- /dev/null +++ b/engine/sim/sim_controller.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "sim.hpp" +#include "player/rating.hpp" +#include "player/player.hpp" + +struct min_player_stat_t : sim_controller_t +{ + // no using as this is a unary sim controller with no paired data + using data_t = sim_controller_data_t; + + player_t* target_player; + stat_e rating; + double min_rating; + + min_player_stat_t( sim_t*, player_t*, stat_e, double ); + const std::string name() const override { return "min_player_stat"; }; + bool evaluate_post_init() override;// { return false; } + bool evaluate_post_iter() override;// { return true; } + void report_json_profileset( js::JsonOutput& ) override; + void report_json_options( js::JsonOutput& ) override; + void report_html( std::ostream& ) override; +}; diff --git a/source_files/QT_engine.pri b/source_files/QT_engine.pri index ec8f083c5c1..2b8ff4f5293 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -171,6 +171,7 @@ HEADERS += engine/sim/reforge_plot.hpp HEADERS += engine/sim/scale_factor_control.hpp HEADERS += engine/sim/sim.hpp HEADERS += engine/sim/sim_control.hpp +HEADERS += engine/sim/sim_controller.hpp HEADERS += engine/sim/sim_ostream.hpp HEADERS += engine/sim/uptime.hpp HEADERS += engine/sim/work_queue.hpp @@ -366,6 +367,7 @@ SOURCES += engine/sim/raid_event.cpp SOURCES += engine/sim/reforge_plot.cpp SOURCES += engine/sim/scale_factor_control.cpp SOURCES += engine/sim/sim.cpp +SOURCES += engine/sim/sim_controller.cpp SOURCES += engine/sim/sim_ostream.cpp SOURCES += engine/sim/uptime_benefit.cpp SOURCES += engine/util/cache.cpp diff --git a/source_files/VS_engine.props b/source_files/VS_engine.props index ad67be4033c..44a98da242c 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -175,6 +175,7 @@ To change the list of source files run synchronize.py + @@ -369,6 +370,7 @@ To change the list of source files run synchronize.py + diff --git a/source_files/cmake_engine.txt b/source_files/cmake_engine.txt index 6f99f9d9d94..0845f9d2dde 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -169,6 +169,7 @@ sim/reforge_plot.hpp sim/scale_factor_control.hpp sim/sim.hpp sim/sim_control.hpp +sim/sim_controller.hpp sim/sim_ostream.hpp sim/uptime.hpp sim/work_queue.hpp @@ -363,6 +364,7 @@ sim/raid_event.cpp sim/reforge_plot.cpp sim/scale_factor_control.cpp sim/sim.cpp +sim/sim_controller.cpp sim/sim_ostream.cpp sim/uptime_benefit.cpp util/cache.cpp diff --git a/source_files/engine_make b/source_files/engine_make index 862193bab57..a069441936e 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -168,6 +168,7 @@ SRC += \ sim$(PATHSEP)reforge_plot.cpp \ sim$(PATHSEP)scale_factor_control.cpp \ sim$(PATHSEP)sim.cpp \ + sim$(PATHSEP)sim_controller.cpp \ sim$(PATHSEP)sim_ostream.cpp \ sim$(PATHSEP)uptime_benefit.cpp \ util$(PATHSEP)cache.cpp \ From 24764eaed90c096819375d431a8929c5d7b4f144 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 21 Oct 2025 05:16:11 -0600 Subject: [PATCH 03/21] [sim_controllers] Switch return type of `sim_controller_t::set_data(...)`. --- engine/sim/sim.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index ef4bf2bf247..37a77262d00 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -107,7 +107,7 @@ struct sim_controller_t template data_wrapper_t get_data(); template - bool set_data( T&& data ); + void set_data( T&& data ); }; struct sim_progress_t @@ -942,10 +942,9 @@ data_wrapper_t sim_controller_t::get_data() } template -bool sim_controller_t::set_data( T&& data ) +void sim_controller_t::set_data( T&& data ) { auto& scd = parent->sim_controller_data; assert( scd.find( name() ) != scd.end() ); scd[ name() ].data = std::make_shared( data ); - return true; // TODO: this RV is stupid } From ef35db814823452939bca38fe0c6c1ca39d3658b Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 25 Oct 2025 01:47:31 -0600 Subject: [PATCH 04/21] [sim_controllers] Fix action list merge issue by guaranteeing `sim_t::combat()` gets called once. This causes all actions to be constructed, fixing the warning. --- engine/sim/sim.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index e83681d92af..6473846b79d 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -3062,10 +3062,7 @@ bool sim_t::iterate() progress_bar.init(); if ( profileset_enabled && parent && evaluate_sim_controller_post_init() ) - { cancel(); - return true; - } try { From 45846b5b693a029ed2585b6945df3a19dd5d67ff Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 25 Oct 2025 04:51:05 -0600 Subject: [PATCH 05/21] [sim_controllers] Tier set bonus constraint sample. --- engine/class_modules/monk/sc_monk.cpp | 3 +- engine/sim/sim_controller.cpp | 58 +++++++++++++++++---------- engine/sim/sim_controller.hpp | 31 ++++++++++++-- 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 05e86d51b8b..df24262c8d7 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6893,7 +6893,8 @@ void monk_t::init() { base_t::init(); - sim->register_sim_controller( this, STAT_CRIT_RATING, 100 ); + // sim->register_sim_controller( this, STAT_CRIT_RATING, 100 ); + sim->register_sim_controller( this, TWW2, B2 ); } // monk_t::init_spells ====================================================== diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index cbf0fcf110e..bbb3bbe9267 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,25 +1,11 @@ #include "sim_controller.hpp" -// struct min_player_stat_t : sim_controller_t -// { -// // no using as this is a unary sim controller with no paired data - -// player_t* target_player; -// rating_e rating; -// double min_rating; - -// min_player_stat_t( sim_t*, player_t*, rating_e, double ); -// const std::string name() const override { return { "min_player_stat" }; }; -// bool evaluate_post_init() override; -// bool evaluate_post_iter() override { return true; } -// void report_json_profileset( js::JsonOutput& ) override; -// void report_json_options( js::JsonOutput& ) override; -// void report_html( std::ostream& ) override; -// }; +#include "player/set_bonus.hpp" min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) : sim_controller_t( sim ), target_player( target_player ), rating( rating ), min_rating( amount ) -{} +{ +} bool min_player_stat_t::evaluate_post_iter() { @@ -28,10 +14,40 @@ bool min_player_stat_t::evaluate_post_iter() bool min_player_stat_t::evaluate_post_init() { - // return false; return target_player->get_stat_value( rating ) < min_rating; } -void min_player_stat_t::report_json_profileset( js::JsonOutput& ){} -void min_player_stat_t::report_json_options( js::JsonOutput& ){} -void min_player_stat_t::report_html( std::ostream& ){} +void min_player_stat_t::report_json_profileset( js::JsonOutput& ) +{ +} +void min_player_stat_t::report_json_options( js::JsonOutput& ) +{ +} +void min_player_stat_t::report_html( std::ostream& ) +{ +} + +tier_set_count_t::tier_set_count_t( sim_t* sim, player_t* target_player, set_bonus_type_e tier, set_bonus_e count ) + : sim_controller_t( sim ), target_player( target_player ), tier( tier ), count( count ) +{ +} + +bool tier_set_count_t::evaluate_post_init() +{ + return !target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); +} + +bool tier_set_count_t::evaluate_post_iter() +{ + return false; +} + +void tier_set_count_t::report_json_profileset( js::JsonOutput& ) +{ +} +void tier_set_count_t::report_json_options( js::JsonOutput& ) +{ +} +void tier_set_count_t::report_html( std::ostream& ) +{ +} diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 072760fcecd..58c414f62a7 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -3,10 +3,16 @@ #include "sim.hpp" #include "player/rating.hpp" #include "player/player.hpp" +#include "sc_enums.hpp" struct min_player_stat_t : sim_controller_t { - // no using as this is a unary sim controller with no paired data + /* + * This sim controller doesn't work, as at all controller evaluation points + * only have base rating provided by the class/spec. If gear stats were to + * be set once on actor init and preserved between iterations, this would be + * fixed. + */ using data_t = sim_controller_data_t; player_t* target_player; @@ -14,9 +20,26 @@ struct min_player_stat_t : sim_controller_t double min_rating; min_player_stat_t( sim_t*, player_t*, stat_e, double ); - const std::string name() const override { return "min_player_stat"; }; - bool evaluate_post_init() override;// { return false; } - bool evaluate_post_iter() override;// { return true; } + const std::string name() const override { return "min_player_stat"; } + bool evaluate_post_init() override; + bool evaluate_post_iter() override; + void report_json_profileset( js::JsonOutput& ) override; + void report_json_options( js::JsonOutput& ) override; + void report_html( std::ostream& ) override; +}; + +struct tier_set_count_t : sim_controller_t +{ + using data_t = sim_controller_data_t; + + player_t* target_player; + set_bonus_type_e tier; + set_bonus_e count; + + tier_set_count_t( sim_t*, player_t*, set_bonus_type_e, set_bonus_e ); + const std::string name() const override { return "tier_set_count"; } + bool evaluate_post_init() override; + bool evaluate_post_iter() override; void report_json_profileset( js::JsonOutput& ) override; void report_json_options( js::JsonOutput& ) override; void report_html( std::ostream& ) override; From 0b5bee275123531b5dcf05f15670fdfc1751a316 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 26 Oct 2025 03:18:15 -0600 Subject: [PATCH 06/21] [sim_controllers] Rework implementation a bit to provide more defaults, move implementation out of `sim_t` and improve reporting. --- engine/sim/sim.cpp | 74 ++++++++++++++++++++++++++--------- engine/sim/sim.hpp | 33 ++++++++++------ engine/sim/sim_controller.cpp | 36 ++++------------- engine/sim/sim_controller.hpp | 9 +---- 4 files changed, 85 insertions(+), 67 deletions(-) diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 6473846b79d..a7791112bf9 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -3061,8 +3061,7 @@ bool sim_t::iterate() progress_bar.init(); - if ( profileset_enabled && parent && evaluate_sim_controller_post_init() ) - cancel(); + sim_controller_t::evaluate( this, sim_controller_t::POST_INIT ); try { @@ -3081,8 +3080,7 @@ bool sim_t::iterate() progress_bar.output( false ); } - if ( profileset_enabled && parent && evaluate_sim_controller_post_iter() ) - cancel(); + sim_controller_t::evaluate( this, sim_controller_t::POST_ITER ); do_pause(); auto old_active = current_index; @@ -4758,24 +4756,64 @@ sim_controller_t::sim_controller_t( sim_t* sim ) : parent( sim->parent ), sim( s { } -bool sim_t::evaluate_sim_controller_post_init() +const std::string sim_controller_t::message( call_point_e call_point ) const { - if ( !profileset_enabled && parent != nullptr ) - return false; + std::string msg = + fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), + call_point_string( call_point ) ); + if ( call_point == POST_ITER ) + msg += std::to_string( sim->current_iteration ); + + if ( const std::string r = reason(); r != "" ) + msg += fmt::format( " because {}.", r ); + else + msg += "."; - for ( auto &sc : sim_controllers ) - if ( sc->evaluate_post_init() ) - return true; - return false; + return msg; } -bool sim_t::evaluate_sim_controller_post_iter() +const std::string sim_controller_t::call_point_string( call_point_e call_point ) { - if ( !profileset_enabled && parent != nullptr ) - return false; + switch ( call_point ) + { + case POST_INIT: + return "simulation initialization"; + case POST_ITER: + return "iteration"; + default: + assert( false ); + return "no matching call point"; + } +} - for ( auto &sc : sim_controllers ) - if ( sc->evaluate_post_iter() ) - return true; - return false; +void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) +{ + if ( !sim->profileset_enabled || !sim->parent ) + return; + + typedef std::shared_ptr iter_t; + std::function cb; + switch ( call_point ) + { + case POST_INIT: + cb = []( iter_t& sc ) { return !sc->evaluate_post_init(); }; + break; + case POST_ITER: + cb = []( iter_t& sc ) { return !sc->evaluate_post_iter(); }; + break; + default: + assert( false ); + break; + } + auto sc = range::find_if( sim->sim_controllers, cb ); + if ( sc == sim->sim_controllers.end() ) + return; + + std::shared_ptr& controller = *sc; + assert( controller->sim == sim ); + assert( controller->parent == sim->parent ); + + sim->canceled = true; + sim->error( controller->message( call_point ) ); + sim->interrupt(); } diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 37a77262d00..1e09981b950 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -87,21 +87,32 @@ struct sim_controller_t { using data_t = sim_controller_data_t; + enum call_point_e + { + POST_INIT, + POST_ITER + }; + + static const std::string call_point_string( call_point_e call_point ); + static void evaluate( sim_t* sim, call_point_e call_point ); + private: sim_t* parent; - public: sim_t* sim; sim_controller_t( sim_t* sim ); virtual ~sim_controller_t() = default; - virtual const std::string name() const = 0; - virtual bool evaluate_post_init() = 0; - virtual bool evaluate_post_iter() = 0; - virtual void report_json_profileset( js::JsonOutput& ) = 0; - virtual void report_json_options( js::JsonOutput& ) = 0; - virtual void report_html( std::ostream& ) = 0; + const std::string message( call_point_e ) const; + + virtual const std::string name() const = 0; + virtual const std::string reason() const { return {}; } + virtual bool evaluate_post_init() { return true; }; + virtual bool evaluate_post_iter() { return true; }; + virtual void report_json_profileset( js::JsonOutput& ) {}; + virtual void report_json_options( js::JsonOutput& ) {}; + virtual void report_html( std::ostream& ) {}; protected: template @@ -686,7 +697,7 @@ struct sim_t : private sc_thread_t // sim control private: friend sim_controller_t; - std::vector> sim_controllers; + std::vector> sim_controllers; std::map sim_controller_data; public: @@ -868,17 +879,15 @@ struct sim_t : private sc_thread_t typename = typename std::enable_if_t, bool>> bool register_sim_controller( Args&&... args ) { - if ( profileset_enabled && parent != nullptr ) + if ( profileset_enabled && parent ) { - sim_controllers.emplace_back( std::make_unique( this, std::forward( args )... ) ); + sim_controllers.emplace_back( std::make_shared( this, std::forward( args )... ) ); return parent->sim_controller_data .emplace( sim_controllers.back()->name(), std::make_shared() ) .second; } return false; } - bool evaluate_sim_controller_post_init(); - bool evaluate_sim_controller_post_iter(); // Thread id of this sim_t object #ifndef SC_NO_THREADING diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index bbb3bbe9267..4fd58b11db6 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -7,24 +7,9 @@ min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_ { } -bool min_player_stat_t::evaluate_post_iter() -{ - return false; -} - bool min_player_stat_t::evaluate_post_init() { - return target_player->get_stat_value( rating ) < min_rating; -} - -void min_player_stat_t::report_json_profileset( js::JsonOutput& ) -{ -} -void min_player_stat_t::report_json_options( js::JsonOutput& ) -{ -} -void min_player_stat_t::report_html( std::ostream& ) -{ + return target_player->get_stat_value( rating ) >= min_rating; } tier_set_count_t::tier_set_count_t( sim_t* sim, player_t* target_player, set_bonus_type_e tier, set_bonus_e count ) @@ -34,20 +19,13 @@ tier_set_count_t::tier_set_count_t( sim_t* sim, player_t* target_player, set_bon bool tier_set_count_t::evaluate_post_init() { - return !target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); -} - -bool tier_set_count_t::evaluate_post_iter() -{ - return false; + return target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); } -void tier_set_count_t::report_json_profileset( js::JsonOutput& ) -{ -} -void tier_set_count_t::report_json_options( js::JsonOutput& ) -{ -} -void tier_set_count_t::report_html( std::ostream& ) +const std::string tier_set_count_t::reason() const { + // no to string for set bonus tier or count... + // that should definitely exist :) + return fmt::format( "player {} does not have tier {} {} active", target_player->name(), static_cast( tier ), + static_cast( count ) ); } diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 58c414f62a7..545eb98a250 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -22,10 +22,6 @@ struct min_player_stat_t : sim_controller_t min_player_stat_t( sim_t*, player_t*, stat_e, double ); const std::string name() const override { return "min_player_stat"; } bool evaluate_post_init() override; - bool evaluate_post_iter() override; - void report_json_profileset( js::JsonOutput& ) override; - void report_json_options( js::JsonOutput& ) override; - void report_html( std::ostream& ) override; }; struct tier_set_count_t : sim_controller_t @@ -39,8 +35,5 @@ struct tier_set_count_t : sim_controller_t tier_set_count_t( sim_t*, player_t*, set_bonus_type_e, set_bonus_e ); const std::string name() const override { return "tier_set_count"; } bool evaluate_post_init() override; - bool evaluate_post_iter() override; - void report_json_profileset( js::JsonOutput& ) override; - void report_json_options( js::JsonOutput& ) override; - void report_html( std::ostream& ) override; + const std::string reason() const override; }; From 19e96f8c0be59e3cbc7d3d3b951cf8b6179660e0 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 26 Oct 2025 03:40:07 -0600 Subject: [PATCH 07/21] [sim_controllers] Move the last of the implementation details out of `sim_t`. --- engine/class_modules/monk/sc_monk.cpp | 4 ++-- engine/sim/sim.hpp | 31 +++------------------------ engine/sim/sim_controller.hpp | 28 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 30 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index df24262c8d7..abfcba7614c 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6893,8 +6893,8 @@ void monk_t::init() { base_t::init(); - // sim->register_sim_controller( this, STAT_CRIT_RATING, 100 ); - sim->register_sim_controller( this, TWW2, B2 ); + // sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100 ); + sim_controller_t::register_sim_controller( sim, this, TWW2, B2 ); } // monk_t::init_spells ====================================================== diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 1e09981b950..ad18f15e4e9 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -95,6 +95,9 @@ struct sim_controller_t static const std::string call_point_string( call_point_e call_point ); static void evaluate( sim_t* sim, call_point_e call_point ); + template , bool>> + static bool register_sim_controller( sim_t* sim, Args&&... args ); private: sim_t* parent; @@ -875,19 +878,6 @@ struct sim_t : private sc_thread_t { return _rng; } double averaged_range( double min, double max ); - template , bool>> - bool register_sim_controller( Args&&... args ) - { - if ( profileset_enabled && parent ) - { - sim_controllers.emplace_back( std::make_shared( this, std::forward( args )... ) ); - return parent->sim_controller_data - .emplace( sim_controllers.back()->name(), std::make_shared() ) - .second; - } - return false; - } // Thread id of this sim_t object #ifndef SC_NO_THREADING @@ -942,18 +932,3 @@ struct sim_t : private sc_thread_t void disable_debug_seed(); bool requires_cleanup() const; }; - -template -data_wrapper_t sim_controller_t::get_data() -{ - auto& data = parent->sim_controller_data.at( name() ); - return { *std::static_pointer_cast( data.data ), data.mutex }; -} - -template -void sim_controller_t::set_data( T&& data ) -{ - auto& scd = parent->sim_controller_data; - assert( scd.find( name() ) != scd.end() ); - scd[ name() ].data = std::make_shared( data ); -} diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 545eb98a250..a46218d5f3a 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -5,6 +5,34 @@ #include "player/player.hpp" #include "sc_enums.hpp" +template +data_wrapper_t sim_controller_t::get_data() +{ + auto& data = parent->sim_controller_data.at( name() ); + return { *std::static_pointer_cast( data.data ), data.mutex }; +} + +template +void sim_controller_t::set_data( T&& data ) +{ + auto& scd = parent->sim_controller_data; + assert( scd.find( name() ) != scd.end() ); + scd[ name() ].data = std::make_shared( data ); +} + +template +bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) +{ + if ( sim && sim->profileset_enabled && sim->parent ) + { + sim->sim_controllers.emplace_back( std::make_shared( sim, std::forward( args )... ) ); + return sim->parent->sim_controller_data + .emplace( sim->sim_controllers.back()->name(), std::make_shared() ) + .second; + } + return false; +} + struct min_player_stat_t : sim_controller_t { /* From 7508703dc2b795870486edb3d65efb9de0bb1df7 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 26 Oct 2025 04:03:30 -0600 Subject: [PATCH 08/21] [sim_controllers] Move more implementation out of `sim.[h/c]pp`. Log point at which a controller causes a profileset to bail out, as well as reason. --- engine/sim/sim.cpp | 83 -------------------------------- engine/sim/sim.hpp | 42 ++++++++++++----- engine/sim/sim_controller.cpp | 89 +++++++++++++++++++++++++++++++++++ engine/sim/sim_controller.hpp | 40 +++++++++------- 4 files changed, 143 insertions(+), 111 deletions(-) diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index a7791112bf9..c87e153a74f 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -4734,86 +4734,3 @@ bool sim_t::rethrow_exception_queue() return false; } - -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) -{ -} - -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) - : mutex(), data( data ) -{ -} - -sim_controller_data_t::sim_controller_data_t() -{ -} - -sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) -{ -} - -sim_controller_t::sim_controller_t( sim_t* sim ) : parent( sim->parent ), sim( sim ) -{ -} - -const std::string sim_controller_t::message( call_point_e call_point ) const -{ - std::string msg = - fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), - call_point_string( call_point ) ); - if ( call_point == POST_ITER ) - msg += std::to_string( sim->current_iteration ); - - if ( const std::string r = reason(); r != "" ) - msg += fmt::format( " because {}.", r ); - else - msg += "."; - - return msg; -} - -const std::string sim_controller_t::call_point_string( call_point_e call_point ) -{ - switch ( call_point ) - { - case POST_INIT: - return "simulation initialization"; - case POST_ITER: - return "iteration"; - default: - assert( false ); - return "no matching call point"; - } -} - -void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) -{ - if ( !sim->profileset_enabled || !sim->parent ) - return; - - typedef std::shared_ptr iter_t; - std::function cb; - switch ( call_point ) - { - case POST_INIT: - cb = []( iter_t& sc ) { return !sc->evaluate_post_init(); }; - break; - case POST_ITER: - cb = []( iter_t& sc ) { return !sc->evaluate_post_iter(); }; - break; - default: - assert( false ); - break; - } - auto sc = range::find_if( sim->sim_controllers, cb ); - if ( sc == sim->sim_controllers.end() ) - return; - - std::shared_ptr& controller = *sc; - assert( controller->sim == sim ); - assert( controller->parent == sim->parent ); - - sim->canceled = true; - sim->error( controller->message( call_point ) ); - sim->interrupt(); -} diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index ad18f15e4e9..15980fe63fa 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -58,7 +58,9 @@ struct data_wrapper_t { T& data; - data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) {} + data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) + { + } private: std::scoped_lock lock; @@ -77,18 +79,14 @@ struct sim_controller_data_wrapper_t sim_controller_data_wrapper_t( const sim_controller_data_wrapper_t& ) = delete; }; -struct sim_controller_data_t -{ - sim_controller_data_t(); - sim_controller_data_t( sim_controller_data_t& data ); -}; - +// local profileset data and methods struct sim_controller_t { using data_t = sim_controller_data_t; enum call_point_e { + NONE, POST_INIT, POST_ITER }; @@ -101,18 +99,33 @@ struct sim_controller_t private: sim_t* parent; + call_point_e exit_point; + std::string exit_reason; + public: sim_t* sim; sim_controller_t( sim_t* sim ); virtual ~sim_controller_t() = default; + sim_controller_t( sim_controller_t& ) = delete; + sim_controller_t( const sim_controller_t& ) = delete; + const std::string message( call_point_e ) const; - virtual const std::string name() const = 0; - virtual const std::string reason() const { return {}; } - virtual bool evaluate_post_init() { return true; }; - virtual bool evaluate_post_iter() { return true; }; + virtual const std::string name() const = 0; + virtual const std::string reason() const = 0; + + virtual bool evaluate_post_init() + { + return true; + } + + virtual bool evaluate_post_iter() + { + return true; + } + virtual void report_json_profileset( js::JsonOutput& ) {}; virtual void report_json_options( js::JsonOutput& ) {}; virtual void report_html( std::ostream& ) {}; @@ -124,6 +137,13 @@ struct sim_controller_t void set_data( T&& data ); }; +// global profileset data to be shared across all instantiations of a derived `sim_controller_t` +struct sim_controller_data_t +{ + sim_controller_data_t(); + sim_controller_data_t( sim_controller_data_t& data ); +}; + struct sim_progress_t { int current_iterations; diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 4fd58b11db6..7953187414e 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,6 +1,95 @@ #include "sim_controller.hpp" #include "player/set_bonus.hpp" +#include "profileset.hpp" +#include "sim.hpp" + +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) +{ +} + +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) + : mutex(), data( data ) +{ +} + +sim_controller_data_t::sim_controller_data_t() +{ +} + +sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) +{ +} + +sim_controller_t::sim_controller_t( sim_t* sim ) + : parent( sim->parent ), exit_point( sim_controller_t::NONE ), exit_reason( {} ), sim( sim ) +{ +} + +const std::string sim_controller_t::call_point_string( call_point_e call_point ) +{ + switch ( call_point ) + { + case POST_INIT: + return "simulation initialization"; + case POST_ITER: + return "iteration"; + default: + assert( false ); + return "no matching call point"; + } +} + +void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) +{ + if ( !sim->profileset_enabled || !sim->parent ) + return; + + typedef std::shared_ptr iter_t; + std::function cb; + switch ( call_point ) + { + case POST_INIT: + cb = []( iter_t& sc ) { return !sc->evaluate_post_init(); }; + break; + case POST_ITER: + cb = []( iter_t& sc ) { return !sc->evaluate_post_iter(); }; + break; + default: + assert( false ); + break; + } + auto sc = range::find_if( sim->sim_controllers, cb ); + if ( sc == sim->sim_controllers.end() ) + return; + + std::shared_ptr& controller = *sc; + assert( controller->sim == sim ); + assert( controller->parent == sim->parent ); + + controller->exit_point = call_point; + controller->exit_reason = controller->reason(); + + sim->canceled = true; + sim->error( controller->message( call_point ) ); + sim->interrupt(); +} + +const std::string sim_controller_t::message( call_point_e call_point ) const +{ + std::string msg = + fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), + call_point_string( call_point ) ); + if ( call_point == POST_ITER ) + msg += std::to_string( sim->current_iteration ); + + if ( const std::string r = reason(); r != "" ) + msg += fmt::format( " because {}.", r ); + else + msg += "."; + + return msg; +} min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) : sim_controller_t( sim ), target_player( target_player ), rating( rating ), min_rating( amount ) diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index a46218d5f3a..484bf4135b7 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -1,9 +1,22 @@ #pragma once -#include "sim.hpp" -#include "player/rating.hpp" #include "player/player.hpp" +#include "player/rating.hpp" #include "sc_enums.hpp" +#include "sim.hpp" + +template +bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) +{ + if ( sim && sim->profileset_enabled && sim->parent ) + { + sim->sim_controllers.emplace_back( std::make_shared( sim, std::forward( args )... ) ); + return sim->parent->sim_controller_data + .emplace( sim->sim_controllers.back()->name(), std::make_shared() ) + .second; + } + return false; +} template data_wrapper_t sim_controller_t::get_data() @@ -20,19 +33,6 @@ void sim_controller_t::set_data( T&& data ) scd[ name() ].data = std::make_shared( data ); } -template -bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) -{ - if ( sim && sim->profileset_enabled && sim->parent ) - { - sim->sim_controllers.emplace_back( std::make_shared( sim, std::forward( args )... ) ); - return sim->parent->sim_controller_data - .emplace( sim->sim_controllers.back()->name(), std::make_shared() ) - .second; - } - return false; -} - struct min_player_stat_t : sim_controller_t { /* @@ -48,7 +48,10 @@ struct min_player_stat_t : sim_controller_t double min_rating; min_player_stat_t( sim_t*, player_t*, stat_e, double ); - const std::string name() const override { return "min_player_stat"; } + const std::string name() const override + { + return "min_player_stat"; + } bool evaluate_post_init() override; }; @@ -61,7 +64,10 @@ struct tier_set_count_t : sim_controller_t set_bonus_e count; tier_set_count_t( sim_t*, player_t*, set_bonus_type_e, set_bonus_e ); - const std::string name() const override { return "tier_set_count"; } + const std::string name() const override + { + return "tier_set_count"; + } bool evaluate_post_init() override; const std::string reason() const override; }; From 831c9dffed9cdcffe5eb39868a931235d6a3d2f2 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 26 Oct 2025 04:46:09 -0600 Subject: [PATCH 09/21] [sim_controllers] `sim_controller_t::message` should probably not be const. --- engine/sim/sim.hpp | 2 +- engine/sim/sim_controller.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 15980fe63fa..7f4c325dd73 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -111,7 +111,7 @@ struct sim_controller_t sim_controller_t( sim_controller_t& ) = delete; sim_controller_t( const sim_controller_t& ) = delete; - const std::string message( call_point_e ) const; + const std::string message( call_point_e ); virtual const std::string name() const = 0; virtual const std::string reason() const = 0; diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 7953187414e..e41b155519f 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -75,7 +75,7 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) sim->interrupt(); } -const std::string sim_controller_t::message( call_point_e call_point ) const +const std::string sim_controller_t::message( call_point_e call_point ) { std::string msg = fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), From b0add172008ff9e67c1b7a7131461cfe4fc24529 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 27 Oct 2025 06:07:02 -0600 Subject: [PATCH 10/21] [sim_controllers] Replace `std::shared_ptr` with `std::unique_ptr` to clarify ownership. --- engine/sim/sim.hpp | 5 ++--- engine/sim/sim_controller.cpp | 10 +++++----- engine/sim/sim_controller.hpp | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 7f4c325dd73..363a07ae854 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -47,8 +47,7 @@ namespace report::json class report_configuration_t; } -namespace profileset -{ +namespace profileset{ class profilesets_t; } @@ -720,7 +719,7 @@ struct sim_t : private sc_thread_t // sim control private: friend sim_controller_t; - std::vector> sim_controllers; + std::vector> sim_controllers; std::map sim_controller_data; public: diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index e41b155519f..fc0616b6a39 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -45,15 +45,15 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) if ( !sim->profileset_enabled || !sim->parent ) return; - typedef std::shared_ptr iter_t; - std::function cb; + typedef std::unique_ptr sc_ptr_t; + std::function cb; switch ( call_point ) { case POST_INIT: - cb = []( iter_t& sc ) { return !sc->evaluate_post_init(); }; + cb = []( sc_ptr_t& sc ) { return !sc->evaluate_post_init(); }; break; case POST_ITER: - cb = []( iter_t& sc ) { return !sc->evaluate_post_iter(); }; + cb = []( sc_ptr_t& sc ) { return !sc->evaluate_post_iter(); }; break; default: assert( false ); @@ -63,7 +63,7 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) if ( sc == sim->sim_controllers.end() ) return; - std::shared_ptr& controller = *sc; + auto* controller = sc->get(); assert( controller->sim == sim ); assert( controller->parent == sim->parent ); diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 484bf4135b7..20387c2b6c8 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -10,7 +10,7 @@ bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) { if ( sim && sim->profileset_enabled && sim->parent ) { - sim->sim_controllers.emplace_back( std::make_shared( sim, std::forward( args )... ) ); + sim->sim_controllers.emplace_back( std::make_unique( sim, std::forward( args )... ) ); return sim->parent->sim_controller_data .emplace( sim->sim_controllers.back()->name(), std::make_shared() ) .second; From 5f92ce344eb53662dd2fe85f739bb5c20eee55c6 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 27 Oct 2025 06:35:30 -0600 Subject: [PATCH 11/21] for real this time? --- engine/sim/sim_controller.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index fc0616b6a39..38f59063861 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,5 +1,6 @@ #include "sim_controller.hpp" +#include "sc_enums.hpp" #include "player/set_bonus.hpp" #include "profileset.hpp" #include "sim.hpp" @@ -71,7 +72,7 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) controller->exit_reason = controller->reason(); sim->canceled = true; - sim->error( controller->message( call_point ) ); + sim->error( error_level_e::TRIVIAL, "{}", controller->message( call_point ) ); sim->interrupt(); } From 45f294204f4778e824f3c0a13456902d6a00ad79 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 31 Oct 2025 00:49:42 -0600 Subject: [PATCH 12/21] [sim_controllers] Initial reporting. --- engine/report/json/report_json.cpp | 11 +++++++++++ engine/report/report_html_sim.cpp | 22 ++++++++++++++++++++++ engine/sim/sim.hpp | 23 ++++++++++++++++++----- engine/sim/sim_controller.cpp | 26 ++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/engine/report/json/report_json.cpp b/engine/report/json/report_json.cpp index 5a25c85d0d6..b5f5e2e77b8 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1047,6 +1047,11 @@ void profileset_json2( const profileset::profilesets_t& profileset, const sim_t& } } + // report source, location, and reason of interrupt for + // all registered profileset sim controllers + for ( const auto& controller: sim.sim_controllers ) + controller->report_json_profileset( obj ); + // Optional override ouput data if ( !sim.profileset_output_data.empty() ) { @@ -1092,6 +1097,12 @@ void profileset_json3( const profileset::profilesets_t& profilesets, const sim_t obj[ "iterations" ] = as( result.iterations() ); } + // report source, location, and reason of interrupt for + // all registered profileset sim controllers + for ( const auto& controller: sim.sim_controllers ) + controller->report_json_profileset( obj ); + + // Optional override ouput data if ( !sim.profileset_output_data.empty() ) { diff --git a/engine/report/report_html_sim.cpp b/engine/report/report_html_sim.cpp index b1c0bd7520d..c1e373dcbed 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1160,6 +1160,28 @@ void print_profilesets( std::ostream& out, const profileset::profilesets_t& prof print_profilesets_chart( out, sim ); + + if ( sim.sim_controllers.size() ) + { + out << "

Profileset Sim Control

\n"; + out << "
\n"; + + out << "
Sim Controllers
    \n"; + for ( const auto& controller: sim.sim_controllers ) + controller->report_html_options( out ); + out << "
\n"; + + // report source, location, and reason of interrupt for + // all registered profileset sim controllers + // TODO: check if any are culled, otherwise omit table + out << "
Interrupted Profilesets
    \n"; + for ( const auto& controller: sim.sim_controllers ) + controller->report_html_profileset( out ); + out << "
\n"; + out << "
"; + } + + out << ""; out << ""; } diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 363a07ae854..125354070ac 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -125,9 +125,24 @@ struct sim_controller_t return true; } - virtual void report_json_profileset( js::JsonOutput& ) {}; - virtual void report_json_options( js::JsonOutput& ) {}; - virtual void report_html( std::ostream& ) {}; + + void report_json_profileset( js::JsonOutput& ); + void report_json_options( js::JsonOutput& ); + void report_html_profileset( std::ostream& ); + void report_html_options( std::ostream& ); + /* + * TODO: controllers currently cannot sensibly report, as only `parent` likely + * exists as of the time of report generation. upon destruction, profileset exit + * points and reasons must be collected by the parent sim for safekeeping + * + * on each profileset: + * interrupt_by: name() + * exit_point: to_string(exit_point) + * exit_reason: exit_reason + * + * on sim: + * TODO: config representation (active controllers, configuration parameters) + */ protected: template @@ -717,8 +732,6 @@ struct sim_t : private sc_thread_t bool merge_enemy_priority_dmg; // sim control -private: - friend sim_controller_t; std::vector> sim_controllers; std::map sim_controller_data; diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 38f59063861..df91dc1198e 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -92,6 +92,32 @@ const std::string sim_controller_t::message( call_point_e call_point ) return msg; } +void sim_controller_t::report_json_profileset( js::JsonOutput& output ) +{ + if ( !exit_point || exit_reason.empty() ) + return; + + output["interrupted_by"] = name(); + output["exit_point"] = call_point_string( exit_point ); + output["exit_reason"] = exit_reason; +} + +void sim_controller_t::report_json_options( js::JsonOutput& ) +{ + // TODO: implement opt parsing and automatic generation of report json from opts +} + +void sim_controller_t::report_html_profileset( std::ostream& output ) +{ + output << "
  • " + << util::encode_html( call_point_string( exit_point ) ) + << util::encode_html( exit_reason ) + << "
  • "; +} + +void sim_controller_t::report_html_options( std::ostream& ) +{} + min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) : sim_controller_t( sim ), target_player( target_player ), rating( rating ), min_rating( amount ) { From b4089fa3ae137bcd8fafeac18c0a562ece3237a7 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 31 Oct 2025 01:54:24 -0600 Subject: [PATCH 13/21] [sim_controllers] Fix HTML reporting and rearrange data so the lifetimes make sense. --- engine/class_modules/monk/sc_monk.cpp | 2 +- engine/report/json/report_json.cpp | 8 ++-- engine/report/report_html_sim.cpp | 11 ++--- engine/sim/sim.hpp | 59 +++++++++++++-------------- engine/sim/sim_controller.cpp | 56 +++++++++++++++---------- engine/sim/sim_controller.hpp | 5 ++- 6 files changed, 78 insertions(+), 63 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index abfcba7614c..02e63d01ea8 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6893,7 +6893,7 @@ void monk_t::init() { base_t::init(); - // sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100 ); + sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100.0 ); sim_controller_t::register_sim_controller( sim, this, TWW2, B2 ); } diff --git a/engine/report/json/report_json.cpp b/engine/report/json/report_json.cpp index b5f5e2e77b8..0f50e3f174f 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1049,8 +1049,8 @@ void profileset_json2( const profileset::profilesets_t& profileset, const sim_t& // report source, location, and reason of interrupt for // all registered profileset sim controllers - for ( const auto& controller: sim.sim_controllers ) - controller->report_json_profileset( obj ); + // for ( const auto& controller: sim.sim_controllers ) + // controller->report_json_profileset( obj ); // Optional override ouput data if ( !sim.profileset_output_data.empty() ) @@ -1099,8 +1099,8 @@ void profileset_json3( const profileset::profilesets_t& profilesets, const sim_t // report source, location, and reason of interrupt for // all registered profileset sim controllers - for ( const auto& controller: sim.sim_controllers ) - controller->report_json_profileset( obj ); + // for ( const auto& controller: sim.sim_controllers ) + // controller->report_json_profileset( obj ); // Optional override ouput data diff --git a/engine/report/report_html_sim.cpp b/engine/report/report_html_sim.cpp index c1e373dcbed..3bdafdb5b73 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1161,22 +1161,23 @@ void print_profilesets( std::ostream& out, const profileset::profilesets_t& prof print_profilesets_chart( out, sim ); - if ( sim.sim_controllers.size() ) + if ( sim.sim_controller_data.size() ) { out << "

    Profileset Sim Control

    \n"; out << "
    \n"; out << "
    Sim Controllers
      \n"; - for ( const auto& controller: sim.sim_controllers ) - controller->report_html_options( out ); + for ( const auto& [ key, controller_data ] : sim.sim_controller_data ) + controller_data.report_html_options( out ); out << "
    \n"; // report source, location, and reason of interrupt for // all registered profileset sim controllers // TODO: check if any are culled, otherwise omit table out << "
    Interrupted Profilesets
      \n"; - for ( const auto& controller: sim.sim_controllers ) - controller->report_html_profileset( out ); + for ( const auto& [ key, controller_data ] : sim.sim_controller_data ) + if ( controller_data.exit_reasons.size() ) + controller_data.report_html_profileset( out ); out << "
    \n"; out << "
    "; } diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 125354070ac..68f8ae47c73 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -57,24 +57,33 @@ struct data_wrapper_t { T& data; - data_wrapper_t( T& data, std::mutex& m ) : data( data ), lock( m ) + data_wrapper_t( T& data, std::recursive_mutex& m ) : data( data ), lock( m ) { } private: - std::scoped_lock lock; + std::scoped_lock lock; }; +struct exit_reason_t; struct sim_controller_data_wrapper_t { - std::mutex mutex; - std::shared_ptr data; + std::recursive_mutex mutex; + std::unique_ptr data; + std::vector exit_reasons; + // configuration for reporting sim_controller_data_wrapper_t(); - sim_controller_data_wrapper_t( std::shared_ptr data ); + sim_controller_data_wrapper_t( std::unique_ptr&& data ); + + ~sim_controller_data_wrapper_t() = default; + + void report_json_profileset( js::JsonOutput& ) const; + void report_json_options( js::JsonOutput& ) const; + void report_html_profileset( std::ostream& ) const; + void report_html_options( std::ostream& ) const; // disallow copy, as that would introduce additional mutexes for a single controller name - sim_controller_data_wrapper_t( sim_controller_data_wrapper_t& ) = delete; sim_controller_data_wrapper_t( const sim_controller_data_wrapper_t& ) = delete; }; @@ -96,12 +105,7 @@ struct sim_controller_t typename = typename std::enable_if_t, bool>> static bool register_sim_controller( sim_t* sim, Args&&... args ); -private: sim_t* parent; - call_point_e exit_point; - std::string exit_reason; - -public: sim_t* sim; sim_controller_t( sim_t* sim ); @@ -125,25 +129,6 @@ struct sim_controller_t return true; } - - void report_json_profileset( js::JsonOutput& ); - void report_json_options( js::JsonOutput& ); - void report_html_profileset( std::ostream& ); - void report_html_options( std::ostream& ); - /* - * TODO: controllers currently cannot sensibly report, as only `parent` likely - * exists as of the time of report generation. upon destruction, profileset exit - * points and reasons must be collected by the parent sim for safekeeping - * - * on each profileset: - * interrupt_by: name() - * exit_point: to_string(exit_point) - * exit_reason: exit_reason - * - * on sim: - * TODO: config representation (active controllers, configuration parameters) - */ - protected: template data_wrapper_t get_data(); @@ -151,11 +136,25 @@ struct sim_controller_t void set_data( T&& data ); }; +struct exit_reason_t +{ + const std::string profileset_name; + const sim_controller_t::call_point_e exit_point; + const std::string exit_reason; + + exit_reason_t( const std::string profileset_name, const sim_controller_t::call_point_e exit_point, const std::string exit_reason ) + : profileset_name( profileset_name ), exit_point( exit_point ), exit_reason( exit_reason ) + {} +}; + // global profileset data to be shared across all instantiations of a derived `sim_controller_t` struct sim_controller_data_t { sim_controller_data_t(); sim_controller_data_t( sim_controller_data_t& data ); + sim_controller_data_t( const sim_controller_data_t& ) = delete; + + virtual ~sim_controller_data_t() = default; }; struct sim_progress_t diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index df91dc1198e..12d2243db4a 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -5,12 +5,12 @@ #include "profileset.hpp" #include "sim.hpp" -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data( nullptr ) +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data(), exit_reasons() { } -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::shared_ptr data ) - : mutex(), data( data ) +sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::unique_ptr&& data ) + : mutex(), data( std::move( data ) ), exit_reasons() { } @@ -23,8 +23,10 @@ sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) } sim_controller_t::sim_controller_t( sim_t* sim ) - : parent( sim->parent ), exit_point( sim_controller_t::NONE ), exit_reason( {} ), sim( sim ) + : parent( sim->parent ), sim( sim ) { + assert( sim ); + assert( sim->parent ); } const std::string sim_controller_t::call_point_string( call_point_e call_point ) @@ -68,8 +70,12 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) assert( controller->sim == sim ); assert( controller->parent == sim->parent ); - controller->exit_point = call_point; - controller->exit_reason = controller->reason(); + auto& scd = sim->parent->sim_controller_data.at( controller->name() ); + std::scoped_lock L( scd.mutex ); + + auto name = controller->name(); + scd.exit_reasons.emplace_back( sim->parent->profilesets->current_profileset_name(), call_point, + controller->reason() ); sim->canceled = true; sim->error( error_level_e::TRIVIAL, "{}", controller->message( call_point ) ); @@ -92,30 +98,32 @@ const std::string sim_controller_t::message( call_point_e call_point ) return msg; } -void sim_controller_t::report_json_profileset( js::JsonOutput& output ) +void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const { - if ( !exit_point || exit_reason.empty() ) - return; - - output["interrupted_by"] = name(); - output["exit_point"] = call_point_string( exit_point ); - output["exit_reason"] = exit_reason; + for ( const exit_reason_t& exit_reason : exit_reasons ) + { + output[ "interrupted_by" ] = exit_reason.profileset_name; + output[ "exit_point" ] = sim_controller_t::call_point_string( exit_reason.exit_point ); + output[ "exit_reason" ] = exit_reason.exit_reason; + } } -void sim_controller_t::report_json_options( js::JsonOutput& ) +void sim_controller_data_wrapper_t::report_json_options( js::JsonOutput& ) const { // TODO: implement opt parsing and automatic generation of report json from opts } -void sim_controller_t::report_html_profileset( std::ostream& output ) +void sim_controller_data_wrapper_t::report_html_profileset( std::ostream& output ) const { - output << "
  • " - << util::encode_html( call_point_string( exit_point ) ) - << util::encode_html( exit_reason ) - << "
  • "; + for ( const exit_reason_t& exit_reason : exit_reasons ) + output << "
  • " + << util::encode_html( exit_reason.profileset_name ) << " " + << util::encode_html( sim_controller_t::call_point_string( exit_reason.exit_point ) ) << " " + << util::encode_html( exit_reason.exit_reason ) + << "
  • "; } -void sim_controller_t::report_html_options( std::ostream& ) +void sim_controller_data_wrapper_t::report_html_options( std::ostream& ) const {} min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) @@ -125,7 +133,13 @@ min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_ bool min_player_stat_t::evaluate_post_init() { - return target_player->get_stat_value( rating ) >= min_rating; + return true; +} + +const std::string min_player_stat_t::reason() const +{ + return fmt::format( "player {} does not exceed {} rating for {}", target_player->name(), + min_rating, util::stat_type_string( rating ) ); } tier_set_count_t::tier_set_count_t( sim_t* sim, player_t* target_player, set_bonus_type_e tier, set_bonus_e count ) diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 20387c2b6c8..947a2b69b41 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -12,7 +12,7 @@ bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) { sim->sim_controllers.emplace_back( std::make_unique( sim, std::forward( args )... ) ); return sim->parent->sim_controller_data - .emplace( sim->sim_controllers.back()->name(), std::make_shared() ) + .emplace( sim->sim_controllers.back()->name(), std::make_unique() ) .second; } return false; @@ -30,7 +30,7 @@ void sim_controller_t::set_data( T&& data ) { auto& scd = parent->sim_controller_data; assert( scd.find( name() ) != scd.end() ); - scd[ name() ].data = std::make_shared( data ); + scd[ name() ].data = std::make_unique( data ); } struct min_player_stat_t : sim_controller_t @@ -53,6 +53,7 @@ struct min_player_stat_t : sim_controller_t return "min_player_stat"; } bool evaluate_post_init() override; + const std::string reason() const override; }; struct tier_set_count_t : sim_controller_t From ed8e9a09a8ea7e2f0d5e03513931b4e8d92a86e8 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 31 Oct 2025 05:14:16 -0600 Subject: [PATCH 14/21] [sim_controllers] Start implementing options. --- engine/sim/sim.hpp | 4 +++- engine/sim/sim_controller.cpp | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 68f8ae47c73..6768ba36c17 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -71,7 +71,7 @@ struct sim_controller_data_wrapper_t std::recursive_mutex mutex; std::unique_ptr data; std::vector exit_reasons; - // configuration for reporting + std::vector> options; sim_controller_data_wrapper_t(); sim_controller_data_wrapper_t( std::unique_ptr&& data ); @@ -115,9 +115,11 @@ struct sim_controller_t sim_controller_t( const sim_controller_t& ) = delete; const std::string message( call_point_e ); + void add_option( std::unique_ptr ); virtual const std::string name() const = 0; virtual const std::string reason() const = 0; + virtual void create_options() {} virtual bool evaluate_post_init() { diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 12d2243db4a..33631bf19bb 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -73,7 +73,6 @@ void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) auto& scd = sim->parent->sim_controller_data.at( controller->name() ); std::scoped_lock L( scd.mutex ); - auto name = controller->name(); scd.exit_reasons.emplace_back( sim->parent->profilesets->current_profileset_name(), call_point, controller->reason() ); @@ -98,6 +97,10 @@ const std::string sim_controller_t::message( call_point_e call_point ) return msg; } +void sim_controller_t::add_option( std::unique_ptr option ) +{ +} + void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const { for ( const exit_reason_t& exit_reason : exit_reasons ) From 0a5e34adc870a15ad5e9a48dcc3fd78ebb368694 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Thu, 4 Dec 2025 03:43:06 -0700 Subject: [PATCH 15/21] checkpoint --- engine/class_modules/monk/sc_monk.cpp | 31 +- engine/report/report_html_sim.cpp | 23 +- engine/sim/sim.cpp | 27 +- engine/sim/sim.hpp | 480 +++++++++++--------------- engine/sim/sim_controller.cpp | 290 ++++++++++++---- engine/sim/sim_controller.hpp | 135 ++++++-- 6 files changed, 574 insertions(+), 412 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 02e63d01ea8..ba2f883e982 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -36,6 +36,7 @@ #include "report/charts.hpp" #include "report/highchart.hpp" #include "sc_enums.hpp" +#include "sim/sim_controller.hpp" #include @@ -4346,7 +4347,7 @@ struct xuen_spell_t : public monk_spell_t cast_during_sck = true; // Specifically set for 10.1 class trinket - harmful = true; + harmful = true; } void execute() override @@ -4577,7 +4578,7 @@ struct niuzao_spell_t : public monk_spell_t // Specifically set for 10.1 class trinket harmful = true; // Forcing the minimum GCD to 750 milliseconds - min_gcd = timespan_t::from_millis( 750 ); + min_gcd = timespan_t::from_millis( 750 ); apply_affecting_aura( p->talent.brewmaster.walk_with_the_ox ); } @@ -4630,7 +4631,7 @@ struct chiji_spell_t : public monk_spell_t // Specifically set for 10.1 class trinket harmful = true; // Forcing the minimum GCD to 750 milliseconds - min_gcd = timespan_t::from_millis( 750 ); + min_gcd = timespan_t::from_millis( 750 ); } void execute() override @@ -4659,7 +4660,7 @@ struct yulon_spell_t : public monk_spell_t // Specifically set for 10.1 class trinket harmful = true; // Forcing the minimum GCD to 750 milliseconds - min_gcd = timespan_t::from_millis( 750 ); + min_gcd = timespan_t::from_millis( 750 ); } void execute() override @@ -6891,10 +6892,28 @@ bool monk_t::validate_fight_style( fight_style_e style ) const void monk_t::init() { + auto t_pc = []( sim_t *sim, unsigned int id ) { return std::make_unique( sim, id ); }; + auto t_pcd = []( std::string_view options ) { return std::make_unique( options ); }; + profileset_controller_t::register_controller( "tier", { t_pc, t_pcd } ); + base_t::init(); - sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100.0 ); - sim_controller_t::register_sim_controller( sim, this, TWW2, B2 ); + // if ( !sim->parent ) + // sim->profileset_controller_data.emplace_back( "tier", "" ); + // if ( sim->parent ) + // { + // for ( auto& pcd : sim->parent->profileset_controller_data ) + // pcd.construct_controller( sim ); + // for ( auto& pc : sim->profileset_controller ) + // { + // auto c_pc = static_cast( pc.get() ); + // c_pc->target_player = this; + // c_pc->tier = TWW2; + // c_pc->count = B2; + // } + // } + // sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100.0 ); + // sim_controller_t::register_sim_controller( sim, this, TWW2, B2 ); } // monk_t::init_spells ====================================================== diff --git a/engine/report/report_html_sim.cpp b/engine/report/report_html_sim.cpp index 3bdafdb5b73..cda221bb4f8 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1160,28 +1160,7 @@ void print_profilesets( std::ostream& out, const profileset::profilesets_t& prof print_profilesets_chart( out, sim ); - - if ( sim.sim_controller_data.size() ) - { - out << "

    Profileset Sim Control

    \n"; - out << "
    \n"; - - out << "
    Sim Controllers
      \n"; - for ( const auto& [ key, controller_data ] : sim.sim_controller_data ) - controller_data.report_html_options( out ); - out << "
    \n"; - - // report source, location, and reason of interrupt for - // all registered profileset sim controllers - // TODO: check if any are culled, otherwise omit table - out << "
    Interrupted Profilesets
      \n"; - for ( const auto& [ key, controller_data ] : sim.sim_controller_data ) - if ( controller_data.exit_reasons.size() ) - controller_data.report_html_profileset( out ); - out << "
    \n"; - out << "
    "; - } - + profileset_controller_t::html_report( sim, out ); out << ""; out << ""; diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index c87e153a74f..552f62b7fee 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1546,8 +1546,8 @@ sim_t::sim_t() count_overheal_as_heal( false ), scaling_normalized( 1.0 ), merge_enemy_priority_dmg( false ), - sim_controllers(), - sim_controller_data(), + profileset_controller(), + profileset_controller_data(), // Multi-Threading threads( 0 ), thread_index( 0 ), @@ -2950,6 +2950,21 @@ void sim_t::init() plot->initialize(); } + if ( !parent && !profileset_controller_options.empty() && !profileset_map.empty() ) + { + for ( const auto& [ key, values ] : profileset_controller_options ) + { + if ( profileset_controller_t::controller_exists( key ) ) + for ( const auto& value : values ) + profileset_controller_data.emplace_back( key, value ); + else + throw sc_invalid_sim_argument( fmt::format( "Unknown profileset controller option with name '{}'.", key ) ); + } + } + if ( parent && profileset_enabled ) + for ( auto& profileset_controller_datum : parent->profileset_controller_data ) + profileset_controller_datum.construct_controller( this ); + initialized = true; init_time = chrono::elapsed(start_time); @@ -2981,7 +2996,7 @@ void sim_t::analyze() std::fflush( stdout ); } - + assert( iterations > 0 ); // Run core analyze for all actor collected data before proceeding to full analysis. This is to prevent errors from @@ -3061,7 +3076,7 @@ bool sim_t::iterate() progress_bar.init(); - sim_controller_t::evaluate( this, sim_controller_t::POST_INIT ); + profileset_controller_t::evaluate( this, POST_INIT ); try { @@ -3080,7 +3095,7 @@ bool sim_t::iterate() progress_bar.output( false ); } - sim_controller_t::evaluate( this, sim_controller_t::POST_ITER ); + profileset_controller_t::evaluate( this, POST_ITER ); do_pause(); auto old_active = current_index; @@ -3893,6 +3908,8 @@ void sim_t::create_options() add_option( opt_bool( "merge_enemy_priority_dmg", merge_enemy_priority_dmg ) ); add_option( opt_int( "decorated_tooltips", decorated_tooltips ) ); add_option( opt_uint( "spell_query_wrap", spell_query_wrap ) ); + // Sim Controller Options + add_option( opt_map_list( "profileset_controller.", profileset_controller_options ) ); // Charts add_option( opt_bool( "chart_show_relative_difference", chart_show_relative_difference ) ); add_option( opt_string( "relative_difference_base", relative_difference_base ) ); diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 6768ba36c17..41ccf6039f8 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -6,12 +6,14 @@ #pragma once #include "config.hpp" + #include "event_manager.hpp" +#include "interfaces/sc_js.hpp" #include "player/gear_stats.hpp" #include "progress_bar.hpp" -#include "sim_ostream.hpp" #include "sim/option.hpp" -#include "interfaces/sc_js.hpp" +#include "sim_controller.hpp" +#include "sim_ostream.hpp" #include "util/concurrency.hpp" #include "util/rng.hpp" #include "util/sample_data.hpp" @@ -20,7 +22,7 @@ #include #include -#include +#include struct actor_target_data_t; struct buff_t; @@ -29,7 +31,7 @@ class dbc_t; class dbc_override_t; struct expr_t; namespace highchart { - struct chart_t; +struct chart_t; } struct iteration_data_entry_t; struct option_t; @@ -48,123 +50,15 @@ class report_configuration_t; } namespace profileset{ - class profilesets_t; +class profilesets_t; } -struct sim_controller_data_t; -template -struct data_wrapper_t -{ - T& data; - - data_wrapper_t( T& data, std::recursive_mutex& m ) : data( data ), lock( m ) - { - } - -private: - std::scoped_lock lock; -}; - -struct exit_reason_t; -struct sim_controller_data_wrapper_t -{ - std::recursive_mutex mutex; - std::unique_ptr data; - std::vector exit_reasons; - std::vector> options; - - sim_controller_data_wrapper_t(); - sim_controller_data_wrapper_t( std::unique_ptr&& data ); - - ~sim_controller_data_wrapper_t() = default; - - void report_json_profileset( js::JsonOutput& ) const; - void report_json_options( js::JsonOutput& ) const; - void report_html_profileset( std::ostream& ) const; - void report_html_options( std::ostream& ) const; - - // disallow copy, as that would introduce additional mutexes for a single controller name - sim_controller_data_wrapper_t( const sim_controller_data_wrapper_t& ) = delete; -}; - -// local profileset data and methods -struct sim_controller_t -{ - using data_t = sim_controller_data_t; - - enum call_point_e - { - NONE, - POST_INIT, - POST_ITER - }; - - static const std::string call_point_string( call_point_e call_point ); - static void evaluate( sim_t* sim, call_point_e call_point ); - template , bool>> - static bool register_sim_controller( sim_t* sim, Args&&... args ); - - sim_t* parent; - sim_t* sim; - - sim_controller_t( sim_t* sim ); - virtual ~sim_controller_t() = default; - - sim_controller_t( sim_controller_t& ) = delete; - sim_controller_t( const sim_controller_t& ) = delete; - - const std::string message( call_point_e ); - void add_option( std::unique_ptr ); - - virtual const std::string name() const = 0; - virtual const std::string reason() const = 0; - virtual void create_options() {} - - virtual bool evaluate_post_init() - { - return true; - } - - virtual bool evaluate_post_iter() - { - return true; - } - -protected: - template - data_wrapper_t get_data(); - template - void set_data( T&& data ); -}; - -struct exit_reason_t -{ - const std::string profileset_name; - const sim_controller_t::call_point_e exit_point; - const std::string exit_reason; - - exit_reason_t( const std::string profileset_name, const sim_controller_t::call_point_e exit_point, const std::string exit_reason ) - : profileset_name( profileset_name ), exit_point( exit_point ), exit_reason( exit_reason ) - {} -}; - -// global profileset data to be shared across all instantiations of a derived `sim_controller_t` -struct sim_controller_data_t -{ - sim_controller_data_t(); - sim_controller_data_t( sim_controller_data_t& data ); - sim_controller_data_t( const sim_controller_data_t& ) = delete; - - virtual ~sim_controller_data_t() = default; -}; - struct sim_progress_t { int current_iterations; int total_iterations; double pct() const - { return std::min( 1.0, current_iterations / static_cast(total_iterations) ); } + { return std::min( 1.0, current_iterations / static_cast( total_iterations ) ); } }; /// Simulation engine @@ -193,7 +87,7 @@ struct sim_t : private sc_thread_t bool fixed_time; bool save_profiles; bool save_profile_with_actions; // When saving full profiles, include actions or not - bool save_full_profile; // save the full profile instead of only active save_e flags + bool save_full_profile; // save the full profile instead of only active save_e flags bool default_actions; // Iteration Controls @@ -207,9 +101,9 @@ struct sim_t : private sc_thread_t int analyze_error_interval, analyze_number; sim_control_t* control; - sim_t* parent; - player_t* target; - player_t* heal_target; + sim_t* parent; + player_t* target; + player_t* heal_target; vector_with_callback target_list; vector_with_callback target_non_sleeping_list; vector_with_callback player_list; @@ -217,56 +111,55 @@ struct sim_t : private sc_thread_t vector_with_callback player_non_sleeping_list; vector_with_callback healing_no_pet_list; vector_with_callback healing_pet_list; - player_t* active_player; - size_t current_index; // Current active player - int num_players; - int num_enemies; - int num_tanks; - int enemy_targets; - int healing; // Creates healing targets. Useful for ferals, I guess. + player_t* active_player; + size_t current_index; // Current active player + int num_players; + int num_enemies; + int num_tanks; + int enemy_targets; + int healing; // Creates healing targets. Useful for ferals, I guess. int global_spawn_index; - int max_player_level; + int max_player_level; rng::truncated_gauss_t queue_lag, gcd_lag, channel_lag; - timespan_t queue_gcd_reduction; - timespan_t default_cooldown_tolerance; - bool strict_gcd_queue; - double confidence, confidence_estimator; + timespan_t queue_gcd_reduction; + timespan_t default_cooldown_tolerance; + bool strict_gcd_queue; + double confidence, confidence_estimator; // Latency rng::truncated_gauss_t world_lag; - double travel_variance, default_skill; - timespan_t reaction_time, regen_periodicity; - timespan_t ignite_sampling_delta; - int optimize_expressions; - int optimize_expressions_rounds; - int current_slot; - int optimal_raid, log, debug_each; + double travel_variance, default_skill; + timespan_t reaction_time, regen_periodicity; + timespan_t ignite_sampling_delta; + int optimize_expressions; + int optimize_expressions_rounds; + int current_slot; + int optimal_raid, log, debug_each; std::vector debug_seed; - stat_e normalized_stat; + stat_e normalized_stat; std::string current_name, default_region_str, default_server_str, save_prefix_str, save_suffix_str; - bool save_talent_str; - auto_dispose< std::vector > actor_list; + bool save_talent_str; + auto_dispose> actor_list; std::string main_target_str; - int stat_cache; - int max_aoe_enemies; - bool requires_regen_event; - bool single_actor_batch; - bool allow_experimental_specializations; - bool enable_all_talents; - bool enable_all_sets; - bool enable_all_item_effects; - int progressbar_type; - int armory_retries; + int stat_cache; + int max_aoe_enemies; + bool requires_regen_event; + bool single_actor_batch; + bool allow_experimental_specializations; + bool enable_all_talents; + bool enable_all_sets; + bool enable_all_item_effects; + int progressbar_type; + int armory_retries; std::unordered_map item_slot_overrides; // Target options - double enemy_death_pct; - int rel_target_level, target_level; + double enemy_death_pct; + int rel_target_level, target_level; std::string target_race; - int target_adds; + int target_adds; std::string sim_progress_base_str, sim_progress_phase_str; - int desired_targets; // desired number of targets - int desired_tank_targets; // desired number of tank target dummy npcs - + int desired_targets; // desired number of targets + int desired_tank_targets; // desired number of tank target dummy npcs // Data access std::unique_ptr dbc; @@ -276,21 +169,21 @@ struct sim_t : private sc_thread_t gear_stats_t enchant; int timewalk; - int scale_to_itemlevel; //itemlevel to scale to. if -1, we don't scale down - bool dungeon_route_smart_targeting; // sets whether the list of mobs will be sorted by their hp - bool challenge_mode; // if active, players will get scaled down to 620 and set bonuses are deactivated - bool scale_itemlevel_down_only; // Items below the value of scale_to_itemlevel will not be scaled up. - bool disable_set_bonuses; // Disables all set bonuses. + int scale_to_itemlevel; // itemlevel to scale to. if -1, we don't scale down + bool dungeon_route_smart_targeting; // sets whether the list of mobs will be sorted by their hp + bool challenge_mode; // if active, players will get scaled down to 620 and set bonuses are deactivated + bool scale_itemlevel_down_only; // Items below the value of scale_to_itemlevel will not be scaled up. + bool disable_set_bonuses; // Disables all set bonuses. bool enable_taunts; - bool use_item_verification; // Disable use-item action verification in the simulator - std::string disable_2_set; // Disables all 2 set bonuses for the tier that this is set as - std::string disable_4_set; // Disables all 4 set bonuses for the tier that this is set as - std::string enable_2_set;// Enables all 2 set bonuses for the tier that this is set as - std::string enable_4_set; // Enables all 4 set bonuses for the tier that this is set as - const spell_data_t* pvp_rules; // Hidden aura that contains the PvP crit damage reduction - bool pvp_mode; // Enables PvP mode - reduces crit damage, adjusts PvP gear iLvl - bool auto_attacks_always_land; /// Allow Auto Attacks (white attacks) to always hit the enemy - bool log_spell_id; // Add spell data ids to log/debug output where available. (actions, buffs) + bool use_item_verification; // Disable use-item action verification in the simulator + std::string disable_2_set; // Disables all 2 set bonuses for the tier that this is set as + std::string disable_4_set; // Disables all 4 set bonuses for the tier that this is set as + std::string enable_2_set; // Enables all 2 set bonuses for the tier that this is set as + std::string enable_4_set; // Enables all 4 set bonuses for the tier that this is set as + const spell_data_t* pvp_rules; // Hidden aura that contains the PvP crit damage reduction + bool pvp_mode; // Enables PvP mode - reduces crit damage, adjusts PvP gear iLvl + bool auto_attacks_always_land; /// Allow Auto Attacks (white attacks) to always hit the enemy + bool log_spell_id; // Add spell data ids to log/debug output where available. (actions, buffs) // Actor tracking int active_enemies; @@ -331,13 +224,13 @@ struct sim_t : private sc_thread_t int bleeding; // Misc stuff needs resolving - int bloodlust; + int bloodlust; std::vector target_health; } overrides; struct auras_t { - buff_t* fallback; // generic global fallback buff + buff_t* fallback; // generic global fallback buff buff_t* arcane_intellect; buff_t* battle_shout; buff_t* mark_of_the_wild; @@ -349,81 +242,81 @@ struct sim_t : private sc_thread_t struct legion_opt_t { // Legion - int infernal_cinders_users = 1; - int engine_of_eradication_orbs = 4; - int void_stalkers_contract_targets = -1; - double specter_of_betrayal_overlap = 1.0; + int infernal_cinders_users = 1; + int engine_of_eradication_orbs = 4; + int void_stalkers_contract_targets = -1; + double specter_of_betrayal_overlap = 1.0; std::vector cradle_of_anguish_resets; } legion_opts; struct bfa_opt_t { /// Chance to spawn the rare droplet - double secrets_of_the_deep_chance = 0.1; // TODO: Guessed, needs validation + double secrets_of_the_deep_chance = 0.1; // TODO: Guessed, needs validation /// Chance that the player collects the droplet, defaults to always - double secrets_of_the_deep_collect_chance = 1.0; + double secrets_of_the_deep_collect_chance = 1.0; /// Gutripper base RPPM when target is above 30% - double gutripper_default_rppm = 2.0; + double gutripper_default_rppm = 2.0; /// Chance to pick up visage spawned by Seductive Power - double seductive_power_pickup_chance = 1.0; + double seductive_power_pickup_chance = 1.0; /// Treacherous Covenant update period. - timespan_t covenant_period = 1.0_s; + timespan_t covenant_period = 1.0_s; /// Chance to gain the buff on each Treacherous Covenant update. - double covenant_chance = 1.0; + double covenant_chance = 1.0; /// Chance to gain a stack of Incandescent Sliver each time it ticks. - double incandescent_sliver_chance = 1.0; + double incandescent_sliver_chance = 1.0; /// Fight or Flight proc attempt period - timespan_t fight_or_flight_period = 1.0_s; + timespan_t fight_or_flight_period = 1.0_s; /// Chance to gain the buff on each Fight or Flight attempt - double fight_or_flight_chance = 0.0; + double fight_or_flight_chance = 0.0; /// Chance of being silenced by Harbinger's Inscrutable Will projectile - double harbingers_inscrutable_will_silence_chance = 0.0; + double harbingers_inscrutable_will_silence_chance = 0.0; /// Chance avoiding Harbinger's Inscrutable Will projectile by moving - double harbingers_inscrutable_will_move_chance = 1.0; + double harbingers_inscrutable_will_move_chance = 1.0; /// Chance player is above 60% HP for Leggings of the Aberrant Tidesage damage proc - double aberrant_tidesage_damage_chance = 1.0; + double aberrant_tidesage_damage_chance = 1.0; /// Chance player is above 90% HP for Fa'thuul's Floodguards damage proc - double fathuuls_floodguards_damage_chance = 1.0; + double fathuuls_floodguards_damage_chance = 1.0; /// Chance player is above 90% HP for Grips of Forgotten Sanity damage proc - double grips_of_forsaken_sanity_damage_chance = 1.0; + double grips_of_forsaken_sanity_damage_chance = 1.0; /// Chance player takes damage and loses Untouchable from Stormglide Steps - double stormglide_steps_take_damage_chance = 0.0; + double stormglide_steps_take_damage_chance = 0.0; /// Duration of the Lurker's Insidious Gift buff, the player can cancel it early to avoid unnecessary damage. 0 = full duration - timespan_t lurkers_insidious_gift_duration = 0_ms; + timespan_t lurkers_insidious_gift_duration = 0_ms; /// Expected duration (in seconds) of shield from Abyssal Speaker's Gauntlets. 0 = full duration - timespan_t abyssal_speakers_gauntlets_shield_duration = 0_ms; + timespan_t abyssal_speakers_gauntlets_shield_duration = 0_ms; /// Expected duration of the absorb provided by Trident of Deep Ocean. 0 = full duration - timespan_t trident_of_deep_ocean_duration = 0_ms; + timespan_t trident_of_deep_ocean_duration = 0_ms; /// Chance that the player has a higher health percentage than the target for Legplates of Unbound Anguish proc - double legplates_of_unbound_anguish_chance = 1.0; + double legplates_of_unbound_anguish_chance = 1.0; /// Period to check for if an ally dies with Loyal to the End - timespan_t loyal_to_the_end_ally_death_timer = 60_s; + timespan_t loyal_to_the_end_ally_death_timer = 60_s; /// Chance on every check to see if an ally dies with Loyal to the End - double loyal_to_the_end_ally_death_chance = 0.0; + double loyal_to_the_end_ally_death_chance = 0.0; /// Number of allies with the Loyal to the End azerite trait, default = 4 (max) - int loyal_to_the_end_allies = 0; + int loyal_to_the_end_allies = 0; /// Number of allies also using the Worldvein Resonance minor - int worldvein_allies = 0; + int worldvein_allies = 0; /// Chance to proc Reality Shift (normally triggers on moving specific distance) - double ripple_in_space_proc_chance = 0.0; + double ripple_in_space_proc_chance = 0.0; /// Chance to be in range to hit with Blood of the Enemy major power (12 yd PBAoE) - double blood_of_the_enemy_in_range = 1.0; + double blood_of_the_enemy_in_range = 1.0; /// Period to check for if Undulating Tides gets locked out - timespan_t undulating_tides_lockout_timer = 60_s; + timespan_t undulating_tides_lockout_timer = 60_s; /// Chance on every check to see if Undulating Tides gets locked out - double undulating_tides_lockout_chance = 0.0; + double undulating_tides_lockout_chance = 0.0; /// Base RPPM for Leviathan's Lure - double leviathans_lure_base_rppm = 0.75; + double leviathans_lure_base_rppm = 0.75; /// Chance to catch returning wave of Aquipotent Nautilus - double aquipotent_nautilus_catch_chance = 1.0; + double aquipotent_nautilus_catch_chance = 1.0; /// Chance of having to interrupt casting by moving to void tear from Za'qul's Portal Key - double zaquls_portal_key_move_chance = 0.0; + double zaquls_portal_key_move_chance = 0.0; /// Unleash stacked potency from Anu-Azshara, Staff of the Eternal after X seconds - timespan_t anuazshara_unleash_time = 0_ms; + timespan_t anuazshara_unleash_time = 0_ms; /// Storm of the Eternal haste and crit stat split ratio. - double storm_of_the_eternal_ratio = 0.05; + double storm_of_the_eternal_ratio = 0.05; /// How long before combat to start channeling Azshara's Font of Power - timespan_t font_of_power_precombat_channel = 0_ms; + timespan_t font_of_power_precombat_channel = 0_ms; /// Average duration of buff in percentage double voidtwisted_titanshard_percent_duration = 0.5; /// Period between checking if surging vitality can proc @@ -437,33 +330,33 @@ struct sim_t : private sc_thread_t /// Percentage of Whispered Truths reductions to be applied to offensive spells. double whispered_truths_offensive_chance = 0.75; /// Initial stacks for Seductive Power buff - int initial_seductive_power_stacks = 0; + int initial_seductive_power_stacks = 0; /// Number of allies affected by Jes' Howler buff - unsigned jes_howler_allies = 4; + unsigned jes_howler_allies = 4; /// Initial stacks for Archive of the Titans - int initial_archive_of_the_titans_stacks = 0; + int initial_archive_of_the_titans_stacks = 0; /// Hps done while using the Azerite Trait Arcane Heart - unsigned arcane_heart_hps = 0; + unsigned arcane_heart_hps = 0; /// Prepull spell cast count to assume. - int subroutine_recalibration_precombat_stacks = 0; + int subroutine_recalibration_precombat_stacks = 0; /// Additional spell cast count to assume each buff cycle. - int subroutine_recalibration_dummy_casts = 0; + int subroutine_recalibration_dummy_casts = 0; /// Number of Reorigination array stats on the actors in the sim - int reorigination_array_stacks = 0; + int reorigination_array_stacks = 0; /// Allow Reorigination Array to ignore scale factor stat changes (default false) - bool reorigination_array_ignore_scale_factors = false; + bool reorigination_array_ignore_scale_factors = false; /// Randomize Variable Intensity Gigavolt Oscillating Reactor start-of-combat oscillation - bool randomize_oscillation = true; + bool randomize_oscillation = true; /// Automatically use Oscillating Overload on max stack, true = yes if no use_item, 0 = no - bool auto_oscillating_overload = true; + bool auto_oscillating_overload = true; /// Is the actor in Zuldazar? Relevant for one of the set bonuses. - bool zuldazar = false; + bool zuldazar = false; /// Whether the player is in Ny'alotha or not. bool nyalotha = true; /// Whether the player is in Nazjatar/Eternal Palace for various effects - bool nazjatar = true; + bool nazjatar = true; /// Whether the Shiver Venom Crossbow/Lance should assume the target has the Shiver Venom debuff - bool shiver_venom = false; + bool shiver_venom = false; } bfa_opts; struct shadowlands_opt_t @@ -527,7 +420,7 @@ struct sim_t : private sc_thread_t /// Sets the default delay that the player waits before facing their Doubt. /// This is disabled if the APL creates the "newfound_resolve" action. timespan_t newfound_resolve_default_delay = 4_s; - double newfound_resolve_delay_relstddev = 0.2; + double newfound_resolve_delay_relstddev = 0.2; /// Seconds between damage/healing triggers for the Pustule Eruption soulbind, has a minimum 1s ICD timespan_t pustule_eruption_interval = 1_s; /// Chance that the player will pickup Shredded Soul orb left by Ebonsoul Vise @@ -557,8 +450,8 @@ struct sim_t : private sc_thread_t bool disable_iqd_execute = false; // Better Together Override // Defaults active - bool better_together_ally = true; - bool enable_rune_words = false; + bool better_together_ally = true; + bool enable_rune_words = false; bool enable_domination_gems = false; // fleshcraft cancel delay from the_first_sigil timespan_t the_first_sigil_fleshcraft_cancel_time = 50_ms; @@ -668,7 +561,7 @@ struct sim_t : private sc_thread_t chrono::wall_clock::duration elapsed_time; std::vector work_per_thread; size_t work_done; - double iteration_dmg, priority_iteration_dmg, iteration_heal, iteration_absorb; + double iteration_dmg, priority_iteration_dmg, iteration_heal, iteration_absorb; simple_sample_data_t total_dmg, raid_hps, total_heal, total_absorb, raid_aps; extended_sample_data_t raid_dps, simulation_length; chrono::wall_clock::duration merge_time, init_time, analyze_time; @@ -676,11 +569,11 @@ struct sim_t : private sc_thread_t // replayability std::vector iteration_data, low_iteration_data, high_iteration_data; // Report percent (how many% of lowest/highest iterations reported, default 2.5%) - double report_iteration_data; + double report_iteration_data; // Minimum number of low/high iterations reported (default 5 of each) - int min_report_iteration_data; - int report_progress; - int bloodlust_percent; + int min_report_iteration_data; + int report_progress; + int bloodlust_percent; timespan_t bloodlust_time; std::string reference_player_str; std::vector players_by_dps; @@ -693,7 +586,7 @@ struct sim_t : private sc_thread_t std::vector players_by_variance; std::vector targets_by_name; std::vector id_dictionary; - std::map > divisor_timeline_cache; + std::map> divisor_timeline_cache; std::vector json_reports; std::string output_file_str, html_file_str, json_file_str; std::string reforge_plot_output_file_str; @@ -733,14 +626,17 @@ struct sim_t : private sc_thread_t bool merge_enemy_priority_dmg; // sim control - std::vector> sim_controllers; - std::map sim_controller_data; + std::vector> profileset_controller; + // deque used as profileset_controller_data_wrapper_t is nocopy, thus std::vector + // is incompatible + std::deque profileset_controller_data; + opts::map_list_t profileset_controller_options; public: // Multi-Threading mutex_t merge_mutex; int threads; - std::vector children; // Manual delete! + std::vector children; // Manual delete! int thread_index; computer_process::priority_e process_priority; std::shared_ptr work_queue; @@ -757,7 +653,7 @@ struct sim_t : private sc_thread_t std::string spell_query_xml_output_file_str; unsigned spell_query_wrap; - std::unique_ptr pause_mutex; // External pause mutex, instantiated an external entity (in our case the GUI). + std::unique_ptr pause_mutex; // External pause mutex, instantiated an external entity (in our case the GUI). bool paused; // Highcharts stuff @@ -768,7 +664,7 @@ struct sim_t : private sc_thread_t // A map of highcharts data, added as a json object into the HTML report. JQuery installs handlers // to correct elements (toggled elements in the HTML report) based on the data. - std::map > chart_data; + std::map> chart_data; bool chart_show_relative_difference; // Use the max metric actor as the relative difference base instead of the min @@ -779,7 +675,7 @@ struct sim_t : private sc_thread_t // List of callbacks to call when an actor_target_data_t object is created. Currently used to // initialize the generic targetdata debuffs/dots we have. - std::vector > target_data_initializer; + std::vector> target_data_initializer; bool display_hotfixes, disable_hotfixes; bool display_bonus_ids; @@ -801,47 +697,47 @@ struct sim_t : private sc_thread_t ~sim_t() override; void run() override; - int main( const std::vector& args ); - double iteration_time_adjust(); - double expected_max_time() const; - bool is_canceled() const; - void cancel_iteration(); - void cancel(); - void interrupt(); - void add_relative( sim_t* cousin ); - void remove_relative( sim_t* cousin ); + int main( const std::vector& args ); + double iteration_time_adjust(); + double expected_max_time() const; + bool is_canceled() const; + void cancel_iteration(); + void cancel(); + void interrupt(); + void add_relative( sim_t* cousin ); + void remove_relative( sim_t* cousin ); sim_progress_t progress( std::string* detailed = nullptr, int index = -1 ); - double progress( std::string& phase, std::string* detailed = nullptr, int index = -1 ); - void detailed_progress( std::string*, int current_iterations, int total_iterations ); - void datacollection_begin(); - void datacollection_end(); - void reset(); - void check_actors(); - void init_fight_style(); - void init_parties(); - void init_actors(); - void init_actor( player_t* ); - void init_actor_pets(); - void init(); - void analyze(); - void merge( sim_t& other_sim ); - void merge(); - bool iterate(); - void partition(); - bool execute(); - void analyze_error(); - void analyze_iteration_data(); - void print_options(); - void add_option( std::unique_ptr opt ); - void create_options(); - bool parse_option( const std::string& name, const std::string& value ); - void setup( sim_control_t* ); - bool time_to_think( timespan_t proc_time ); + double progress( std::string& phase, std::string* detailed = nullptr, int index = -1 ); + void detailed_progress( std::string*, int current_iterations, int total_iterations ); + void datacollection_begin(); + void datacollection_end(); + void reset(); + void check_actors(); + void init_fight_style(); + void init_parties(); + void init_actors(); + void init_actor( player_t* ); + void init_actor_pets(); + void init(); + void analyze(); + void merge( sim_t& other_sim ); + void merge(); + bool iterate(); + void partition(); + bool execute(); + void analyze_error(); + void analyze_iteration_data(); + void print_options(); + void add_option( std::unique_ptr opt ); + void create_options(); + bool parse_option( const std::string& name, const std::string& value ); + void setup( sim_control_t* ); + bool time_to_think( timespan_t proc_time ); player_t* find_player( util::string_view name ) const; player_t* find_player( int index ) const; cooldown_t* get_cooldown( util::string_view name ); - void use_optimal_buffs_and_debuffs( int value ); - std::unique_ptr create_expression( util::string_view name ); + void use_optimal_buffs_and_debuffs( int value ); + std::unique_ptr create_expression( util::string_view name ); /** * Create error with printf formatting. @@ -852,7 +748,7 @@ struct sim_t : private sc_thread_t if ( thread_index != 0 ) return; - set_error( level, fmt::sprintf( format, std::forward(args)... ) ); + set_error( level, fmt::sprintf( format, std::forward( args )... ) ); } template @@ -861,7 +757,7 @@ struct sim_t : private sc_thread_t if ( thread_index != 0 ) return; - set_error( error_level_e::TRIVIAL, fmt::sprintf( format, std::forward(args)... ) ); + set_error( error_level_e::TRIVIAL, fmt::sprintf( format, std::forward( args )... ) ); } /** @@ -896,14 +792,14 @@ struct sim_t : private sc_thread_t void activate_actors(); void heartbeat_event_callback(); - std::vector> heartbeat_event_callback_function; - void register_heartbeat_event_callback( std::function fn ); + std::vector> heartbeat_event_callback_function; + void register_heartbeat_event_callback( std::function fn ); timespan_t current_time() const { return event_mgr.current_time; } static double distribution_mean_error( const sim_t& s, const extended_sample_data_t& sd ) { return s.confidence_estimator * sd.mean_std_dev; } - void register_target_data_initializer(std::function cb) + void register_target_data_initializer( std::function cb ) { target_data_initializer.push_back( cb ); } const rng::rng_t& rng() const { return _rng; } @@ -911,7 +807,6 @@ struct sim_t : private sc_thread_t { return _rng; } double averaged_range( double min, double max ); - // Thread id of this sim_t object #ifndef SC_NO_THREADING std::thread::id thread_id() const @@ -932,9 +827,9 @@ struct sim_t : private sc_thread_t * Print using fmt libraries python-like formatting syntax. */ template - void print_debug( fmt::format_string format, Args&& ... args ) + void print_debug( fmt::format_string format, Args&&... args ) { - if ( ! debug ) + if ( !debug ) return; out_debug.vprint( format, fmt::make_format_args( args... ) ); @@ -947,9 +842,9 @@ struct sim_t : private sc_thread_t * Print using fmt libraries python-like formatting syntax. */ template - void print_log( fmt::format_string format, Args&& ... args ) + void print_log( fmt::format_string format, Args&&... args ) { - if ( ! log ) + if ( !log ) return; out_log.vprint( format, fmt::make_format_args( args... ) ); @@ -965,3 +860,20 @@ struct sim_t : private sc_thread_t void disable_debug_seed(); bool requires_cleanup() const; }; + +template +data_wrapper_t profileset_controller_t::get_data() +{ + auto& pcd = parent->profileset_controller_data; + assert( pcd.size() > id ); + auto& data = pcd[ id ]; + return { *data.data.get(), data.mutex }; +} + +template +void profileset_controller_t::set_data( T&& data ) +{ + auto& pcd = parent->profileset_controller_data; + assert( pcd.size() > id ); + pcd[ id ].data = std::make_unique( data ); +} diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 33631bf19bb..16fc1405bdc 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,35 +1,146 @@ #include "sim_controller.hpp" -#include "sc_enums.hpp" #include "player/set_bonus.hpp" #include "profileset.hpp" +#include "sc_enums.hpp" #include "sim.hpp" +/* + * TODO: + * - initialize contents of factory map + * - pass in option values to pc_t, sim_controller.cpp:21 + * + * PROCEDURE: + * 1) register controllers via `profileset_controller_t::register_controller(...)`, + * which emplaces controller(s) into the factory map for later construction. + * 2) parent sim parses options, constructing `pcd_w_t` for each type via factory map. + * 3) child sim iterates over parent->profileset_controller_data, constructing `pc_t` + * for each `pcd_w_t` + */ + +/* + * sim_controller_t: + * - scope: profileset + * - evaluates whether or not profileset should exit + * - reports why an exit occurs + * - provides options, as in many cases a custom `sim_controller_data_t` type is + * not required for storage reasons + * + * sim_controller_data_t + * - scope: parent sim + * - contains custom data, such as current "winner" for binary controllers + * including profileset culling + * - contains option values + * + * sim_controller_data_wrapper_t + * - scope: parent sim + * - contains mutex for thread safety when operating on sim_controller_data_t + * - contains sim_controller_data_t pointer + * - contains exit_reasons () + * - contains registered option definitions + * + * data_wrapper_t + * - scope: sim_controller_data_t getter + * - contains scoped recursive lock on sim_controller_data_t + * - contains reference to sim_controller_data_t data + * + * notes: + * + * profilesets_t objects are configured and init via + * - sim_t::execute() > sim_t::iterate() > sim_t::init() > profilesets->initialize( sim ) + * - profilesets_t::parse( ... ) > m_profilesets.push_back( ... ) + * profilesets_t is executed via + * - profilesets->iterate( sim ) > profilesets_t::generate_work( ... ) + * profileset sims/workers are constructed and executed via + * - SEQUENTIAL: new sim_t( ... ) + * - PARALLEL: push_back( new worker_t( profileset_control ) ) + * profileset-creation options are stripped from profileset_control prior to construction + * - profilesets_t::create_sim_options, profilesets_t::initialize, filter_control + * + * sim where ( !parent ) parses values stored in sim_t::sim_controller_options, + * static sim_controller_data_t::register([ key, opts ]) -> scd_w_t { key, id, scd_t { parsed_options_values... } } + * sim where ( parent ) iterates over sim_t::sim_controller_data + * sim_controller_data_t::register_controller() -> sc_t { id, parsed_options_values... } + * + * how can i copy parsed_option_values from scd_t -> sc_t without a type that + * contains the option_values and without reparsing the options? + * + * use atomic incrementing id for scds + * + * no storage as a map, as scd id and vector position should be identical + * + * move implementation of reporting strictly to scd_t + * + * implement reporting under a static member to handle all reporting with minimal + * implementation in html/json files + */ + +// profilesets object is configured/init via sim_t +// - execute() -:> iterate() -:> init() -:> profilesets->initialize(self) +// - parse(...) -:> m.profilesets.push_back(...) +// profilesets are executed via profilesets->iterate(self) -> generate_work(...) +// profileset sims are created and executed via +// - profilesets->iterate(self) -:> generate_work(...) -:> +// - SEQUENTIAL: new sim_t +// - PARALLEL: push_back new worker_t(pset_data) +// strip profileset-specific options from opts used to initialize sims +// profilesets_t::create_sim_options, profilesets_t::initialize, filter_control + +/* + * implementation: + * - parent sim parses sim controller instantiation option and child options + * - sc_main.cpp:273 arg parse -> sc_main.cpp:287 opt parse and actor creation + * - parent sim constructs sim controller data for auto incrementing index id + * - as work is created, child option data is copied and sim controller is constructed by iterating over scd vec, passing along id as well + * + * NOTES: + * - implement sim opts as function options, which induces opt order dependency, no preventing option parse without filtering profileset sim control + * - implement sim opts as map list and execute processing in setup or init if sim is parent + * OTHER CHANGES: + * - move exit_reasons and options (?) from data wrapper to data + * - provide getters and setters for exit_reasons and options as appropriate + * - no key-value pairs, everything should be a vec + * - initialize controllers and scd with atomic id generated via scd + * - rename to profileset_controller, as they control profilesets not sims per se + */ + +std::unordered_map profileset_controller_t::factory; +std::atomic_uint profileset_controller_data_wrapper_t::id_generator; -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t() : mutex(), data(), exit_reasons() +profileset_controller_data_t::profileset_controller_data_t( std::string_view options ) { } -sim_controller_data_wrapper_t::sim_controller_data_wrapper_t( std::unique_ptr&& data ) - : mutex(), data( std::move( data ) ), exit_reasons() +profileset_controller_data_wrapper_t::profileset_controller_data_wrapper_t( std::string key, std::string_view options ) + : mutex(), id( id_generator++ ), key( key ) { + if ( const auto& value = profileset_controller_t::factory.find( key ); + value != profileset_controller_t::factory.end() ) + data = value->second.second( options ); + assert( data ); } -sim_controller_data_t::sim_controller_data_t() +void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) { + if ( const auto& value = profileset_controller_t::factory.find( key ); + value != profileset_controller_t::factory.end() ) + { + sim->profileset_controller.emplace_back( value->second.first( sim, id ) ); + return; + } + assert( false && "No factory fn for key found." ); } -sim_controller_data_t::sim_controller_data_t( sim_controller_data_t& ) +bool profileset_controller_t::register_controller( std::string key, profileset_controller_t::factory_fn_pair_t&& value ) { + return factory.try_emplace( key, std::move( value ) ).second; } -sim_controller_t::sim_controller_t( sim_t* sim ) - : parent( sim->parent ), sim( sim ) +bool profileset_controller_t::controller_exists( std::string key ) { - assert( sim ); - assert( sim->parent ); + return factory.find( key ) != factory.end(); } -const std::string sim_controller_t::call_point_string( call_point_e call_point ) +const std::string profileset_controller_t::call_point_string( call_point_e call_point ) { switch ( call_point ) { @@ -43,95 +154,98 @@ const std::string sim_controller_t::call_point_string( call_point_e call_point ) } } -void sim_controller_t::evaluate( sim_t* sim, call_point_e call_point ) +void profileset_controller_t::evaluate( sim_t* sim, call_point_e call_point ) { if ( !sim->profileset_enabled || !sim->parent ) return; - typedef std::unique_ptr sc_ptr_t; - std::function cb; + std::function& )> cb; switch ( call_point ) { case POST_INIT: - cb = []( sc_ptr_t& sc ) { return !sc->evaluate_post_init(); }; + cb = []( std::unique_ptr& sc ) { return !sc->evaluate_post_init(); }; break; case POST_ITER: - cb = []( sc_ptr_t& sc ) { return !sc->evaluate_post_iter(); }; + cb = []( std::unique_ptr& sc ) { return !sc->evaluate_post_iter(); }; break; default: assert( false ); break; } - auto sc = range::find_if( sim->sim_controllers, cb ); - if ( sc == sim->sim_controllers.end() ) + auto pc = range::find_if( sim->profileset_controller, cb ); + if ( pc == sim->profileset_controller.end() ) return; - auto* controller = sc->get(); + auto controller = pc->get(); assert( controller->sim == sim ); assert( controller->parent == sim->parent ); - auto& scd = sim->parent->sim_controller_data.at( controller->name() ); - std::scoped_lock L( scd.mutex ); - - scd.exit_reasons.emplace_back( sim->parent->profilesets->current_profileset_name(), call_point, - controller->reason() ); + controller->set_exit_reason( + { sim->parent->profilesets->current_profileset_name(), call_point, controller->reason() } ); sim->canceled = true; sim->error( error_level_e::TRIVIAL, "{}", controller->message( call_point ) ); sim->interrupt(); } -const std::string sim_controller_t::message( call_point_e call_point ) +void profileset_controller_t::html_report( const sim_t& sim, std::ostream& out ) { - std::string msg = - fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), - call_point_string( call_point ) ); - if ( call_point == POST_ITER ) - msg += std::to_string( sim->current_iteration ); + if ( sim.profileset_controller_data.empty() ) + return; - if ( const std::string r = reason(); r != "" ) - msg += fmt::format( " because {}.", r ); - else - msg += "."; + out << "

    Profileset Sim Control

    \n"; + out << "
    \n"; - return msg; -} + out << "
    Sim Controllers
      \n"; + for ( const auto& controller_data_wrapper : sim.profileset_controller_data ) + if ( const auto& controller_data = controller_data_wrapper.data; controller_data ) + controller_data->report_html_options( out ); + out << "
    \n"; -void sim_controller_t::add_option( std::unique_ptr option ) -{ -} + // report source, location, and reason of interrupt for + // all registered profileset profileset controllers + bool has_culled_profileset = range::any_of( sim.profileset_controller_data, + []( const auto& entry ) { return entry.data->exit_reasons.size(); } ); -void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const -{ - for ( const exit_reason_t& exit_reason : exit_reasons ) + if ( has_culled_profileset ) { - output[ "interrupted_by" ] = exit_reason.profileset_name; - output[ "exit_point" ] = sim_controller_t::call_point_string( exit_reason.exit_point ); - output[ "exit_reason" ] = exit_reason.exit_reason; + out << "
    Interrupted Profilesets
      \n"; + for ( const auto& controller_data_wrapper : sim.profileset_controller_data ) + if ( const auto& controller_data = controller_data_wrapper.data; + controller_data && controller_data->exit_reasons.size() ) + controller_data->report_html_profileset( out ); + out << "
    \n"; } + out << "
    "; } -void sim_controller_data_wrapper_t::report_json_options( js::JsonOutput& ) const +profileset_controller_t::profileset_controller_t( sim_t* sim, unsigned int id ) + : parent( sim->parent ), sim( sim ), id( id ) { - // TODO: implement opt parsing and automatic generation of report json from opts + assert( sim && sim->parent ); } -void sim_controller_data_wrapper_t::report_html_profileset( std::ostream& output ) const +const std::string profileset_controller_t::message( call_point_e call_point ) { - for ( const exit_reason_t& exit_reason : exit_reasons ) - output << "
  • " - << util::encode_html( exit_reason.profileset_name ) << " " - << util::encode_html( sim_controller_t::call_point_string( exit_reason.exit_point ) ) << " " - << util::encode_html( exit_reason.exit_reason ) - << "
  • "; -} + std::string msg = + fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), + call_point_string( call_point ) ); + if ( call_point == POST_ITER ) + msg += std::to_string( sim->current_iteration ); + + if ( const std::string r = reason(); r != "" ) + msg += fmt::format( " because {}.", r ); + else + msg += "."; -void sim_controller_data_wrapper_t::report_html_options( std::ostream& ) const -{} + return msg; +} -min_player_stat_t::min_player_stat_t( sim_t* sim, player_t* target_player, stat_e rating, double amount ) - : sim_controller_t( sim ), target_player( target_player ), rating( rating ), min_rating( amount ) +void profileset_controller_t::set_exit_reason( exit_reason_t&& exit_reason ) { + auto& pcd = parent->profileset_controller_data; + assert( pcd.size() > id ); + pcd[ id ].data->exit_reasons.emplace_back( std::move( exit_reason ) ); } bool min_player_stat_t::evaluate_post_init() @@ -141,17 +255,14 @@ bool min_player_stat_t::evaluate_post_init() const std::string min_player_stat_t::reason() const { - return fmt::format( "player {} does not exceed {} rating for {}", target_player->name(), - min_rating, util::stat_type_string( rating ) ); -} - -tier_set_count_t::tier_set_count_t( sim_t* sim, player_t* target_player, set_bonus_type_e tier, set_bonus_e count ) - : sim_controller_t( sim ), target_player( target_player ), tier( tier ), count( count ) -{ + return fmt::format( "player {} does not exceed {} rating for {}", target_player->name(), min_rating, + util::stat_type_string( rating ) ); } bool tier_set_count_t::evaluate_post_init() { + tier = TWW2; + count = B2; return target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); } @@ -162,3 +273,50 @@ const std::string tier_set_count_t::reason() const return fmt::format( "player {} does not have tier {} {} active", target_player->name(), static_cast( tier ), static_cast( count ) ); } + +// void tier_set_count_t::create_options() +// { +// add_option( opt_int( "test", test ) ); +// } + +// void sim_controller_t::add_option( std::unique_ptr&& option ) +// { +// auto& scd = sim->parent->sim_controller_data.at( name() ); +// std::scoped_lock L( scd.mutex ); + +// scd.options.emplace_back( std::move( option ) ); +// } + +// void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const +// { +// for ( const exit_reason_t& exit_reason : exit_reasons ) +// { +// output[ "interrupted_by" ] = exit_reason.profileset_name; +// output[ "exit_point" ] = sim_controller_t::call_point_string( exit_reason.exit_point ); +// output[ "exit_reason" ] = exit_reason.exit_reason; +// } +// } + +// void sim_controller_data_wrapper_t::report_json_options( js::JsonOutput& ) const +// { +// // TODO: implement opt parsing and automatic generation of report json from opts +// } + +// void sim_controller_data_wrapper_t::report_html_profileset( std::ostream& output ) const +// { +// for ( const exit_reason_t& exit_reason : exit_reasons ) +// output << "
  • " +// << util::encode_html( exit_reason.profileset_name ) << " " +// << util::encode_html( sim_controller_t::call_point_string( exit_reason.exit_point ) ) << " " +// << util::encode_html( exit_reason.exit_reason ) +// << "
  • \n"; +// } + +// void sim_controller_data_wrapper_t::report_html_options( std::ostream& output ) const +// { +// output << "
  • " +// << util::encode_html( key ) << " "; +// for ( const auto& opt : options ) +// output << fmt::format( "{} ", opt ); +// output << "
  • \n"; +// } diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 947a2b69b41..29aedbaff80 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -3,37 +3,111 @@ #include "player/player.hpp" #include "player/rating.hpp" #include "sc_enums.hpp" -#include "sim.hpp" +#include "util/generic.hpp" -template -bool sim_controller_t::register_sim_controller( sim_t* sim, Args&&... args ) -{ - if ( sim && sim->profileset_enabled && sim->parent ) - { - sim->sim_controllers.emplace_back( std::make_unique( sim, std::forward( args )... ) ); - return sim->parent->sim_controller_data - .emplace( sim->sim_controllers.back()->name(), std::make_unique() ) - .second; - } - return false; -} +#include +#include +#include + +struct sim_t; template -data_wrapper_t sim_controller_t::get_data() +struct data_wrapper_t { - auto& data = parent->sim_controller_data.at( name() ); - return { *std::static_pointer_cast( data.data ), data.mutex }; -} +private: + std::scoped_lock lock; +public: + const T& data; -template -void sim_controller_t::set_data( T&& data ) + data_wrapper_t( const T& data, std::recursive_mutex& m ) : lock( m ), data( data ) {} +}; + +enum call_point_e +{ + CALL_POINT_NONE, + POST_INIT, + POST_ITER +}; + +struct exit_reason_t { - auto& scd = parent->sim_controller_data; - assert( scd.find( name() ) != scd.end() ); - scd[ name() ].data = std::make_unique( data ); -} + const std::string profileset_name; + const call_point_e exit_point; + const std::string exit_reason; +}; -struct min_player_stat_t : sim_controller_t +struct profileset_controller_data_t : private noncopyable +{ + std::vector exit_reasons; + std::vector> options; + + profileset_controller_data_t( std::string_view ); + virtual ~profileset_controller_data_t() = default; + + virtual void report_html_options( std::ostream& ) {} + virtual void report_html_profileset( std::ostream& ) {} +}; + +struct profileset_controller_data_wrapper_t : private noncopyable +{ + static std::atomic_uint id_generator; + std::recursive_mutex mutex; + unsigned int id; + std::string key; + std::unique_ptr data; + + profileset_controller_data_wrapper_t( std::string, std::string_view ); + + void construct_controller( sim_t* ); +}; + +struct profileset_controller_t : private noncopyable +{ + using controller_factory_t = std::function(sim_t*, unsigned int)>; + using data_factory_t = std::function(std::string_view)>; + using factory_fn_pair_t = std::pair; +protected: + friend profileset_controller_data_wrapper_t; + static std::unordered_map factory; +public: + static bool register_controller( std::string, factory_fn_pair_t&& ); + static bool controller_exists( std::string ); + + using data_t = profileset_controller_data_t; + static const std::string call_point_string( call_point_e call_point ); + static void evaluate( sim_t* sim, call_point_e call_point ); + + static void html_report( const sim_t&, std::ostream& ); + + sim_t* parent; + sim_t* sim; + const unsigned int id; + + profileset_controller_t( sim_t*, unsigned int ); + virtual ~profileset_controller_t() = default; + + const std::string message( call_point_e ); + // void add_option( std::unique_ptr&& ); + + virtual const std::string name() const = 0; + virtual const std::string reason() const = 0; + // virtual void create_options() {} + virtual bool evaluate_post_init() { + return true; + } + virtual bool evaluate_post_iter() { + return true; + } + +protected: + template + data_wrapper_t get_data(); + template + void set_data( T&& data ); + void set_exit_reason( exit_reason_t&& ); +}; + +struct min_player_stat_t : profileset_controller_t { /* * This sim controller doesn't work, as at all controller evaluation points @@ -41,13 +115,12 @@ struct min_player_stat_t : sim_controller_t * be set once on actor init and preserved between iterations, this would be * fixed. */ - using data_t = sim_controller_data_t; + using data_t = profileset_controller_data_t; player_t* target_player; stat_e rating; double min_rating; - min_player_stat_t( sim_t*, player_t*, stat_e, double ); const std::string name() const override { return "min_player_stat"; @@ -56,19 +129,23 @@ struct min_player_stat_t : sim_controller_t const std::string reason() const override; }; -struct tier_set_count_t : sim_controller_t +struct tier_set_count_t : profileset_controller_t { - using data_t = sim_controller_data_t; + using data_t = profileset_controller_data_t; player_t* target_player; set_bonus_type_e tier; set_bonus_e count; + int test; - tier_set_count_t( sim_t*, player_t*, set_bonus_type_e, set_bonus_e ); + tier_set_count_t( sim_t* sim, unsigned int id ) : profileset_controller_t( sim, id ) + { + } const std::string name() const override { return "tier_set_count"; } bool evaluate_post_init() override; const std::string reason() const override; + // void create_options() override; }; From aede102b2f7b17992560ebbd4302eeb0ec23a19b Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 5 Dec 2025 02:58:26 -0700 Subject: [PATCH 16/21] options are working and profileset controllers can be created with no c++ changes --- engine/class_modules/monk/sc_monk.cpp | 2 +- engine/sim/sim_controller.cpp | 79 ++++++++++++++++++++------- engine/sim/sim_controller.hpp | 21 ++++--- 3 files changed, 75 insertions(+), 27 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index ba2f883e982..f7a68b1b91a 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6893,7 +6893,7 @@ bool monk_t::validate_fight_style( fight_style_e style ) const void monk_t::init() { auto t_pc = []( sim_t *sim, unsigned int id ) { return std::make_unique( sim, id ); }; - auto t_pcd = []( std::string_view options ) { return std::make_unique( options ); }; + auto t_pcd = []() { return std::make_unique(); }; profileset_controller_t::register_controller( "tier", { t_pc, t_pcd } ); base_t::init(); diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 16fc1405bdc..3b5d9a322bc 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,5 +1,7 @@ #include "sim_controller.hpp" +#include "dbc/item_set_bonus.hpp" +#include "dbc/dbc.hpp" #include "player/set_bonus.hpp" #include "profileset.hpp" #include "sc_enums.hpp" @@ -106,16 +108,16 @@ std::unordered_map profileset_controller_t::factory; std::atomic_uint profileset_controller_data_wrapper_t::id_generator; -profileset_controller_data_t::profileset_controller_data_t( std::string_view options ) +profileset_controller_data_t::profileset_controller_data_t() { } profileset_controller_data_wrapper_t::profileset_controller_data_wrapper_t( std::string key, std::string_view options ) - : mutex(), id( id_generator++ ), key( key ) + : mutex(), id( id_generator++ ), key( key ), options( options ) { if ( const auto& value = profileset_controller_t::factory.find( key ); value != profileset_controller_t::factory.end() ) - data = value->second.second( options ); + data = value->second.second(); assert( data ); } @@ -124,7 +126,20 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) if ( const auto& value = profileset_controller_t::factory.find( key ); value != profileset_controller_t::factory.end() ) { - sim->profileset_controller.emplace_back( value->second.first( sim, id ) ); + auto controller = value->second.first( sim, id ); + controller->create_options(); + opts::parse( sim, "profileset_controller", controller->options, options, + [ this, &sim ]( opts::parse_status status, util::string_view name, util::string_view value ) { + // Fail parsing if strict parsing is used and the option is not found + if ( sim->strict_parsing && status == opts::parse_status::NOT_FOUND ) + return opts::parse_status::FAILURE; + // .. otherwise, just warn that there's an unknown option + if ( status == opts::parse_status::NOT_FOUND ) + sim->error( "Warning: profileset controller '{}' provided unknown option '{}' with value '{}', ignoring.", + key, name, value ); + return status; + } ); + sim->profileset_controller.emplace_back( std::move( controller ) ); return; } assert( false && "No factory fn for key found." ); @@ -219,6 +234,11 @@ void profileset_controller_t::html_report( const sim_t& sim, std::ostream& out ) out << ""; } +void profileset_controller_t::add_option( std::unique_ptr&& option ) +{ + options.emplace_back( std::move( option ) ); +} + profileset_controller_t::profileset_controller_t( sim_t* sim, unsigned int id ) : parent( sim->parent ), sim( sim ), id( id ) { @@ -261,9 +281,9 @@ const std::string min_player_stat_t::reason() const bool tier_set_count_t::evaluate_post_init() { - tier = TWW2; - count = B2; - return target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); + if ( target_player ) + return target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); + return true; } const std::string tier_set_count_t::reason() const @@ -274,18 +294,39 @@ const std::string tier_set_count_t::reason() const static_cast( count ) ); } -// void tier_set_count_t::create_options() -// { -// add_option( opt_int( "test", test ) ); -// } - -// void sim_controller_t::add_option( std::unique_ptr&& option ) -// { -// auto& scd = sim->parent->sim_controller_data.at( name() ); -// std::scoped_lock L( scd.mutex ); - -// scd.options.emplace_back( std::move( option ) ); -// } +void tier_set_count_t::create_options() +{ + add_option( opt_func( "tier", [ this ]( sim_t*, util::string_view, util::string_view value ){ + auto set_bonuses = item_set_bonus_t::data( target_player ? target_player->dbc->ptr : false ); + for ( const auto& set_bonus : set_bonuses ) + { + if ( util::str_compare_ci( set_bonus.tier, value ) ) + { + this->tier = static_cast( set_bonus.enum_id ); + return true; + } + } + return false; + } ) ); + add_option( opt_func( "pc", [ this ]( sim_t*, util::string_view, util::string_view value ) { + auto bonus_value = util::to_unsigned( value ); + if ( bonus_value > B_MAX ) + return false; + this->count = static_cast( bonus_value - 1 ); + return true; + } ) ); + add_option( opt_func( "player", [ this ]( sim_t* sim, util::string_view, util::string_view value ){ + for ( auto& player : sim->player_list ) + { + if ( util::str_compare_ci( player->name(), value ) ) + { + this->target_player = player; + return true; + } + } + return false; + } ) ); +} // void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const // { diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 29aedbaff80..3c3bf1378db 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -9,6 +9,13 @@ #include #include +/* + * TODO: + * - intialize contents of factory map + * - implement reporting + * - implement profileset culling specialization + */ + struct sim_t; template @@ -39,9 +46,8 @@ struct exit_reason_t struct profileset_controller_data_t : private noncopyable { std::vector exit_reasons; - std::vector> options; - profileset_controller_data_t( std::string_view ); + profileset_controller_data_t(); virtual ~profileset_controller_data_t() = default; virtual void report_html_options( std::ostream& ) {} @@ -54,6 +60,7 @@ struct profileset_controller_data_wrapper_t : private noncopyable std::recursive_mutex mutex; unsigned int id; std::string key; + std::string_view options; std::unique_ptr data; profileset_controller_data_wrapper_t( std::string, std::string_view ); @@ -64,7 +71,7 @@ struct profileset_controller_data_wrapper_t : private noncopyable struct profileset_controller_t : private noncopyable { using controller_factory_t = std::function(sim_t*, unsigned int)>; - using data_factory_t = std::function(std::string_view)>; + using data_factory_t = std::function()>; using factory_fn_pair_t = std::pair; protected: friend profileset_controller_data_wrapper_t; @@ -82,16 +89,17 @@ struct profileset_controller_t : private noncopyable sim_t* parent; sim_t* sim; const unsigned int id; + std::vector> options; profileset_controller_t( sim_t*, unsigned int ); virtual ~profileset_controller_t() = default; const std::string message( call_point_e ); - // void add_option( std::unique_ptr&& ); + void add_option( std::unique_ptr&& ); virtual const std::string name() const = 0; virtual const std::string reason() const = 0; - // virtual void create_options() {} + virtual void create_options() {} virtual bool evaluate_post_init() { return true; } @@ -136,7 +144,6 @@ struct tier_set_count_t : profileset_controller_t player_t* target_player; set_bonus_type_e tier; set_bonus_e count; - int test; tier_set_count_t( sim_t* sim, unsigned int id ) : profileset_controller_t( sim, id ) { @@ -147,5 +154,5 @@ struct tier_set_count_t : profileset_controller_t } bool evaluate_post_init() override; const std::string reason() const override; - // void create_options() override; + void create_options() override; }; From 8981a16711a914bc40bdf6fb390d060e6fa00291 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 6 Dec 2025 03:32:11 -0700 Subject: [PATCH 17/21] refactor a few methods and implement html reporting --- engine/class_modules/monk/sc_monk.cpp | 26 --- engine/class_modules/monk/sc_monk.hpp | 1 - engine/report/report_html_sim.cpp | 2 +- engine/sim/sim_controller.cpp | 264 ++++++++++++++++---------- engine/sim/sim_controller.hpp | 57 ++++-- 5 files changed, 200 insertions(+), 150 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index f7a68b1b91a..d99190d9d4f 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6890,32 +6890,6 @@ bool monk_t::validate_fight_style( fight_style_e style ) const return true; } -void monk_t::init() -{ - auto t_pc = []( sim_t *sim, unsigned int id ) { return std::make_unique( sim, id ); }; - auto t_pcd = []() { return std::make_unique(); }; - profileset_controller_t::register_controller( "tier", { t_pc, t_pcd } ); - - base_t::init(); - - // if ( !sim->parent ) - // sim->profileset_controller_data.emplace_back( "tier", "" ); - // if ( sim->parent ) - // { - // for ( auto& pcd : sim->parent->profileset_controller_data ) - // pcd.construct_controller( sim ); - // for ( auto& pc : sim->profileset_controller ) - // { - // auto c_pc = static_cast( pc.get() ); - // c_pc->target_player = this; - // c_pc->tier = TWW2; - // c_pc->count = B2; - // } - // } - // sim_controller_t::register_sim_controller( sim, this, STAT_CRIT_RATING, 100.0 ); - // sim_controller_t::register_sim_controller( sim, this, TWW2, B2 ); -} - // monk_t::init_spells ====================================================== void monk_t::init_spells() { diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 03af810b65e..3deac666961 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -1477,7 +1477,6 @@ struct monk_t : public stagger_t bool validate_fight_style( fight_style_e style ) const override; // Init / Reset - void init() override; void create_pets() override; void init_spells() override; void init_background_actions() override; diff --git a/engine/report/report_html_sim.cpp b/engine/report/report_html_sim.cpp index cda221bb4f8..126de5a523d 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1160,7 +1160,7 @@ void print_profilesets( std::ostream& out, const profileset::profilesets_t& prof print_profilesets_chart( out, sim ); - profileset_controller_t::html_report( sim, out ); + profileset_controller::report_html( sim, out ); out << ""; out << ""; diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index 3b5d9a322bc..a3004db3736 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -1,7 +1,7 @@ #include "sim_controller.hpp" -#include "dbc/item_set_bonus.hpp" #include "dbc/dbc.hpp" +#include "dbc/item_set_bonus.hpp" #include "player/set_bonus.hpp" #include "profileset.hpp" #include "sc_enums.hpp" @@ -88,28 +88,68 @@ // profilesets_t::create_sim_options, profilesets_t::initialize, filter_control /* - * implementation: - * - parent sim parses sim controller instantiation option and child options - * - sc_main.cpp:273 arg parse -> sc_main.cpp:287 opt parse and actor creation - * - parent sim constructs sim controller data for auto incrementing index id - * - as work is created, child option data is copied and sim controller is constructed by iterating over scd vec, passing along id as well - * - * NOTES: - * - implement sim opts as function options, which induces opt order dependency, no preventing option parse without filtering profileset sim control - * - implement sim opts as map list and execute processing in setup or init if sim is parent - * OTHER CHANGES: - * - move exit_reasons and options (?) from data wrapper to data - * - provide getters and setters for exit_reasons and options as appropriate - * - no key-value pairs, everything should be a vec - * - initialize controllers and scd with atomic id generated via scd - * - rename to profileset_controller, as they control profilesets not sims per se - */ - -std::unordered_map profileset_controller_t::factory; + * implementation: + * - parent sim parses sim controller instantiation option and child options + * - sc_main.cpp:273 arg parse -> sc_main.cpp:287 opt parse and actor creation + * - parent sim constructs sim controller data for auto incrementing index id + * - as work is created, child option data is copied and sim controller is constructed by iterating over scd vec, + * passing along id as well + * + * NOTES: + * - implement sim opts as function options, which induces opt order dependency, no preventing option parse without + * filtering profileset sim control + * - implement sim opts as map list and execute processing in setup or init if sim is parent + * OTHER CHANGES: + * - move exit_reasons and options (?) from data wrapper to data + * - provide getters and setters for exit_reasons and options as appropriate + * - no key-value pairs, everything should be a vec + * - initialize controllers and scd with atomic id generated via scd + * - rename to profileset_controller, as they control profilesets not sims per se + */ + +/* + * Global profileset_controller_t factories. If you create a `profileset_controller_t` + * subtype specific to some context, have that context register it early in sim init + * via the static function `profileset_controller_t::register_controller`. + */ +std::unordered_map profileset_controller_t::factory = { + { "set_bonus_enabled", + { []( sim_t* sim, unsigned int id ) { return std::make_unique( sim, id ); }, + []( std::string_view key, std::string_view options ) { + return std::make_unique( key, options ); + } } } }; + std::atomic_uint profileset_controller_data_wrapper_t::id_generator; -profileset_controller_data_t::profileset_controller_data_t() +profileset_controller_data_t::profileset_controller_data_t( std::string_view key, std::string_view options ) + : key( key ), options( options ) +{ +} + +void profileset_controller_data_t::report_html_options( std::ostream& output ) const +{ + output << "" + << "" << util::encode_html( key ) << "" + << "" << exit_reasons.size() << "" + << "" << util::encode_html( options ) << "" + << "\n"; +} + +void profileset_controller_data_t::report_html_profileset( std::ostream& output ) const { + bool first = true; + output << fmt::format( "{}", exit_reasons.size(), + util::encode_html( key ) ); + for ( const auto& [ name, call_point, reason ] : exit_reasons ) + { + if ( !first ) + output << ""; + output << "" << util::encode_html( name ) << "" + << "" << util::encode_html( profileset_controller::call_point_string( call_point ) ) << "" + << "" << util::encode_html( reason ) << "" + << "\n"; + first = false; + } } profileset_controller_data_wrapper_t::profileset_controller_data_wrapper_t( std::string key, std::string_view options ) @@ -117,7 +157,7 @@ profileset_controller_data_wrapper_t::profileset_controller_data_wrapper_t( std: { if ( const auto& value = profileset_controller_t::factory.find( key ); value != profileset_controller_t::factory.end() ) - data = value->second.second(); + data = value->second.second( key, options ); assert( data ); } @@ -129,16 +169,17 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) auto controller = value->second.first( sim, id ); controller->create_options(); opts::parse( sim, "profileset_controller", controller->options, options, - [ this, &sim ]( opts::parse_status status, util::string_view name, util::string_view value ) { - // Fail parsing if strict parsing is used and the option is not found - if ( sim->strict_parsing && status == opts::parse_status::NOT_FOUND ) - return opts::parse_status::FAILURE; - // .. otherwise, just warn that there's an unknown option - if ( status == opts::parse_status::NOT_FOUND ) - sim->error( "Warning: profileset controller '{}' provided unknown option '{}' with value '{}', ignoring.", - key, name, value ); - return status; - } ); + [ this, &sim ]( opts::parse_status status, util::string_view name, util::string_view value ) { + // Fail parsing if strict parsing is used and the option is not found + if ( sim->strict_parsing && status == opts::parse_status::NOT_FOUND ) + return opts::parse_status::FAILURE; + // .. otherwise, just warn that there's an unknown option + if ( status == opts::parse_status::NOT_FOUND ) + sim->error( + "Warning: profileset controller '{}' provided unknown option '{}' with value '{}', ignoring.", + key, name, value ); + return status; + } ); sim->profileset_controller.emplace_back( std::move( controller ) ); return; } @@ -155,20 +196,6 @@ bool profileset_controller_t::controller_exists( std::string key ) return factory.find( key ) != factory.end(); } -const std::string profileset_controller_t::call_point_string( call_point_e call_point ) -{ - switch ( call_point ) - { - case POST_INIT: - return "simulation initialization"; - case POST_ITER: - return "iteration"; - default: - assert( false ); - return "no matching call point"; - } -} - void profileset_controller_t::evaluate( sim_t* sim, call_point_e call_point ) { if ( !sim->profileset_enabled || !sim->parent ) @@ -203,37 +230,6 @@ void profileset_controller_t::evaluate( sim_t* sim, call_point_e call_point ) sim->interrupt(); } -void profileset_controller_t::html_report( const sim_t& sim, std::ostream& out ) -{ - if ( sim.profileset_controller_data.empty() ) - return; - - out << "

    Profileset Sim Control

    \n"; - out << "
    \n"; - - out << "
    Sim Controllers
      \n"; - for ( const auto& controller_data_wrapper : sim.profileset_controller_data ) - if ( const auto& controller_data = controller_data_wrapper.data; controller_data ) - controller_data->report_html_options( out ); - out << "
    \n"; - - // report source, location, and reason of interrupt for - // all registered profileset profileset controllers - bool has_culled_profileset = range::any_of( sim.profileset_controller_data, - []( const auto& entry ) { return entry.data->exit_reasons.size(); } ); - - if ( has_culled_profileset ) - { - out << "
    Interrupted Profilesets
      \n"; - for ( const auto& controller_data_wrapper : sim.profileset_controller_data ) - if ( const auto& controller_data = controller_data_wrapper.data; - controller_data && controller_data->exit_reasons.size() ) - controller_data->report_html_profileset( out ); - out << "
    \n"; - } - out << "
    "; -} - void profileset_controller_t::add_option( std::unique_ptr&& option ) { options.emplace_back( std::move( option ) ); @@ -249,7 +245,7 @@ const std::string profileset_controller_t::message( call_point_e call_point ) { std::string msg = fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), - call_point_string( call_point ) ); + profileset_controller::call_point_string( call_point ) ); if ( call_point == POST_ITER ) msg += std::to_string( sim->current_iteration ); @@ -268,6 +264,82 @@ void profileset_controller_t::set_exit_reason( exit_reason_t&& exit_reason ) pcd[ id ].data->exit_reasons.emplace_back( std::move( exit_reason ) ); } +namespace +{ +// how to do this with reference wrapper instead of template? +template +void report_html_table( + std::ostream& out, std::vector keys, const std::deque& data, + T ref, std::function& )> cond = []( const auto& ) { + return true; + } ) +{ + out << "\n" + << ""; + bool first = true; + for ( const auto& key : keys ) + { + out << fmt::format( ""; + first = false; + } + out << "\n"; + for ( const auto& datum_wrapper : data ) + if ( const auto& datum = datum_wrapper.data; datum && cond( datum ) ) + std::invoke( ref, datum, out ); + out << "
    ", first ? "left" : "center" ) << key << "
    "; +} +} // namespace + +namespace profileset_controller +{ +const std::string call_point_string( call_point_e call_point ) +{ + switch ( call_point ) + { + case POST_INIT: + return "simulation initialization"; + case POST_ITER: + return "iteration"; + default: + assert( false ); + return "no matching call point"; + } +} + +void report_html( const sim_t& sim, std::ostream& out ) +{ + if ( sim.profileset_controller_data.empty() ) + return; + + out << "

    Profileset Sim Control

    \n"; + out << "
    \n"; + + out << "
    Profileset Controllers\n"; + report_html_table( out, { "Type", "Count", "Options" }, sim.profileset_controller_data, + &profileset_controller_data_t::report_html_options ); + out << "
    \n"; + + // report source, location, and reason of interrupt for + // all registered profileset profileset controllers + bool has_culled_profileset = range::any_of( sim.profileset_controller_data, + []( const auto& datum ) { return datum.data->exit_reasons.size(); } ); + + if ( has_culled_profileset ) + { + out << "
    Cancelled Profilesets\n"; + report_html_table( out, { "Type", "Profileset Name", "Cancellation Point", "Reason" }, + sim.profileset_controller_data, &profileset_controller_data_t::report_html_profileset, + []( const auto& datum ) { return datum->exit_reasons.size(); } ); + out << "
    \n"; + } + out << "
    "; +} + +void report_json() +{ +} +} // namespace profileset_controller + bool min_player_stat_t::evaluate_post_init() { return true; @@ -279,24 +351,29 @@ const std::string min_player_stat_t::reason() const util::stat_type_string( rating ) ); } -bool tier_set_count_t::evaluate_post_init() +bool set_bonus_enabled_t::evaluate_post_init() { if ( target_player ) return target_player->sets->has_set_bonus( target_player->specialization(), tier, count ); return true; } -const std::string tier_set_count_t::reason() const +const std::string set_bonus_enabled_t::reason() const { // no to string for set bonus tier or count... // that should definitely exist :) - return fmt::format( "player {} does not have tier {} {} active", target_player->name(), static_cast( tier ), - static_cast( count ) ); + auto set_bonuses = item_set_bonus_t::data( target_player ? target_player->dbc->ptr : false ); + std::string tier_name{}; + for ( const auto& set_bonus : set_bonuses ) + if ( set_bonus.enum_id == static_cast( tier ) ) + tier_name = set_bonus.tier; + return fmt::format( "player {} does not have set {} {}pc active", target_player->name(), tier_name, + static_cast( count + 1 ) ); } -void tier_set_count_t::create_options() +void set_bonus_enabled_t::create_options() { - add_option( opt_func( "tier", [ this ]( sim_t*, util::string_view, util::string_view value ){ + add_option( opt_func( "tier", [ this ]( sim_t*, util::string_view, util::string_view value ) { auto set_bonuses = item_set_bonus_t::data( target_player ? target_player->dbc->ptr : false ); for ( const auto& set_bonus : set_bonuses ) { @@ -315,7 +392,7 @@ void tier_set_count_t::create_options() this->count = static_cast( bonus_value - 1 ); return true; } ) ); - add_option( opt_func( "player", [ this ]( sim_t* sim, util::string_view, util::string_view value ){ + add_option( opt_func( "player", [ this ]( sim_t* sim, util::string_view, util::string_view value ) { for ( auto& player : sim->player_list ) { if ( util::str_compare_ci( player->name(), value ) ) @@ -342,22 +419,3 @@ void tier_set_count_t::create_options() // { // // TODO: implement opt parsing and automatic generation of report json from opts // } - -// void sim_controller_data_wrapper_t::report_html_profileset( std::ostream& output ) const -// { -// for ( const exit_reason_t& exit_reason : exit_reasons ) -// output << "
  • " -// << util::encode_html( exit_reason.profileset_name ) << " " -// << util::encode_html( sim_controller_t::call_point_string( exit_reason.exit_point ) ) << " " -// << util::encode_html( exit_reason.exit_reason ) -// << "
  • \n"; -// } - -// void sim_controller_data_wrapper_t::report_html_options( std::ostream& output ) const -// { -// output << "
  • " -// << util::encode_html( key ) << " "; -// for ( const auto& opt : options ) -// output << fmt::format( "{} ", opt ); -// output << "
  • \n"; -// } diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 3c3bf1378db..98512d90f8c 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -5,13 +5,12 @@ #include "sc_enums.hpp" #include "util/generic.hpp" +#include #include #include -#include /* * TODO: - * - intialize contents of factory map * - implement reporting * - implement profileset culling specialization */ @@ -23,10 +22,13 @@ struct data_wrapper_t { private: std::scoped_lock lock; + public: const T& data; - data_wrapper_t( const T& data, std::recursive_mutex& m ) : lock( m ), data( data ) {} + data_wrapper_t( const T& data, std::recursive_mutex& m ) : lock( m ), data( data ) + { + } }; enum call_point_e @@ -45,21 +47,27 @@ struct exit_reason_t struct profileset_controller_data_t : private noncopyable { + const std::string key; + std::string_view options; std::vector exit_reasons; - profileset_controller_data_t(); + profileset_controller_data_t( std::string_view, std::string_view ); virtual ~profileset_controller_data_t() = default; - virtual void report_html_options( std::ostream& ) {} - virtual void report_html_profileset( std::ostream& ) {} + virtual void report_html_options( std::ostream& ) const; + virtual void report_html_profileset( std::ostream& ) const; }; struct profileset_controller_data_wrapper_t : private noncopyable { +private: static std::atomic_uint id_generator; + +public: std::recursive_mutex mutex; - unsigned int id; - std::string key; + + const unsigned int id; + const std::string key; std::string_view options; std::unique_ptr data; @@ -70,22 +78,22 @@ struct profileset_controller_data_wrapper_t : private noncopyable struct profileset_controller_t : private noncopyable { - using controller_factory_t = std::function(sim_t*, unsigned int)>; - using data_factory_t = std::function()>; + using controller_factory_t = std::function( sim_t*, unsigned int )>; + using data_factory_t = + std::function( std::string_view, std::string_view )>; using factory_fn_pair_t = std::pair; + protected: friend profileset_controller_data_wrapper_t; static std::unordered_map factory; + public: static bool register_controller( std::string, factory_fn_pair_t&& ); static bool controller_exists( std::string ); using data_t = profileset_controller_data_t; - static const std::string call_point_string( call_point_e call_point ); static void evaluate( sim_t* sim, call_point_e call_point ); - static void html_report( const sim_t&, std::ostream& ); - sim_t* parent; sim_t* sim; const unsigned int id; @@ -99,11 +107,15 @@ struct profileset_controller_t : private noncopyable virtual const std::string name() const = 0; virtual const std::string reason() const = 0; - virtual void create_options() {} - virtual bool evaluate_post_init() { + virtual void create_options() + { + } + virtual bool evaluate_post_init() + { return true; } - virtual bool evaluate_post_iter() { + virtual bool evaluate_post_iter() + { return true; } @@ -115,6 +127,13 @@ struct profileset_controller_t : private noncopyable void set_exit_reason( exit_reason_t&& ); }; +namespace profileset_controller +{ +const std::string call_point_string( call_point_e call_point ); +void report_html( const sim_t&, std::ostream& ); +void report_json(); +}; // namespace profileset_controller + struct min_player_stat_t : profileset_controller_t { /* @@ -137,7 +156,7 @@ struct min_player_stat_t : profileset_controller_t const std::string reason() const override; }; -struct tier_set_count_t : profileset_controller_t +struct set_bonus_enabled_t : profileset_controller_t { using data_t = profileset_controller_data_t; @@ -145,12 +164,12 @@ struct tier_set_count_t : profileset_controller_t set_bonus_type_e tier; set_bonus_e count; - tier_set_count_t( sim_t* sim, unsigned int id ) : profileset_controller_t( sim, id ) + set_bonus_enabled_t( sim_t* sim, unsigned int id ) : profileset_controller_t( sim, id ) { } const std::string name() const override { - return "tier_set_count"; + return "set_bonus_enabled"; } bool evaluate_post_init() override; const std::string reason() const override; From 028082a8809d213e5699c2bcf2654520f1083388 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:22:18 -0700 Subject: [PATCH 18/21] implement json reporting --- engine/report/json/report_json.cpp | 12 +-- engine/sim/sim_controller.cpp | 163 ++++++++--------------------- engine/sim/sim_controller.hpp | 5 +- 3 files changed, 45 insertions(+), 135 deletions(-) diff --git a/engine/report/json/report_json.cpp b/engine/report/json/report_json.cpp index 0f50e3f174f..c3bf0f91d07 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1047,11 +1047,6 @@ void profileset_json2( const profileset::profilesets_t& profileset, const sim_t& } } - // report source, location, and reason of interrupt for - // all registered profileset sim controllers - // for ( const auto& controller: sim.sim_controllers ) - // controller->report_json_profileset( obj ); - // Optional override ouput data if ( !sim.profileset_output_data.empty() ) { @@ -1097,12 +1092,6 @@ void profileset_json3( const profileset::profilesets_t& profilesets, const sim_t obj[ "iterations" ] = as( result.iterations() ); } - // report source, location, and reason of interrupt for - // all registered profileset sim controllers - // for ( const auto& controller: sim.sim_controllers ) - // controller->report_json_profileset( obj ); - - // Optional override ouput data if ( !sim.profileset_output_data.empty() ) { @@ -1290,6 +1279,7 @@ void to_json( const ::report::json::report_configuration_t& report_configuration { auto profileset_root = root[ "profilesets" ]; profileset_json( report_configuration, *sim.profilesets, sim, profileset_root ); + profileset_controller::report_json( sim, root ); } if ( !sim.plot->dps_plot_stats.empty() ) diff --git a/engine/sim/sim_controller.cpp b/engine/sim/sim_controller.cpp index a3004db3736..404d66bb3ac 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/sim_controller.cpp @@ -6,112 +6,7 @@ #include "profileset.hpp" #include "sc_enums.hpp" #include "sim.hpp" -/* - * TODO: - * - initialize contents of factory map - * - pass in option values to pc_t, sim_controller.cpp:21 - * - * PROCEDURE: - * 1) register controllers via `profileset_controller_t::register_controller(...)`, - * which emplaces controller(s) into the factory map for later construction. - * 2) parent sim parses options, constructing `pcd_w_t` for each type via factory map. - * 3) child sim iterates over parent->profileset_controller_data, constructing `pc_t` - * for each `pcd_w_t` - */ - -/* - * sim_controller_t: - * - scope: profileset - * - evaluates whether or not profileset should exit - * - reports why an exit occurs - * - provides options, as in many cases a custom `sim_controller_data_t` type is - * not required for storage reasons - * - * sim_controller_data_t - * - scope: parent sim - * - contains custom data, such as current "winner" for binary controllers - * including profileset culling - * - contains option values - * - * sim_controller_data_wrapper_t - * - scope: parent sim - * - contains mutex for thread safety when operating on sim_controller_data_t - * - contains sim_controller_data_t pointer - * - contains exit_reasons () - * - contains registered option definitions - * - * data_wrapper_t - * - scope: sim_controller_data_t getter - * - contains scoped recursive lock on sim_controller_data_t - * - contains reference to sim_controller_data_t data - * - * notes: - * - * profilesets_t objects are configured and init via - * - sim_t::execute() > sim_t::iterate() > sim_t::init() > profilesets->initialize( sim ) - * - profilesets_t::parse( ... ) > m_profilesets.push_back( ... ) - * profilesets_t is executed via - * - profilesets->iterate( sim ) > profilesets_t::generate_work( ... ) - * profileset sims/workers are constructed and executed via - * - SEQUENTIAL: new sim_t( ... ) - * - PARALLEL: push_back( new worker_t( profileset_control ) ) - * profileset-creation options are stripped from profileset_control prior to construction - * - profilesets_t::create_sim_options, profilesets_t::initialize, filter_control - * - * sim where ( !parent ) parses values stored in sim_t::sim_controller_options, - * static sim_controller_data_t::register([ key, opts ]) -> scd_w_t { key, id, scd_t { parsed_options_values... } } - * sim where ( parent ) iterates over sim_t::sim_controller_data - * sim_controller_data_t::register_controller() -> sc_t { id, parsed_options_values... } - * - * how can i copy parsed_option_values from scd_t -> sc_t without a type that - * contains the option_values and without reparsing the options? - * - * use atomic incrementing id for scds - * - * no storage as a map, as scd id and vector position should be identical - * - * move implementation of reporting strictly to scd_t - * - * implement reporting under a static member to handle all reporting with minimal - * implementation in html/json files - */ - -// profilesets object is configured/init via sim_t -// - execute() -:> iterate() -:> init() -:> profilesets->initialize(self) -// - parse(...) -:> m.profilesets.push_back(...) -// profilesets are executed via profilesets->iterate(self) -> generate_work(...) -// profileset sims are created and executed via -// - profilesets->iterate(self) -:> generate_work(...) -:> -// - SEQUENTIAL: new sim_t -// - PARALLEL: push_back new worker_t(pset_data) -// strip profileset-specific options from opts used to initialize sims -// profilesets_t::create_sim_options, profilesets_t::initialize, filter_control - -/* - * implementation: - * - parent sim parses sim controller instantiation option and child options - * - sc_main.cpp:273 arg parse -> sc_main.cpp:287 opt parse and actor creation - * - parent sim constructs sim controller data for auto incrementing index id - * - as work is created, child option data is copied and sim controller is constructed by iterating over scd vec, - * passing along id as well - * - * NOTES: - * - implement sim opts as function options, which induces opt order dependency, no preventing option parse without - * filtering profileset sim control - * - implement sim opts as map list and execute processing in setup or init if sim is parent - * OTHER CHANGES: - * - move exit_reasons and options (?) from data wrapper to data - * - provide getters and setters for exit_reasons and options as appropriate - * - no key-value pairs, everything should be a vec - * - initialize controllers and scd with atomic id generated via scd - * - rename to profileset_controller, as they control profilesets not sims per se - */ - -/* - * Global profileset_controller_t factories. If you create a `profileset_controller_t` - * subtype specific to some context, have that context register it early in sim init - * via the static function `profileset_controller_t::register_controller`. - */ + std::unordered_map profileset_controller_t::factory = { { "set_bonus_enabled", { []( sim_t* sim, unsigned int id ) { return std::make_unique( sim, id ); }, @@ -152,6 +47,31 @@ void profileset_controller_data_t::report_html_profileset( std::ostream& output } } +void profileset_controller_data_t::report_json_options( js::JsonOutput& root ) const +{ + auto output = root.add(); + auto splits = util::string_split( options, "," ); + output[ "profileset_controller_name" ] = key; + for ( const auto& split : splits ) + { + auto subsplit = util::string_split( split, "=" ); + assert( subsplit.size() == 2 ); + output[ subsplit[ 0 ] ] = subsplit[ 1 ]; + } +} + +void profileset_controller_data_t::report_json_profileset( js::JsonOutput& root ) const +{ + for ( const auto& [ name, call_point, reason ] : exit_reasons ) + { + auto output = root.add(); + output[ "profileset_name" ] = name; + output[ "interrupted_by" ] = key; + output[ "exit_point" ] = profileset_controller::call_point_string( call_point ); + output[ "exit_reason" ] = reason; + } +} + profileset_controller_data_wrapper_t::profileset_controller_data_wrapper_t( std::string key, std::string_view options ) : mutex(), id( id_generator++ ), key( key ), options( options ) { @@ -335,8 +255,22 @@ void report_html( const sim_t& sim, std::ostream& out ) out << ""; } -void report_json() +void report_json( const sim_t& sim, js::JsonOutput& output ) { + if ( sim.profileset_controller_data.empty() ) + return; + + auto root = output[ "profileset_controller" ]; + + auto exits = root[ "cancelled_profilesets" ].make_array(); + for ( const auto& datum_wrapper : sim.profileset_controller_data ) + if ( const auto& datum = datum_wrapper.data; datum ) + datum->report_json_profileset( exits ); + + auto controllers = root[ "enabled_controllers" ].make_array(); + for ( const auto& datum_wrapper : sim.profileset_controller_data ) + if ( const auto& datum = datum_wrapper.data; datum ) + datum->report_json_options( controllers ); } } // namespace profileset_controller @@ -404,18 +338,3 @@ void set_bonus_enabled_t::create_options() return false; } ) ); } - -// void sim_controller_data_wrapper_t::report_json_profileset( js::JsonOutput& output ) const -// { -// for ( const exit_reason_t& exit_reason : exit_reasons ) -// { -// output[ "interrupted_by" ] = exit_reason.profileset_name; -// output[ "exit_point" ] = sim_controller_t::call_point_string( exit_reason.exit_point ); -// output[ "exit_reason" ] = exit_reason.exit_reason; -// } -// } - -// void sim_controller_data_wrapper_t::report_json_options( js::JsonOutput& ) const -// { -// // TODO: implement opt parsing and automatic generation of report json from opts -// } diff --git a/engine/sim/sim_controller.hpp b/engine/sim/sim_controller.hpp index 98512d90f8c..5cf2129e7c3 100644 --- a/engine/sim/sim_controller.hpp +++ b/engine/sim/sim_controller.hpp @@ -11,7 +11,6 @@ /* * TODO: - * - implement reporting * - implement profileset culling specialization */ @@ -56,6 +55,8 @@ struct profileset_controller_data_t : private noncopyable virtual void report_html_options( std::ostream& ) const; virtual void report_html_profileset( std::ostream& ) const; + virtual void report_json_options( js::JsonOutput& ) const; + virtual void report_json_profileset( js::JsonOutput& ) const; }; struct profileset_controller_data_wrapper_t : private noncopyable @@ -131,7 +132,7 @@ namespace profileset_controller { const std::string call_point_string( call_point_e call_point ); void report_html( const sim_t&, std::ostream& ); -void report_json(); +void report_json( const sim_t&, js::JsonOutput& output ); }; // namespace profileset_controller struct min_player_stat_t : profileset_controller_t From 097174a2c240dbb620f02b0d1260c870720c1133 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:29:22 -0700 Subject: [PATCH 19/21] rename files --- engine/sim/{sim_controller.cpp => profileset_controller.cpp} | 2 +- engine/sim/{sim_controller.hpp => profileset_controller.hpp} | 0 engine/sim/sim.hpp | 2 +- source_files/QT_engine.pri | 4 ++-- source_files/VS_engine.props | 4 ++-- source_files/cmake_engine.txt | 4 ++-- source_files/engine_make | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename engine/sim/{sim_controller.cpp => profileset_controller.cpp} (99%) rename engine/sim/{sim_controller.hpp => profileset_controller.hpp} (100%) diff --git a/engine/sim/sim_controller.cpp b/engine/sim/profileset_controller.cpp similarity index 99% rename from engine/sim/sim_controller.cpp rename to engine/sim/profileset_controller.cpp index 404d66bb3ac..c42eddd01c5 100644 --- a/engine/sim/sim_controller.cpp +++ b/engine/sim/profileset_controller.cpp @@ -1,4 +1,4 @@ -#include "sim_controller.hpp" +#include "profileset_controller.hpp" #include "dbc/dbc.hpp" #include "dbc/item_set_bonus.hpp" diff --git a/engine/sim/sim_controller.hpp b/engine/sim/profileset_controller.hpp similarity index 100% rename from engine/sim/sim_controller.hpp rename to engine/sim/profileset_controller.hpp diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 41ccf6039f8..a1e94b54f87 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -12,7 +12,7 @@ #include "player/gear_stats.hpp" #include "progress_bar.hpp" #include "sim/option.hpp" -#include "sim_controller.hpp" +#include "profileset_controller.hpp" #include "sim_ostream.hpp" #include "util/concurrency.hpp" #include "util/rng.hpp" diff --git a/source_files/QT_engine.pri b/source_files/QT_engine.pri index 2b8ff4f5293..71a086ccc5b 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -165,13 +165,13 @@ HEADERS += engine/sim/plot.hpp HEADERS += engine/sim/proc.hpp HEADERS += engine/sim/proc_rng.hpp HEADERS += engine/sim/profileset.hpp +HEADERS += engine/sim/profileset_controller.hpp HEADERS += engine/sim/progress_bar.hpp HEADERS += engine/sim/raid_event.hpp HEADERS += engine/sim/reforge_plot.hpp HEADERS += engine/sim/scale_factor_control.hpp HEADERS += engine/sim/sim.hpp HEADERS += engine/sim/sim_control.hpp -HEADERS += engine/sim/sim_controller.hpp HEADERS += engine/sim/sim_ostream.hpp HEADERS += engine/sim/uptime.hpp HEADERS += engine/sim/work_queue.hpp @@ -362,12 +362,12 @@ SOURCES += engine/sim/plot.cpp SOURCES += engine/sim/proc.cpp SOURCES += engine/sim/proc_rng.cpp SOURCES += engine/sim/profileset.cpp +SOURCES += engine/sim/profileset_controller.cpp SOURCES += engine/sim/progress_bar.cpp SOURCES += engine/sim/raid_event.cpp SOURCES += engine/sim/reforge_plot.cpp SOURCES += engine/sim/scale_factor_control.cpp SOURCES += engine/sim/sim.cpp -SOURCES += engine/sim/sim_controller.cpp SOURCES += engine/sim/sim_ostream.cpp SOURCES += engine/sim/uptime_benefit.cpp SOURCES += engine/util/cache.cpp diff --git a/source_files/VS_engine.props b/source_files/VS_engine.props index 44a98da242c..0817a8109a3 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -169,13 +169,13 @@ To change the list of source files run synchronize.py + - @@ -365,12 +365,12 @@ To change the list of source files run synchronize.py + - diff --git a/source_files/cmake_engine.txt b/source_files/cmake_engine.txt index 0845f9d2dde..dfe2a652435 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -163,13 +163,13 @@ sim/plot.hpp sim/proc.hpp sim/proc_rng.hpp sim/profileset.hpp +sim/profileset_controller.hpp sim/progress_bar.hpp sim/raid_event.hpp sim/reforge_plot.hpp sim/scale_factor_control.hpp sim/sim.hpp sim/sim_control.hpp -sim/sim_controller.hpp sim/sim_ostream.hpp sim/uptime.hpp sim/work_queue.hpp @@ -359,12 +359,12 @@ sim/plot.cpp sim/proc.cpp sim/proc_rng.cpp sim/profileset.cpp +sim/profileset_controller.cpp sim/progress_bar.cpp sim/raid_event.cpp sim/reforge_plot.cpp sim/scale_factor_control.cpp sim/sim.cpp -sim/sim_controller.cpp sim/sim_ostream.cpp sim/uptime_benefit.cpp util/cache.cpp diff --git a/source_files/engine_make b/source_files/engine_make index a069441936e..09b2f575205 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -163,12 +163,12 @@ SRC += \ sim$(PATHSEP)proc.cpp \ sim$(PATHSEP)proc_rng.cpp \ sim$(PATHSEP)profileset.cpp \ + sim$(PATHSEP)profileset_controller.cpp \ sim$(PATHSEP)progress_bar.cpp \ sim$(PATHSEP)raid_event.cpp \ sim$(PATHSEP)reforge_plot.cpp \ sim$(PATHSEP)scale_factor_control.cpp \ sim$(PATHSEP)sim.cpp \ - sim$(PATHSEP)sim_controller.cpp \ sim$(PATHSEP)sim_ostream.cpp \ sim$(PATHSEP)uptime_benefit.cpp \ util$(PATHSEP)cache.cpp \ From f9d008e452edc5baf44ff7cf6a6091f21828d64e Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 6 Dec 2025 04:38:36 -0700 Subject: [PATCH 20/21] rename files --- engine/class_modules/monk/sc_monk.cpp | 2 +- engine/class_modules/monk/sc_monk.hpp | 2 +- .../{profileset_controller.cpp => profileset_control.cpp} | 6 +++--- .../{profileset_controller.hpp => profileset_control.hpp} | 0 engine/sim/sim.hpp | 2 +- source_files/QT_engine.pri | 4 ++-- source_files/VS_engine.props | 4 ++-- source_files/cmake_engine.txt | 4 ++-- source_files/engine_make | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) rename engine/sim/{profileset_controller.cpp => profileset_control.cpp} (98%) rename engine/sim/{profileset_controller.hpp => profileset_control.hpp} (100%) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index d99190d9d4f..61e3bdd5e81 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -36,7 +36,7 @@ #include "report/charts.hpp" #include "report/highchart.hpp" #include "sc_enums.hpp" -#include "sim/sim_controller.hpp" +#include "sim/profileset_control.hpp" #include diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 3deac666961..6c7dc799573 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -16,7 +16,7 @@ #include "sc_enums.hpp" #include "sc_stagger.hpp" #include "sim/proc.hpp" -#include "sim/sim_controller.hpp" +#include "sim/profileset_control.hpp" #include "util/timeline.hpp" #include diff --git a/engine/sim/profileset_controller.cpp b/engine/sim/profileset_control.cpp similarity index 98% rename from engine/sim/profileset_controller.cpp rename to engine/sim/profileset_control.cpp index c42eddd01c5..a06f2e8a5d6 100644 --- a/engine/sim/profileset_controller.cpp +++ b/engine/sim/profileset_control.cpp @@ -1,4 +1,4 @@ -#include "profileset_controller.hpp" +#include "profileset_control.hpp" #include "dbc/dbc.hpp" #include "dbc/item_set_bonus.hpp" @@ -164,12 +164,12 @@ profileset_controller_t::profileset_controller_t( sim_t* sim, unsigned int id ) const std::string profileset_controller_t::message( call_point_e call_point ) { std::string msg = - fmt::format( "Profileset {} was canceled by {} after {}", parent->profilesets->current_profileset_name(), name(), + fmt::format( "Profileset {} was canceled by Profileset Controller {} after {}", parent->profilesets->current_profileset_name(), name(), profileset_controller::call_point_string( call_point ) ); if ( call_point == POST_ITER ) msg += std::to_string( sim->current_iteration ); - if ( const std::string r = reason(); r != "" ) + if ( const auto r = reason(); !r.empty() ) msg += fmt::format( " because {}.", r ); else msg += "."; diff --git a/engine/sim/profileset_controller.hpp b/engine/sim/profileset_control.hpp similarity index 100% rename from engine/sim/profileset_controller.hpp rename to engine/sim/profileset_control.hpp diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index a1e94b54f87..487f59f6447 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -12,7 +12,7 @@ #include "player/gear_stats.hpp" #include "progress_bar.hpp" #include "sim/option.hpp" -#include "profileset_controller.hpp" +#include "profileset_control.hpp" #include "sim_ostream.hpp" #include "util/concurrency.hpp" #include "util/rng.hpp" diff --git a/source_files/QT_engine.pri b/source_files/QT_engine.pri index 71a086ccc5b..89ff6a2cf88 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -165,7 +165,7 @@ HEADERS += engine/sim/plot.hpp HEADERS += engine/sim/proc.hpp HEADERS += engine/sim/proc_rng.hpp HEADERS += engine/sim/profileset.hpp -HEADERS += engine/sim/profileset_controller.hpp +HEADERS += engine/sim/profileset_control.hpp HEADERS += engine/sim/progress_bar.hpp HEADERS += engine/sim/raid_event.hpp HEADERS += engine/sim/reforge_plot.hpp @@ -362,7 +362,7 @@ SOURCES += engine/sim/plot.cpp SOURCES += engine/sim/proc.cpp SOURCES += engine/sim/proc_rng.cpp SOURCES += engine/sim/profileset.cpp -SOURCES += engine/sim/profileset_controller.cpp +SOURCES += engine/sim/profileset_control.cpp SOURCES += engine/sim/progress_bar.cpp SOURCES += engine/sim/raid_event.cpp SOURCES += engine/sim/reforge_plot.cpp diff --git a/source_files/VS_engine.props b/source_files/VS_engine.props index 0817a8109a3..7d1be47fc9a 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -169,7 +169,7 @@ To change the list of source files run synchronize.py - + @@ -365,7 +365,7 @@ To change the list of source files run synchronize.py - + diff --git a/source_files/cmake_engine.txt b/source_files/cmake_engine.txt index dfe2a652435..446e2b032b1 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -163,7 +163,7 @@ sim/plot.hpp sim/proc.hpp sim/proc_rng.hpp sim/profileset.hpp -sim/profileset_controller.hpp +sim/profileset_control.hpp sim/progress_bar.hpp sim/raid_event.hpp sim/reforge_plot.hpp @@ -359,7 +359,7 @@ sim/plot.cpp sim/proc.cpp sim/proc_rng.cpp sim/profileset.cpp -sim/profileset_controller.cpp +sim/profileset_control.cpp sim/progress_bar.cpp sim/raid_event.cpp sim/reforge_plot.cpp diff --git a/source_files/engine_make b/source_files/engine_make index 09b2f575205..1047377ba7f 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -163,7 +163,7 @@ SRC += \ sim$(PATHSEP)proc.cpp \ sim$(PATHSEP)proc_rng.cpp \ sim$(PATHSEP)profileset.cpp \ - sim$(PATHSEP)profileset_controller.cpp \ + sim$(PATHSEP)profileset_control.cpp \ sim$(PATHSEP)progress_bar.cpp \ sim$(PATHSEP)raid_event.cpp \ sim$(PATHSEP)reforge_plot.cpp \ From 16d75d4bf934781e4ad8c430dc71f6c29cdf8ea3 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 7 Dec 2025 01:24:49 -0700 Subject: [PATCH 21/21] add helper function to create factory map function pairs. --- engine/sim/profileset_control.cpp | 14 +++++--------- engine/sim/profileset_control.hpp | 23 ++++++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index a06f2e8a5d6..ed1517ca525 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -8,11 +8,7 @@ #include "sim.hpp" std::unordered_map profileset_controller_t::factory = { - { "set_bonus_enabled", - { []( sim_t* sim, unsigned int id ) { return std::make_unique( sim, id ); }, - []( std::string_view key, std::string_view options ) { - return std::make_unique( key, options ); - } } } }; + { "set_bonus_enabled", profileset_controller::create_fn_pair() } }; std::atomic_uint profileset_controller_data_wrapper_t::id_generator; @@ -163,13 +159,13 @@ profileset_controller_t::profileset_controller_t( sim_t* sim, unsigned int id ) const std::string profileset_controller_t::message( call_point_e call_point ) { - std::string msg = - fmt::format( "Profileset {} was canceled by Profileset Controller {} after {}", parent->profilesets->current_profileset_name(), name(), - profileset_controller::call_point_string( call_point ) ); + std::string msg = fmt::format( "Profileset {} was canceled by Profileset Controller {} after {}", + parent->profilesets->current_profileset_name(), name(), + profileset_controller::call_point_string( call_point ) ); if ( call_point == POST_ITER ) msg += std::to_string( sim->current_iteration ); - if ( const auto r = reason(); !r.empty() ) + if ( const auto r = reason(); !r.empty() ) msg += fmt::format( " because {}.", r ); else msg += "."; diff --git a/engine/sim/profileset_control.hpp b/engine/sim/profileset_control.hpp index 5cf2129e7c3..410bbafd3ec 100644 --- a/engine/sim/profileset_control.hpp +++ b/engine/sim/profileset_control.hpp @@ -16,6 +16,13 @@ struct sim_t; +enum call_point_e +{ + CALL_POINT_NONE, + POST_INIT, + POST_ITER +}; + template struct data_wrapper_t { @@ -30,13 +37,6 @@ struct data_wrapper_t } }; -enum call_point_e -{ - CALL_POINT_NONE, - POST_INIT, - POST_ITER -}; - struct exit_reason_t { const std::string profileset_name; @@ -133,6 +133,14 @@ namespace profileset_controller const std::string call_point_string( call_point_e call_point ); void report_html( const sim_t&, std::ostream& ); void report_json( const sim_t&, js::JsonOutput& output ); + +template +profileset_controller_t::factory_fn_pair_t create_fn_pair() +{ + return { + []( sim_t* sim, unsigned int id ) { return std::make_unique( sim, id ); }, + []( std::string_view key, std::string_view options ) { return std::make_unique( key, options ); } }; +} }; // namespace profileset_controller struct min_player_stat_t : profileset_controller_t @@ -168,6 +176,7 @@ struct set_bonus_enabled_t : profileset_controller_t set_bonus_enabled_t( sim_t* sim, unsigned int id ) : profileset_controller_t( sim, id ) { } + const std::string name() const override { return "set_bonus_enabled";