From 4a46c402820f9bbae86bc59c1fd2dd08e2630e97 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/26] [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 31b8fbdd0bf..5ea738a0625 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -4846,3 +4846,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 ef5e29e25e5..968f6ce20ae 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" @@ -20,6 +21,7 @@ #include #include #include +#include struct actor_target_data_t; struct buff_t; @@ -46,10 +48,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; @@ -581,6 +646,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; @@ -780,6 +852,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 @@ -833,3 +918,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 693448a4eb54aa8c051c8187bd3b31a23bc392c4 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/26] [sim_controllers] Fix a few implementation details and implement basic example. --- engine/class_modules/monk/sc_monk.cpp | 8 ++++++ engine/class_modules/monk/sc_monk.hpp | 3 ++ 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, 126 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 dd9fb303b4f..12957c42fe3 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5549,6 +5549,14 @@ 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() { base_t::init_spells(); diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 1a8419e8ca6..7131c031219 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 @@ -1157,6 +1158,8 @@ 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; void init_base_stats() override; diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 5ea738a0625..293c3318152 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1552,6 +1552,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 ), @@ -3110,6 +3112,12 @@ bool sim_t::iterate() progress_bar.init(); + if ( profileset_enabled && parent && evaluate_sim_controller_post_init() ) + { + cancel(); + return true; + } + try { activate_actors(); @@ -3127,6 +3135,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 ) @@ -4847,6 +4858,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() { } @@ -4855,15 +4875,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 968f6ce20ae..7cb3d0f5ff5 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -54,16 +54,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; @@ -81,7 +77,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 { @@ -103,7 +98,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; @@ -856,11 +852,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(); @@ -932,5 +931,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 eb2d8b61f9a..26b24cc520a 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -165,6 +165,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 @@ -355,6 +356,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 13c40bd2177..4dbc41b5ec8 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -169,6 +169,7 @@ To change the list of source files run synchronize.py + @@ -358,6 +359,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 cccd4bfe0e8..1fb24d14e3a 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -163,6 +163,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 @@ -352,6 +353,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 9b0871db0a9..5e9de03549a 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -163,6 +163,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 48857ae9e40ffec8b85918888eeb28dfd3d89883 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/26] [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 7cb3d0f5ff5..c1de3561f98 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -108,7 +108,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 @@ -926,10 +926,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 59da2e3d86f5310851592c1811a87fc1ff67d0f5 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/26] [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 293c3318152..27c64676294 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -3113,10 +3113,7 @@ bool sim_t::iterate() progress_bar.init(); if ( profileset_enabled && parent && evaluate_sim_controller_post_init() ) - { cancel(); - return true; - } try { From a31a9989fe4567a0dcace46a78f51a74394d6f85 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/26] [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 12957c42fe3..e26a8b4ecb5 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5553,7 +5553,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 497a3675826f3f2a949e53f992509e0b4414e595 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/26] [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 27c64676294..fe03ead7a34 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -3112,8 +3112,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 { @@ -3132,8 +3131,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; @@ -4876,24 +4874,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 c1de3561f98..92384023fe7 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -88,21 +88,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 @@ -645,7 +656,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: @@ -852,17 +863,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 e66055e1b353c9303ae882e1bc0a6d629a3cdb27 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/26] [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 e26a8b4ecb5..cda4190c267 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5553,8 +5553,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 92384023fe7..6f268cf3c55 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -96,6 +96,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; @@ -859,19 +862,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 @@ -926,18 +916,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 1aec79f977c31436fba3a1c7c1c37fa53ea2c7a6 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/26] [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 fe03ead7a34..9a3914f603f 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -4852,86 +4852,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 6f268cf3c55..9054f175d33 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -59,7 +59,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; @@ -78,18 +80,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 }; @@ -102,18 +100,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& ) {}; @@ -125,6 +138,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 c654db2b76d390a8a938db82801781506f60d9e5 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/26] [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 9054f175d33..a867270f70c 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -112,7 +112,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 e8a3d8587010d7f3630a510262a6f073f4d462d3 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/26] [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 a867270f70c..296a2572d8b 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -48,8 +48,7 @@ namespace report::json class report_configuration_t; } -namespace profileset -{ +namespace profileset{ class profilesets_t; } @@ -679,7 +678,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 2cddf0ef27d617d0c832656f1369178d39a8a647 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/26] 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 4331563c7b5433beab1f4a0937fd6f5b5f81d8ec 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/26] [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 80db8ebbb11..51277ab5bb3 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1037,6 +1037,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() ) { @@ -1082,6 +1087,12 @@ void profileset_json3( const profileset::profilesets_t& profilesets, const sim_t obj2[ "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 fd0b58d94ab..a903d1639c3 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1164,6 +1164,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 296a2572d8b..2c741a42219 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -126,9 +126,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 @@ -676,8 +691,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 49c0c68632b54429a7d51e777bcb95e88c5d67c8 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/26] [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 cda4190c267..47b75a9d6ce 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5553,7 +5553,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 51277ab5bb3..98213be7821 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1039,8 +1039,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() ) @@ -1089,8 +1089,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 a903d1639c3..c4a14bae637 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1165,22 +1165,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 2c741a42219..a5ad8a53967 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -58,24 +58,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; }; @@ -97,12 +106,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 ); @@ -126,25 +130,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(); @@ -152,11 +137,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 f363d03809caf31b9b07a66235b9814683f160d8 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/26] [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 a5ad8a53967..e0406a36cb7 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -72,7 +72,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 ); @@ -116,9 +116,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 0d2252ddc51f2dcb2a096dd9ce5b04257cc6ab5c 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/26] checkpoint --- engine/class_modules/monk/sc_monk.cpp | 23 +- engine/report/report_html_sim.cpp | 23 +- engine/sim/sim.cpp | 27 +- engine/sim/sim.hpp | 462 +++++++++++--------------- engine/sim/sim_controller.cpp | 290 ++++++++++++---- engine/sim/sim_controller.hpp | 135 ++++++-- 6 files changed, 571 insertions(+), 389 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 47b75a9d6ce..3a834f61253 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -41,6 +41,7 @@ Manatee #include "report/charts.hpp" #include "report/highchart.hpp" #include "sc_enums.hpp" +#include "sim/sim_controller.hpp" #include @@ -5551,10 +5552,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 c4a14bae637..bfd69d9d822 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1164,28 +1164,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 9a3914f603f..98b1b6bc594 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1552,8 +1552,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 ), @@ -2888,6 +2888,23 @@ void sim_t::init() } init_mutex.lock(); + + // TODO: convert to new init registry system + 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_mutex.unlock(); @@ -3112,7 +3129,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 { @@ -3131,7 +3148,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; @@ -3953,6 +3970,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, 50, UINT_MAX ) ); + // 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_bool( "chart_show_relative_difference_percent", chart_show_relative_difference_percent ) ); diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index e0406a36cb7..edb3105d9ce 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" @@ -22,6 +24,7 @@ #include #include #include +#include struct actor_target_data_t; struct buff_t; @@ -30,7 +33,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; @@ -49,123 +52,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 @@ -194,7 +89,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 @@ -208,9 +103,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; @@ -218,56 +113,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; @@ -286,15 +180,15 @@ struct sim_t : private sc_thread_t 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; @@ -336,13 +230,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; @@ -354,81 +248,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 @@ -442,33 +336,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 @@ -502,6 +396,14 @@ struct sim_t : private sc_thread_t timespan_t salvaged_fusion_amplifier_precast = 0_s; /// Fraction of the time that the player is above the health threshold for Titanic Ocular Gland. double titanic_ocular_gland_worthy_chance = 1.0; + /// Sets the chance that the player successfully faces their Doubt to get the Newfound Resolve buff. + double newfound_resolve_success_chance = 1.0; + /// 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; + /// 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 double shredded_soul_pickup_chance = 1.0; /// Type stat gained from So'leah's Secret Technique @@ -521,6 +423,13 @@ struct sim_t : private sc_thread_t bool disable_soul_igniter_second_use = true; /// Disables the execute effect of Inscrutable Quantum Device since it is avoidable in game bool disable_iqd_execute = false; + // Better Together Override + // Defaults active + 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; // Earthbreaker's Impact weak points triggered unsigned int earthbreakers_impact_weak_points = 3; // Grim Eclipse Dot Duration override @@ -623,7 +532,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; @@ -650,7 +559,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; @@ -692,14 +601,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; @@ -719,7 +631,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 @@ -765,47 +677,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 ); bool is_initialized() { @@ -825,7 +737,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 @@ -834,7 +746,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 )... ) ); } /** @@ -889,13 +801,14 @@ struct sim_t : private sc_thread_t { 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 ) + { target_data_initializer.push_back( cb ); } const rng::rng_t& rng() const { return _rng; } rng::rng_t& rng() { 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 @@ -916,9 +829,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... ) ); @@ -931,9 +844,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... ) ); @@ -949,3 +862,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 b764a10a25a3a7683bc6cf28e25c9b69c6c5cf93 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/26] 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 3a834f61253..aab4c98025c 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5553,7 +5553,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 342eb204bc0e9d80323a1fc8e40e373261c1eda8 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/26] 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 aab4c98025c..ca3c573431c 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5550,32 +5550,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 7131c031219..e277eec86fe 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -1158,7 +1158,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 bfd69d9d822..386b91067da 100644 --- a/engine/report/report_html_sim.cpp +++ b/engine/report/report_html_sim.cpp @@ -1164,7 +1164,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 11c903b279ef1814b028f1c10c5fee42b3c861b3 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/26] 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 98213be7821..5d4b6e519cc 100644 --- a/engine/report/json/report_json.cpp +++ b/engine/report/json/report_json.cpp @@ -1037,11 +1037,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() ) { @@ -1087,12 +1082,6 @@ void profileset_json3( const profileset::profilesets_t& profilesets, const sim_t obj2[ "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() ) { @@ -1280,6 +1269,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 c494dd53fcc35dfdfb08754eed1444a86415921f 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/26] 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 edb3105d9ce..fcf818a4a00 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 26b24cc520a..9a393598469 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -159,13 +159,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 @@ -351,12 +351,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 4dbc41b5ec8..fb1f78a7626 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -163,13 +163,13 @@ To change the list of source files run synchronize.py + - @@ -354,12 +354,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 1fb24d14e3a..f66b8ef5e5b 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -157,13 +157,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 @@ -348,12 +348,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 5e9de03549a..966d6443090 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -158,12 +158,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 643290bbcf63a9beda6010dd60b5f1ef5eb90c8a 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/26] 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 ca3c573431c..316abd7353e 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -41,7 +41,7 @@ Manatee #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 e277eec86fe..e30e3886c91 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 fcf818a4a00..56d47af0180 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 9a393598469..67ceaa40024 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -159,7 +159,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 @@ -351,7 +351,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 fb1f78a7626..7cf4f81370b 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -163,7 +163,7 @@ To change the list of source files run synchronize.py - + @@ -354,7 +354,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 f66b8ef5e5b..7cc4e9d8867 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -157,7 +157,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 @@ -348,7 +348,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 966d6443090..415f966e10a 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -158,7 +158,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 669aa1de37d76c025c05ca1de5fd350b07466ffe 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/26] 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"; From cbf601fbc980a25e2e03b47cc96f95c63459c23a Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 16 Jun 2026 10:43:51 -0600 Subject: [PATCH 22/26] clean up some notes --- engine/sim/profileset_control.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/engine/sim/profileset_control.hpp b/engine/sim/profileset_control.hpp index 410bbafd3ec..6bf7fb337ef 100644 --- a/engine/sim/profileset_control.hpp +++ b/engine/sim/profileset_control.hpp @@ -9,11 +9,6 @@ #include #include -/* - * TODO: - * - implement profileset culling specialization - */ - struct sim_t; enum call_point_e From d5a13a7359b3d0e9f61e511063606ef5f6184f9d Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:47:22 -0600 Subject: [PATCH 23/26] Remove non-changes to monk module. --- engine/class_modules/monk/sc_monk.cpp | 4 +--- engine/class_modules/monk/sc_monk.hpp | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 316abd7353e..b96aa11dfda 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -41,7 +41,6 @@ Manatee #include "report/charts.hpp" #include "report/highchart.hpp" #include "sc_enums.hpp" -#include "sim/profileset_control.hpp" #include @@ -5550,7 +5549,6 @@ bool monk_t::validate_fight_style( fight_style_e style ) const return true; } -// monk_t::init_spells ====================================================== void monk_t::init_spells() { base_t::init_spells(); @@ -7452,7 +7450,7 @@ struct monk_module_t : public module_t return true; } - void register_actor_initializers( sim_t* ) const override + void register_actor_initializers( sim_t * ) const override { } diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index e30e3886c91..1f384599b5b 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -1158,7 +1158,6 @@ struct monk_t : public stagger_t bool validate_fight_style( fight_style_e style ) const override; // Init / Reset - void create_pets() override; void init_spells() override; void init_background_actions() override; void init_base_stats() override; From d63707f68a96fb5138a8f15d9c448d19cb843b61 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:52:31 -0600 Subject: [PATCH 24/26] revert some whitespace changes and incorrect rebase resolution --- engine/sim/sim.hpp | 332 +++++++++++++++++++++------------------------ 1 file changed, 156 insertions(+), 176 deletions(-) diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 56d47af0180..595446481b2 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -6,25 +6,24 @@ #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/option.hpp" #include "profileset_control.hpp" #include "sim_ostream.hpp" +#include "sim/option.hpp" #include "util/concurrency.hpp" #include "util/rng.hpp" #include "util/sample_data.hpp" #include "util/util.hpp" #include "util/vector_with_callback.hpp" +#include #include #include -#include #include -#include +#include struct actor_target_data_t; struct buff_t; @@ -33,7 +32,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; @@ -52,7 +51,7 @@ class report_configuration_t; } namespace profileset{ -class profilesets_t; + class profilesets_t; } struct sim_progress_t @@ -60,7 +59,7 @@ 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 @@ -89,7 +88,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 @@ -103,9 +102,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; @@ -113,55 +112,56 @@ 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> actor_list; + bool save_talent_str; + auto_dispose< std::vector > 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; @@ -180,15 +180,15 @@ struct sim_t : private sc_thread_t 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; @@ -230,13 +230,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; @@ -248,81 +248,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 @@ -336,33 +336,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 @@ -396,14 +396,6 @@ struct sim_t : private sc_thread_t timespan_t salvaged_fusion_amplifier_precast = 0_s; /// Fraction of the time that the player is above the health threshold for Titanic Ocular Gland. double titanic_ocular_gland_worthy_chance = 1.0; - /// Sets the chance that the player successfully faces their Doubt to get the Newfound Resolve buff. - double newfound_resolve_success_chance = 1.0; - /// 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; - /// 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 double shredded_soul_pickup_chance = 1.0; /// Type stat gained from So'leah's Secret Technique @@ -423,13 +415,6 @@ struct sim_t : private sc_thread_t bool disable_soul_igniter_second_use = true; /// Disables the execute effect of Inscrutable Quantum Device since it is avoidable in game bool disable_iqd_execute = false; - // Better Together Override - // Defaults active - 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; // Earthbreaker's Impact weak points triggered unsigned int earthbreakers_impact_weak_points = 3; // Grim Eclipse Dot Duration override @@ -532,7 +517,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; @@ -559,7 +544,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; @@ -602,16 +587,13 @@ struct sim_t : private sc_thread_t // sim control 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; @@ -631,7 +613,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 @@ -677,47 +659,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 ); bool is_initialized() { @@ -737,7 +719,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 @@ -746,7 +728,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)... ) ); } /** @@ -801,8 +783,6 @@ struct sim_t : private sc_thread_t { 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 ) - { target_data_initializer.push_back( cb ); } const rng::rng_t& rng() const { return _rng; } rng::rng_t& rng() @@ -829,9 +809,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... ) ); @@ -844,9 +824,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... ) ); From 61bb16afe7c7d531d232aefe1fc55a9a56559077 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:01:56 -0600 Subject: [PATCH 25/26] move include, clear whitespace change. --- engine/class_modules/monk/sc_monk.cpp | 2 +- engine/sim/profileset_control.cpp | 1 + engine/sim/sim.hpp | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index b96aa11dfda..dd9fb303b4f 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -7450,7 +7450,7 @@ struct monk_module_t : public module_t return true; } - void register_actor_initializers( sim_t * ) const override + void register_actor_initializers( sim_t* ) const override { } diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index ed1517ca525..56e771284f7 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -2,6 +2,7 @@ #include "dbc/dbc.hpp" #include "dbc/item_set_bonus.hpp" +#include "interfaces/sc_js.hpp" #include "player/set_bonus.hpp" #include "profileset.hpp" #include "sc_enums.hpp" diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index 595446481b2..b7e3ea4dfed 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -7,7 +7,6 @@ #include "config.hpp" #include "event_manager.hpp" -#include "interfaces/sc_js.hpp" #include "player/gear_stats.hpp" #include "progress_bar.hpp" #include "profileset_control.hpp" From df99267fdb6d0fbb10e59afa77449ae56439a525 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:02:26 -0600 Subject: [PATCH 26/26] remove profileset control from monk files --- engine/class_modules/monk/sc_monk.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 1f384599b5b..1a8419e8ca6 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -16,7 +16,6 @@ #include "sc_enums.hpp" #include "sc_stagger.hpp" #include "sim/proc.hpp" -#include "sim/profileset_control.hpp" #include "util/timeline.hpp" #include