diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index de00f88319..06afe9c498 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -26,7 +26,7 @@ namespace PokemonAutomation{ const bool IS_BETA_VERSION = true; const int PROGRAM_VERSION_MAJOR = 0; const int PROGRAM_VERSION_MINOR = 55; -const int PROGRAM_VERSION_PATCH = 1; +const int PROGRAM_VERSION_PATCH = 2; const std::string PROGRAM_VERSION_BASE = "v" + std::to_string(PROGRAM_VERSION_MAJOR) + diff --git a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.cpp b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.cpp index 79f5aaaba5..c00b906473 100644 --- a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.cpp +++ b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.cpp @@ -90,109 +90,208 @@ void VideoSession::set(const VideoSourceOption& option){ } void VideoSession::reset(){ - m_logger.log("Resetting the video...", COLOR_GREEN); - dispatch_to_main_thread([this]{ - std::lock_guard lg0(m_reset_lock); + { + WriteSpinLock lg(m_queue_lock); +// cout << "VideoSession::reset(): " << m_queued_commands.size() << endl; + m_queued_commands.emplace_back( + Command{ + CommandType::RESET, + nullptr, + Resolution{} + } + ); + } + dispatch_to_main_thread([this]{ run_commands(); }); +} +void VideoSession::set_source( + const std::shared_ptr& device, + Resolution resolution +){ + { + WriteSpinLock lg(m_queue_lock); +// cout << "VideoSession::set_source(): " << m_queued_commands.size() << endl; + m_queued_commands.emplace_back( + Command{ + CommandType::SET_SOURCE, + device, + resolution + } + ); + } + dispatch_to_main_thread([this]{ run_commands(); }); +} +void VideoSession::set_resolution(Resolution resolution){ + { + WriteSpinLock lg(m_queue_lock); +// cout << "VideoSession::set_resolution(): " << m_queued_commands.size() << endl; + m_queued_commands.emplace_back( + Command{ + CommandType::SET_RESOLUTION, + nullptr, + resolution + } + ); + } + dispatch_to_main_thread([this]{ run_commands(); }); +} - m_state_listeners.run_method_unique(&StateListener::pre_shutdown); +void VideoSession::internal_reset(){ + m_logger.log("Resetting the video...", COLOR_GREEN); + m_state_listeners.run_method_unique(&StateListener::pre_shutdown); - { - WriteSpinLock lg(m_state_lock); - if (m_video_source){ - m_video_source->remove_source_frame_listener(*this); - m_video_source->remove_rendered_frame_listener(*this); - m_video_source.reset(); - } + Resolution resolution = m_option.m_resolution; + std::unique_ptr source; + { + WriteSpinLock lg(m_state_lock); + source = std::move(m_video_source); + } + if (source){ + source->remove_source_frame_listener(*this); + source->remove_rendered_frame_listener(*this); + source.reset(); + } - m_video_source = m_descriptor->make_VideoSource(m_logger, m_option.m_resolution); + source = m_descriptor->make_VideoSource(m_logger, resolution); + if (source){ + resolution = source->current_resolution(); + source->add_source_frame_listener(*this); + source->add_rendered_frame_listener(*this); + } - if (m_video_source){ - m_option.m_resolution = m_video_source->current_resolution(); - m_video_source->add_source_frame_listener(*this); - m_video_source->add_rendered_frame_listener(*this); - } - } + { + WriteSpinLock lg(m_state_lock); + m_option.m_resolution = resolution; + m_video_source = std::move(source); + } - m_state_listeners.run_method_unique( - &StateListener::post_startup, - m_video_source.get() - ); - }); + m_state_listeners.run_method_unique( + &StateListener::post_startup, + m_video_source.get() + ); } -void VideoSession::set_source( +void VideoSession::internal_set_source( const std::shared_ptr& device, Resolution resolution ){ m_logger.log("Changing video...", COLOR_GREEN); - dispatch_to_main_thread([this, device, resolution]{ - std::lock_guard lg0(m_reset_lock); - if (*m_descriptor == *device && !m_descriptor->should_reload()){ - return; - } - - m_state_listeners.run_method_unique(&StateListener::pre_shutdown); - - { - WriteSpinLock lg(m_state_lock); - if (m_video_source){ - m_video_source->remove_source_frame_listener(*this); - m_video_source->remove_rendered_frame_listener(*this); - m_video_source.reset(); - } + if (*m_descriptor == *device && !m_descriptor->should_reload()){ + return; + } - m_option.set_descriptor(device); - m_descriptor = device; + m_state_listeners.run_method_unique(&StateListener::pre_shutdown); - Resolution desired_resolution = resolution ? resolution : m_option.m_resolution; -// cout << "VideoSession::set_source(): " << &m_option << " - " << desired_resolution.width << " x " << desired_resolution.height << endl; + std::unique_ptr source; + { + WriteSpinLock lg(m_state_lock); + source = std::move(m_video_source); + } + if (source){ + source->remove_source_frame_listener(*this); + source->remove_rendered_frame_listener(*this); + source.reset(); + } + { + WriteSpinLock lg(m_state_lock); + m_option.set_descriptor(device); + m_descriptor = device; + } - m_video_source = device->make_VideoSource(m_logger, desired_resolution); + Resolution desired_resolution = resolution ? resolution : m_option.m_resolution; + source = device->make_VideoSource(m_logger, desired_resolution); + if (source){ + resolution = source->current_resolution(); + source->add_source_frame_listener(*this); + source->add_rendered_frame_listener(*this); + } - if (m_video_source){ - m_option.m_resolution = m_video_source->current_resolution(); - m_video_source->add_source_frame_listener(*this); - m_video_source->add_rendered_frame_listener(*this); - } - } + { + WriteSpinLock lg(m_state_lock); + m_option.m_resolution = resolution; + m_video_source = std::move(source); + } - m_state_listeners.run_method_unique( - &StateListener::post_startup, - m_video_source.get() - ); - }); + m_state_listeners.run_method_unique( + &StateListener::post_startup, + m_video_source.get() + ); } -void VideoSession::set_resolution(Resolution resolution){ +void VideoSession::internal_set_resolution(Resolution resolution){ m_logger.log("Changing resolution...", COLOR_GREEN); - dispatch_to_main_thread([this, resolution]{ - std::lock_guard lg0(m_reset_lock); - if (m_option.m_resolution == resolution){ - return; - } + if (m_option.m_resolution == resolution){ + return; + } + + m_state_listeners.run_method_unique(&StateListener::pre_shutdown); + + std::unique_ptr source; + { + WriteSpinLock lg(m_state_lock); + source = std::move(m_video_source); + } + if (source){ + source->remove_source_frame_listener(*this); + source->remove_rendered_frame_listener(*this); + source.reset(); + } + + source = m_descriptor->make_VideoSource(m_logger, resolution); + if (source){ + resolution = source->current_resolution(); + source->add_source_frame_listener(*this); + source->add_rendered_frame_listener(*this); + } + + { + WriteSpinLock lg(m_state_lock); + m_option.m_resolution = resolution; + m_video_source = std::move(source); + } - { - WriteSpinLock lg(m_state_lock); - m_state_listeners.run_method_unique(&StateListener::pre_shutdown); + m_state_listeners.run_method_unique( + &StateListener::post_startup, + m_video_source.get() + ); +} - if (m_video_source){ - m_video_source->remove_source_frame_listener(*this); - m_video_source->remove_rendered_frame_listener(*this); - m_video_source.reset(); +void VideoSession::run_commands(){ + std::lock_guard lg0(m_reset_lock); + if (m_recursion_depth != 0){ + m_logger.log("Suppressing re-entrant command...", COLOR_RED); + return; + } + m_recursion_depth++; + try{ + while (true){ + Command command; + { + WriteSpinLock lg(m_queue_lock); +// cout << "VideoSession::run_commands(): " << m_queued_commands.size() << endl; + if (m_queued_commands.empty()){ + break; + } + command = std::move(m_queued_commands.front()); + m_queued_commands.pop_front(); } - m_video_source = m_descriptor->make_VideoSource(m_logger, resolution); + switch (command.command_type){ + case CommandType::RESET: + internal_reset(); + break; - if (m_video_source){ - m_option.m_resolution = m_video_source->current_resolution(); - m_video_source->add_source_frame_listener(*this); - m_video_source->add_rendered_frame_listener(*this); + case CommandType::SET_SOURCE: + internal_set_source(command.device, command.resolution); + break; + + case CommandType::SET_RESOLUTION: + internal_set_resolution(command.resolution); + break; } } - - m_state_listeners.run_method_unique( - &StateListener::post_startup, - m_video_source.get() - ); - }); + m_recursion_depth--; + }catch (...){ + m_recursion_depth--; + throw; + } } diff --git a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.h b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.h index 3886847050..2c58d1a1f9 100644 --- a/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.h +++ b/SerialPrograms/Source/CommonFramework/VideoPipeline/VideoSession.h @@ -8,6 +8,7 @@ #define PokemonAutomation_VideoPipeline_VideoSession_H #include +#include #include #include "Common/Cpp/EventRateTracker.h" #include "Common/Cpp/Concurrency/SpinLock.h" @@ -176,7 +177,28 @@ class VideoSession private: - mutable std::mutex m_reset_lock; + enum CommandType{ + RESET, + SET_SOURCE, + SET_RESOLUTION + }; + struct Command{ + CommandType command_type; + std::shared_ptr device; + Resolution resolution; + }; + void run_commands(); + + void internal_reset(); + void internal_set_source( + const std::shared_ptr& device, + Resolution resolution = {} + ); + void internal_set_resolution(Resolution resolution); + + +private: + mutable std::recursive_mutex m_reset_lock; mutable SpinLock m_state_lock; Logger& m_logger; @@ -189,6 +211,12 @@ class VideoSession std::shared_ptr m_descriptor; std::unique_ptr m_video_source; + // We need to queue up all reset commands and run them on the main thread. + // This is needed to prevent re-entrant calls from event processing. + SpinLock m_queue_lock; + size_t m_recursion_depth = 0; + std::deque m_queued_commands; + ListenerSet m_state_listeners; ListenerSet m_frame_listeners; }; diff --git a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp index 2bf87afa1e..e05f679cf6 100644 --- a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp +++ b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp @@ -139,6 +139,8 @@ #include "PokemonSwSh/MaxLair/Inference/PokemonSwSh_MaxLair_Detect_PathSide.h" #include "PokemonSwSh/MaxLair/Inference/PokemonSwSh_MaxLair_Detect_PathMap.h" #include "NintendoSwitch/Inference/NintendoSwitch_SelectedSettingDetector.h" +#include "PokemonSV/Inference/Boxes/PokemonSV_BoxShinyDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.h" #include #include @@ -260,10 +262,23 @@ void TestProgram::program(MultiSwitchProgramEnvironment& env, CancellableScope& auto screenshot = feed.snapshot(); - YCommIconDetector detector(true); + PokemonSwSh::BlackDialogBoxDetector detector(true); + detector.make_overlays(overlays); cout << detector.process_frame(screenshot, current_time()) << endl; + +// PokemonSV::BoxShinyDetector detector; +// cout << detector.detect(screenshot) << endl; + + + + +#if 0 + YCommIconDetector detector(true); + cout << detector.process_frame(screenshot, current_time()) << endl; +#endif + #if 0 bool switch2 = true; ImageFloatBox key1_box; diff --git a/SerialPrograms/Source/NintendoSwitch/NintendoSwitch_Settings.cpp b/SerialPrograms/Source/NintendoSwitch/NintendoSwitch_Settings.cpp index 2959565bdb..64db99a9d3 100644 --- a/SerialPrograms/Source/NintendoSwitch/NintendoSwitch_Settings.cpp +++ b/SerialPrograms/Source/NintendoSwitch/NintendoSwitch_Settings.cpp @@ -165,12 +165,6 @@ ConsoleSettingsPanel::ConsoleSettingsPanel(const ConsoleSettings_Descriptor& des , settings(ConsoleSettings::instance()) { PA_ADD_OPTION(settings); - - settings.CONTROLLER_SETTINGS.set_visibility( - settings.CONTROLLER_SETTINGS.current_rows() > 0 - ? ConfigOptionState::ENABLED - : ConfigOptionState::HIDDEN - ); } diff --git a/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_BoxShinyDetector.h b/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_BoxShinyDetector.h index 91520f1b1c..95b1c6ea1a 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_BoxShinyDetector.h +++ b/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_BoxShinyDetector.h @@ -11,7 +11,7 @@ #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" #include "CommonTools/VisualDetector.h" -#include "PokemonSV/Inference/Boxes/PokemonSV_BoxDetection.h" +//#include "PokemonSV/Inference/Boxes/PokemonSV_BoxDetection.h" namespace PokemonAutomation{ namespace NintendoSwitch{ diff --git a/SerialPrograms/Source/PokemonSV/Programs/Eggs/PokemonSV_EggRoutines.cpp b/SerialPrograms/Source/PokemonSV/Programs/Eggs/PokemonSV_EggRoutines.cpp index 6c9dd8d0db..248c78b469 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/Eggs/PokemonSV_EggRoutines.cpp +++ b/SerialPrograms/Source/PokemonSV/Programs/Eggs/PokemonSV_EggRoutines.cpp @@ -757,7 +757,11 @@ bool check_baby_info( gender_detector.make_overlays(overlay_set); BoxNatureDetector nature_detector(stream.overlay(), LANGUAGE); - const int shiny_ret = wait_until(stream, context, std::chrono::milliseconds(200), {shiny_detector}); + const int shiny_ret = wait_until( + stream, context, + std::chrono::milliseconds(500), + {shiny_detector} + ); const bool shiny = (shiny_ret == 0); VideoSnapshot screen = stream.video().snapshot(); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.cpp index 00ce853b5c..12cf72497c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.cpp @@ -50,8 +50,8 @@ void BlackDialogBoxDetector::make_overlays(VideoOverlaySet& items) const{ bool BlackDialogBoxDetector::process_frame(const ImageViewRGB32& frame, WallClock timestamp){ bool detected = true; - for(const auto& box : BLACK_BOXES){ - if (is_black(extract_box_reference(frame, box), 180, 35) == false){ + for (const auto& box : BLACK_BOXES){ + if (is_black(extract_box_reference(frame, box), 200, 35) == false){ detected = false; break; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp index 0563932ff8..81c6d00e7a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp @@ -69,7 +69,11 @@ void BerryFarmer::program(SingleSwitchProgramEnvironment& env, ProControllerCont env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(env.console, context, year); - pbf_mash_button(context, BUTTON_B, 90); + if (context->performance_class() == ControllerPerformanceClass::SysbotBase){ + pbf_wait(context, 90); + }else{ + pbf_mash_button(context, BUTTON_B, 90); + } pbf_press_button(context, BUTTON_A, 10, 10); pbf_mash_button(context, BUTTON_ZL, 385); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp index 844881a893..45aa245eb8 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp @@ -68,7 +68,11 @@ void DailyHighlightFarmer::program(SingleSwitchProgramEnvironment& env, ProContr for (uint32_t c = 0; c < SKIPS; c++){ env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(env.console, context, year); - pbf_mash_button(context, BUTTON_B, 90); + if (context->performance_class() == ControllerPerformanceClass::SysbotBase){ + pbf_wait(context, 90); + }else{ + pbf_mash_button(context, BUTTON_B, 90); + } pbf_press_button(context, BUTTON_A, 10, 110); pbf_press_button(context, BUTTON_ZL, 10, 40); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp index 99ab147be0..3780eb8ce5 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp @@ -68,7 +68,11 @@ void LotoFarmer::program(SingleSwitchProgramEnvironment& env, ProControllerConte for (uint32_t c = 0; c < SKIPS; c++){ env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(env.console, context, year); - pbf_mash_button(context, BUTTON_B, 90); + if (context->performance_class() == ControllerPerformanceClass::SysbotBase){ + pbf_wait(context, 90); + }else{ + pbf_mash_button(context, BUTTON_B, 90); + } pbf_press_button(context, BUTTON_A, 10, 90); pbf_press_button(context, BUTTON_B, 10, 70); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp index 4251e6c50c..94200d8a53 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp @@ -68,7 +68,11 @@ void StowOnSideFarmer::program(SingleSwitchProgramEnvironment& env, ProControlle for (uint32_t c = 0; c < SKIPS; c++){ env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(env.console, context, year); - pbf_mash_button(context, BUTTON_B, 90); + if (context->performance_class() == ControllerPerformanceClass::SysbotBase){ + pbf_wait(context, 90); + }else{ + pbf_mash_button(context, BUTTON_B, 90); + } ssf_press_button_ptv(context, BUTTON_A, 160ms); pbf_mash_button(context, BUTTON_ZL, 385); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp index 48ede4f866..b58b758f88 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp @@ -85,7 +85,11 @@ void WattFarmer::program(SingleSwitchProgramEnvironment& env, ProControllerConte env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(env.console, context, year); - pbf_mash_button(context, BUTTON_B, 90); + if (context->performance_class() == ControllerPerformanceClass::SysbotBase){ + pbf_wait(context, 90); + }else{ + pbf_mash_button(context, BUTTON_B, 90); + } ssf_press_button_ptv(context, BUTTON_A, 40ms); pbf_mash_button(context, BUTTON_B, EXIT_DEN_WAIT);