From 7d59e4f2babb8ab64b36afa8e31afc68bb6aa36e Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:59:01 -0600 Subject: [PATCH 01/11] [monk] Use a profileset controller to gracefully handle profilesets that include impermissible Monk hero talent configurations. --- engine/class_modules/monk/sc_monk.cpp | 121 +++++++++++++++++++++----- engine/class_modules/monk/sc_monk.hpp | 19 ++++ 2 files changed, 118 insertions(+), 22 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index e1cba148a07..37ac63e0f8c 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/profileset_control.hpp" #include @@ -5509,28 +5510,28 @@ bool monk_t::validate_actor() return false; } - int expected = 13; - for ( const auto &hero_tree : player_sub_trees ) - { - int count = as( range::count_if( - player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { - if ( std::get( entry ) != talent_tree::HERO ) - return false; - const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); - if ( !trait ) - return false; - return static_cast( trait->id_sub_tree ) == hero_tree; - } ) ); - - // Report without counting the hidden talent that activates the subtree - count -= 1; - if ( count < expected ) - { - sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, - expected ); - return false; - } - } + // int expected = 13; + // for ( const auto &hero_tree : player_sub_trees ) + // { + // int count = as( range::count_if( + // player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { + // if ( std::get( entry ) != talent_tree::HERO ) + // return false; + // const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); + // if ( !trait ) + // return false; + // return static_cast( trait->id_sub_tree ) == hero_tree; + // } ) ); + + // // Report without counting the hidden talent that activates the subtree + // count -= 1; + // if ( count < expected ) + // { + // sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, + // expected ); + // return false; + // } + // } switch ( specialization() ) { @@ -7018,6 +7019,12 @@ void monk_t::init_finished() { base_t::init_finished(); parse_player_effects(); + + profileset_controller_t::register_controller( + "valid_talents", profileset_controller::create_fn_pair() ); + std::vector rhs; + rhs.push_back( fmt::format( "player={},count=13", name() ) ); + sim->profileset_controller_options.emplace( "valid_talents", rhs ); } void monk_t::reset() @@ -7345,6 +7352,76 @@ void monk_t::create_actions() buff.aspect_of_harmony.construct_actions( this ); } +namespace profileset_control +{ +valid_talents_t::valid_talents_t( sim_t *sim, unsigned int id ) : profileset_controller_t( sim, id ) +{ +} + +const std::string valid_talents_t::name() const +{ + return "valid_talents"; +} + +bool valid_talents_t::evaluate_post_init() +{ + if ( !player ) + return true; + + switch ( player->specialization() ) + { + case MONK_BREWMASTER: + case MONK_WINDWALKER: + for ( const auto &hero_tree : player->player_sub_trees ) + { + auto count = as( range::count_if( + player->player_traits, + [ is_ptr = player->is_ptr(), hero_tree ]( std::tuple entry ) { + if ( std::get( entry ) != talent_tree::HERO ) + return false; + const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); + if ( !trait ) + return false; + return static_cast( trait->id_sub_tree ) == hero_tree; + } ) ); + // Report without counting the hidden talent that activates the subtree + count -= 1; + if ( count == this->count ) + return true; + return false; + } + default: + break; + } + + return true; +} + +const std::string valid_talents_t::reason() const +{ + return fmt::format( "player {} does not have {} talents selected in hero tree", player->name(), count ); +} + +void valid_talents_t::create_options() +{ + add_option( opt_func( "count", [ this ]( sim_t *, util::string_view, util::string_view value ) { + this->count = util::to_unsigned( value ); + 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->player = player; + return true; + } + } + return false; + } ) ); +} +} // namespace profileset_control + std::unique_ptr monk_t::create_expression( std::string_view name_str ) { auto splits = util::string_split( name_str, "." ); diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index c96afdd6741..982f1a4765e 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/profileset_control.hpp" #include "util/timeline.hpp" #include @@ -1216,6 +1217,24 @@ struct monk_t : public stagger_t void trigger_celestial_fortune( action_state_t * ); }; +namespace profileset_control +{ +struct valid_talents_t : profileset_controller_t +{ + using data_t = profileset_controller_data_t; + + player_t *player; + unsigned int count; + + valid_talents_t( sim_t *sim, unsigned int id ); + + const std::string name() const override; + bool evaluate_post_init() override; + const std::string reason() const override; + void create_options() override; +}; +} // namespace profileset_control + namespace events { // based on implementation from sc_demon_hunter.cpp From 7568c1bbe5187fc22959a84946fced11da662d59 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:18:33 -0600 Subject: [PATCH 02/11] [profileset_control] Fix a deadlock caused when `profileset_work_threads` is set to a value greater than 1 and cancellations occur at similar times. --- engine/sim/profileset.cpp | 4 ++-- engine/sim/profileset_control.cpp | 4 ++-- engine/sim/profileset_control.hpp | 2 +- engine/sim/sim.cpp | 15 +++++++++++---- engine/sim/sim.hpp | 5 +++-- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/engine/sim/profileset.cpp b/engine/sim/profileset.cpp index 4828b634131..11ee87109fa 100644 --- a/engine/sim/profileset.cpp +++ b/engine/sim/profileset.cpp @@ -403,7 +403,7 @@ void worker_t::execute() { try { - m_sim = new sim_t( m_parent, 0, m_profileset->options() ); + m_sim = new sim_t( m_parent, 0, m_profileset->options(), m_profileset->name() ); simulate_profileset( m_parent, *m_profileset, m_sim ); } @@ -501,7 +501,7 @@ void profilesets_t::generate_work( sim_t* parent, std::unique_ptr try { - sim_t* profile_sim = new sim_t( parent ); + sim_t* profile_sim = new sim_t( parent, 0, ptr_set->name() ); parent->control = original_opts; simulate_profileset( parent, *ptr_set, profile_sim ); diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index 1a4df649088..fbe2d3e5884 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -140,7 +140,7 @@ void profileset_controller_t::evaluate( sim_t* sim, call_point_e call_point ) assert( controller->parent == sim->parent ); controller->set_exit_reason( - { sim->parent->profilesets->current_profileset_name(), call_point, controller->reason() } ); + { sim->profileset_name, call_point, controller->reason() } ); sim->canceled = true; sim->error( error_level_e::TRIVIAL, "{}", controller->message( call_point ) ); @@ -161,7 +161,7 @@ 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(), + sim->profileset_name, name(), profileset_controller::call_point_string( call_point ) ); if ( call_point == POST_ITER ) msg += std::to_string( sim->current_iteration ); diff --git a/engine/sim/profileset_control.hpp b/engine/sim/profileset_control.hpp index 02b97c0dece..4105dc0771f 100644 --- a/engine/sim/profileset_control.hpp +++ b/engine/sim/profileset_control.hpp @@ -34,7 +34,7 @@ struct data_wrapper_t struct exit_reason_t { - const std::string profileset_name; + const std::string_view profileset_name; const call_point_e exit_point; const std::string exit_reason; }; diff --git a/engine/sim/sim.cpp b/engine/sim/sim.cpp index 4e38c8612cf..a0b9f48770f 100644 --- a/engine/sim/sim.cpp +++ b/engine/sim/sim.cpp @@ -1579,7 +1579,8 @@ sim_t::sim_t() profileset_enabled( false ), profileset_work_threads( 0 ), profileset_init_threads( 1 ), - profilesets( std::make_unique() ) + profilesets( std::make_unique() ), + profileset_name() { item_db_sources.assign( std::begin( default_item_db_sources ), std::end( default_item_db_sources ) ); @@ -1592,10 +1593,13 @@ sim_t::sim_t() profileset::create_options( this ); } -sim_t::sim_t( sim_t* p, int index ) : sim_t() +sim_t::sim_t( sim_t* p, int index, std::string_view profileset_name ) + : sim_t() { assert( p ); + this->profileset_name = profileset_name; + parent = p; thread_index = index; @@ -1619,10 +1623,13 @@ sim_t::sim_t( sim_t* p, int index ) : sim_t() parent -> add_relative( this ); } -sim_t::sim_t( sim_t* p, int index, sim_control_t* control ) : sim_t() +sim_t::sim_t( sim_t* p, int index, sim_control_t* control, std::string_view profileset_name ) + : sim_t() { assert( p && control ); + this->profileset_name = profileset_name; + parent = p; thread_index = index; @@ -3387,7 +3394,7 @@ void sim_t::partition() for ( int i = 0; i < num_children; i++ ) { - auto child = new sim_t( this, i + 1, child_control ); + auto child = new sim_t( this, i + 1, child_control, child_control->combat.name ); assert( child ); children.push_back( child ); diff --git a/engine/sim/sim.hpp b/engine/sim/sim.hpp index b7e3ea4dfed..3510453227d 100644 --- a/engine/sim/sim.hpp +++ b/engine/sim/sim.hpp @@ -651,10 +651,11 @@ struct sim_t : private sc_thread_t bool profileset_enabled; int profileset_work_threads, profileset_init_threads; std::unique_ptr profilesets; + std::string_view profileset_name; sim_t(); - explicit sim_t( sim_t* parent, int thread_index = 0 ); - sim_t( sim_t* parent, int thread_index, sim_control_t* control ); + explicit sim_t( sim_t* parent, int thread_index = 0, std::string_view profileset_name = {} ); + sim_t( sim_t* parent, int thread_index, sim_control_t* control, std::string_view profileset_name = {} ); ~sim_t() override; void run() override; From 5ba349acdeb91bdba6d8bf52b5aba2b7587df3b1 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:33:59 -0600 Subject: [PATCH 03/11] [monk] Allow `valid_talents` to permit sims using `enable_all_talents` option. --- engine/class_modules/monk/sc_monk.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 37ac63e0f8c..d461162e614 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -7365,7 +7365,7 @@ const std::string valid_talents_t::name() const bool valid_talents_t::evaluate_post_init() { - if ( !player ) + if ( !player || sim->enable_all_talents ) return true; switch ( player->specialization() ) @@ -7388,8 +7388,8 @@ bool valid_talents_t::evaluate_post_init() count -= 1; if ( count == this->count ) return true; - return false; } + return false; default: break; } From d3e8278cab9efb54fab01b7c6ba8a2c00f5b1654 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 29 Jun 2026 22:03:33 -0600 Subject: [PATCH 04/11] [monk] Allow talent verification to occur for non-profileset sims. --- engine/class_modules/monk/sc_monk.cpp | 50 ++++++++++++++------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index d461162e614..ed3d9755457 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5510,28 +5510,31 @@ bool monk_t::validate_actor() return false; } - // int expected = 13; - // for ( const auto &hero_tree : player_sub_trees ) - // { - // int count = as( range::count_if( - // player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { - // if ( std::get( entry ) != talent_tree::HERO ) - // return false; - // const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); - // if ( !trait ) - // return false; - // return static_cast( trait->id_sub_tree ) == hero_tree; - // } ) ); - - // // Report without counting the hidden talent that activates the subtree - // count -= 1; - // if ( count < expected ) - // { - // sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, - // expected ); - // return false; - // } - // } + if ( !sim->parent && !sim->profileset_enabled ) + { + int expected = 13; + for ( const auto &hero_tree : player_sub_trees ) + { + int count = as( range::count_if( + player_traits, [ is_ptr = is_ptr(), hero_tree ]( std::tuple entry ) { + if ( std::get( entry ) != talent_tree::HERO ) + return false; + const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); + if ( !trait ) + return false; + return static_cast( trait->id_sub_tree ) == hero_tree; + } ) ); + + // Report without counting the hidden talent that activates the subtree + count -= 1; + if ( count < expected ) + { + sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, + expected ); + return false; + } + } + } switch ( specialization() ) { @@ -7022,8 +7025,7 @@ void monk_t::init_finished() profileset_controller_t::register_controller( "valid_talents", profileset_controller::create_fn_pair() ); - std::vector rhs; - rhs.push_back( fmt::format( "player={},count=13", name() ) ); + std::vector rhs = { fmt::format( "player={},count=13", name() ) }; sim->profileset_controller_options.emplace( "valid_talents", rhs ); } From 4195ff9f413a329edd82699fdd03eaa6e2e6b21b Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:10:31 -0600 Subject: [PATCH 05/11] [monk] Initialize ptr. --- engine/class_modules/monk/sc_monk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index ed3d9755457..8eeba4b43eb 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -2224,7 +2224,7 @@ struct auto_attack_t : public monk_melee_attack_t action_t *damage; template - thunderfist_t( monk_t *player, Args &&...args ) : TBase( player, std::forward( args )... ) + thunderfist_t( monk_t *player, Args &&...args ) : TBase( player, std::forward( args )... ), damage( nullptr ) { if ( !player->talent.windwalker.thunderfist->ok() ) return; From b6a7de5ccc105611f44d47503556b6283c33c53c Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:47:01 -0600 Subject: [PATCH 06/11] maybe --- engine/sim/profileset_control.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index fbe2d3e5884..6b4732cab4e 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -105,7 +105,10 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) 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; + if ( factory.find( key ) == factory.end() ) + return false; + factory.emplace( key, std::move( value ) ); + return true; } bool profileset_controller_t::controller_exists( std::string key ) From a8a5a43ae69dee13ec496eb3f5789783a1f7ee9c Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:07:06 -0600 Subject: [PATCH 07/11] maybe --- engine/sim/profileset_control.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index 6b4732cab4e..c0967c5170d 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -105,10 +105,8 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) bool profileset_controller_t::register_controller( std::string key, profileset_controller_t::factory_fn_pair_t&& value ) { - if ( factory.find( key ) == factory.end() ) - return false; - factory.emplace( key, std::move( value ) ); - return true; + const auto val = factory.try_emplace( key, std::move( value ) ); + return val.second; } bool profileset_controller_t::controller_exists( std::string key ) From e0afb41bca74b25cd8cef911aa9c83773671e91e Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:48:11 -0600 Subject: [PATCH 08/11] For some reason, `try_emplace` causes a memory leak I cannot locally replicate. --- engine/sim/profileset_control.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index c0967c5170d..6b4732cab4e 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -105,8 +105,10 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) bool profileset_controller_t::register_controller( std::string key, profileset_controller_t::factory_fn_pair_t&& value ) { - const auto val = factory.try_emplace( key, std::move( value ) ); - return val.second; + if ( factory.find( key ) == factory.end() ) + return false; + factory.emplace( key, std::move( value ) ); + return true; } bool profileset_controller_t::controller_exists( std::string key ) From 37246cc7029394672ae2a12210d9043134da83f1 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:50:20 -0600 Subject: [PATCH 09/11] [mok] Correct implmentation error that caused an early return preventing checking of all active subtrees. --- engine/class_modules/monk/sc_monk.cpp | 42 +++++++++++++++------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 8eeba4b43eb..2f76880ae5c 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -5527,7 +5527,7 @@ bool monk_t::validate_actor() // Report without counting the hidden talent that activates the subtree count -= 1; - if ( count < expected ) + if ( count < expected && count != 0 ) { sim->error( SEVERE, "Invalid Hero Talent tree, possibly low level. Found {} talents, expected {}.", count, expected ); @@ -7365,6 +7365,27 @@ const std::string valid_talents_t::name() const return "valid_talents"; } +std::function )> matching_talent( player_t *player, + unsigned hero_tree ) +{ + return [ = ]( std::tuple entry ) { + if ( std::get( entry ) != talent_tree::HERO ) + return false; + const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), player->is_ptr() ); + if ( !trait ) + return false; + return static_cast( trait->id_sub_tree ) == hero_tree; + }; +} + +std::function has_expected_count( player_t *player, unsigned expected_count ) +{ + return [ = ]( unsigned hero_tree ) { + unsigned count = range::count_if( player->player_traits, matching_talent( player, hero_tree ) ); + return count > expected_count; + }; +} + bool valid_talents_t::evaluate_post_init() { if ( !player || sim->enable_all_talents ) @@ -7374,24 +7395,7 @@ bool valid_talents_t::evaluate_post_init() { case MONK_BREWMASTER: case MONK_WINDWALKER: - for ( const auto &hero_tree : player->player_sub_trees ) - { - auto count = as( range::count_if( - player->player_traits, - [ is_ptr = player->is_ptr(), hero_tree ]( std::tuple entry ) { - if ( std::get( entry ) != talent_tree::HERO ) - return false; - const trait_data_t *trait = trait_data_t::find( std::get<1>( entry ), is_ptr ); - if ( !trait ) - return false; - return static_cast( trait->id_sub_tree ) == hero_tree; - } ) ); - // Report without counting the hidden talent that activates the subtree - count -= 1; - if ( count == this->count ) - return true; - } - return false; + return range::all_of( player->player_sub_trees, has_expected_count( player, count ) ); default: break; } From bd8c34dfa604c19be31452f4b556fe7c6db1fc80 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:24:22 -0600 Subject: [PATCH 10/11] [profileset_controller] Incorrect comparator to skip registering controller factory. --- engine/sim/profileset_control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index 6b4732cab4e..987849c12ee 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -105,7 +105,7 @@ void profileset_controller_data_wrapper_t::construct_controller( sim_t* sim ) bool profileset_controller_t::register_controller( std::string key, profileset_controller_t::factory_fn_pair_t&& value ) { - if ( factory.find( key ) == factory.end() ) + if ( factory.find( key ) != factory.end() ) return false; factory.emplace( key, std::move( value ) ); return true; From 119ec6e2388f45af5d8164d1982ef689a6a8c38d Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Fri, 3 Jul 2026 21:48:23 -0600 Subject: [PATCH 11/11] maybe --- engine/sim/profileset_control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/sim/profileset_control.cpp b/engine/sim/profileset_control.cpp index 987849c12ee..0f83dfcfcd0 100644 --- a/engine/sim/profileset_control.cpp +++ b/engine/sim/profileset_control.cpp @@ -107,7 +107,7 @@ bool profileset_controller_t::register_controller( std::string key, profileset_c { if ( factory.find( key ) != factory.end() ) return false; - factory.emplace( key, std::move( value ) ); + factory.emplace( key, value ); return true; }