diff --git a/SerialPrograms/Source/CommonFramework/Panels/PanelSession.cpp b/SerialPrograms/Source/CommonFramework/Panels/PanelSession.cpp index 9121269916..1d5367a1fb 100644 --- a/SerialPrograms/Source/CommonFramework/Panels/PanelSession.cpp +++ b/SerialPrograms/Source/CommonFramework/Panels/PanelSession.cpp @@ -29,7 +29,7 @@ PanelSession::PanelSession(const PanelDescriptor& descriptor) } } -void PanelSession::load_json(){ +void PanelSession::load_json_from_global(){ JsonValue* node = PERSISTENT_SETTINGS().panels.get_value(m_descriptor.identifier()); if (node == nullptr){ return; diff --git a/SerialPrograms/Source/CommonFramework/Panels/PanelSession.h b/SerialPrograms/Source/CommonFramework/Panels/PanelSession.h index 9ece35b32c..665ee9ecc4 100644 --- a/SerialPrograms/Source/CommonFramework/Panels/PanelSession.h +++ b/SerialPrograms/Source/CommonFramework/Panels/PanelSession.h @@ -33,10 +33,9 @@ class PanelSession : public UiState{ public: virtual void restore_defaults(){} - - void load_json(); virtual JsonValue to_json() const; virtual void load_json(const JsonValue& json); + void load_json_from_global(); protected: diff --git a/SerialPrograms/Source/CommonFramework/ProgramSession.cpp b/SerialPrograms/Source/CommonFramework/ProgramSession.cpp index 256273edcb..c2e367fda3 100644 --- a/SerialPrograms/Source/CommonFramework/ProgramSession.cpp +++ b/SerialPrograms/Source/CommonFramework/ProgramSession.cpp @@ -37,7 +37,7 @@ ProgramSession::ProgramSession(const ProgramDescriptor& descriptor) , m_instance_id(ProgramTracker::instance().add_program(*this)) // , m_logger(global_logger_raw(), "Program:" + std::to_string(m_instance_id)) , m_logger(global_logger_raw(), "Program") - , m_timestamp(current_time()) + , m_last_state_change(current_time()) , m_state(ProgramState::STOPPED) { load_historical_stats(); @@ -71,8 +71,8 @@ std::string ProgramSession::historical_stats() const{ } return ""; } -WallClock ProgramSession::timestamp() const{ - return m_timestamp.load(std::memory_order_relaxed); +WallClock ProgramSession::last_state_change() const{ + return m_last_state_change.load(std::memory_order_relaxed); } @@ -190,7 +190,7 @@ std::string ProgramSession::start_program(){ // Now start the program. m_logger.log("Starting program..."); - m_timestamp.store(current_time(), std::memory_order_relaxed); + m_last_state_change.store(current_time(), std::memory_order_relaxed); set_state(ProgramState::RUNNING); m_program_thread = GlobalThreadPools::unlimited_realtime().dispatch_now_blocking( [this]{ @@ -254,6 +254,7 @@ std::string ProgramSession::stop_program(){ } } internal_stop_program(); + m_last_state_change.store(current_time(), std::memory_order_relaxed); return ""; } diff --git a/SerialPrograms/Source/CommonFramework/ProgramSession.h b/SerialPrograms/Source/CommonFramework/ProgramSession.h index a493243918..f3e6299c1b 100644 --- a/SerialPrograms/Source/CommonFramework/ProgramSession.h +++ b/SerialPrograms/Source/CommonFramework/ProgramSession.h @@ -89,7 +89,7 @@ class ProgramSession : public TrackableProgram{ virtual ProgramState current_state() const override final{ return m_state.load(std::memory_order_relaxed); } virtual std::string current_stats() const override final; std::string historical_stats() const; - virtual WallClock timestamp() const final; + virtual WallClock last_state_change() const final; // Temporary for migration. StatsTracker* current_stats_tracker(){ return m_current_stats.get(); } @@ -155,7 +155,7 @@ class ProgramSession : public TrackableProgram{ mutable Mutex m_lock; - std::atomic m_timestamp; + std::atomic m_last_state_change; std::atomic m_state; // ProgramMissingResourceTracker m_missing_resource_tracker; diff --git a/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.cpp b/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.cpp index ca89469cf2..6f88edf368 100644 --- a/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.cpp +++ b/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.cpp @@ -112,7 +112,7 @@ void ComputerProgramSession::internal_run_program(){ identifier(), m_descriptor.category(), m_descriptor.display_name(), - timestamp() + last_state_change() ); CancellableHolder scope; ProgramEnvironment env( diff --git a/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.h b/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.h index c567361276..4eb4ca5bac 100644 --- a/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.h +++ b/SerialPrograms/Source/ComputerPrograms/Framework/ComputerProgramSession.h @@ -42,14 +42,13 @@ class ComputerProgramSession final public: + virtual std::string check_validity() const override; virtual void restore_defaults() override; virtual JsonValue to_json() const override; virtual void load_json(const JsonValue& json) override; private: - virtual std::string check_validity() const override; - virtual void internal_run_program() override; virtual void internal_stop_program() override; diff --git a/SerialPrograms/Source/Controllers/Controller.h b/SerialPrograms/Source/Controllers/Controller.h index 22b956911e..f12bf9115c 100644 --- a/SerialPrograms/Source/Controllers/Controller.h +++ b/SerialPrograms/Source/Controllers/Controller.h @@ -58,7 +58,7 @@ class AbstractController{ // Static Information virtual const char* name() = 0; - virtual ControllerClass controller_class() const = 0; + virtual ControllerClass controller_class() const noexcept = 0; // Performance Metrics virtual ControllerPerformanceClass performance_class() const = 0; diff --git a/SerialPrograms/Source/Controllers/ControllerConnection.h b/SerialPrograms/Source/Controllers/ControllerConnection.h index 81818d72ea..765ad542eb 100644 --- a/SerialPrograms/Source/Controllers/ControllerConnection.h +++ b/SerialPrograms/Source/Controllers/ControllerConnection.h @@ -55,15 +55,15 @@ class ControllerConnection : public CancellableScope{ public: - ControllerType current_controller() const{ + ControllerType current_controller() const noexcept{ return m_current_controller.load(std::memory_order_acquire); } - bool is_ready() const{ return status() == Status::READY; } - Status status() const{ return m_status.load(std::memory_order_acquire); } + bool is_ready() const noexcept{ return status() == Status::READY; } + Status status() const noexcept{ return m_status.load(std::memory_order_acquire); } std::string status_text() const; // It it not safe to call this until "is_ready()" is true. - const std::vector& controller_list(){ + const std::vector& controller_list() noexcept{ return m_controller_list; } diff --git a/SerialPrograms/Source/Controllers/ControllerSession.cpp b/SerialPrograms/Source/Controllers/ControllerSession.cpp index ba663335d3..68715905c6 100644 --- a/SerialPrograms/Source/Controllers/ControllerSession.cpp +++ b/SerialPrograms/Source/Controllers/ControllerSession.cpp @@ -118,7 +118,14 @@ std::shared_ptr ControllerSession::descriptor() const{ ReadSpinLock lg(m_state_lock); return m_descriptor; } -ControllerType ControllerSession::controller_type() const{ +ControllerClass ControllerSession::controller_class() const noexcept{ + ReadSpinLock lg(m_state_lock); + if (!m_controller){ + return ControllerClass::None; + } + return m_controller->controller_class(); +} +ControllerType ControllerSession::controller_type() const noexcept{ ReadSpinLock lg(m_state_lock); if (!m_connection){ return ControllerType::None; diff --git a/SerialPrograms/Source/Controllers/ControllerSession.h b/SerialPrograms/Source/Controllers/ControllerSession.h index 3e0532b9fa..9d80440c73 100644 --- a/SerialPrograms/Source/Controllers/ControllerSession.h +++ b/SerialPrograms/Source/Controllers/ControllerSession.h @@ -67,7 +67,8 @@ class ControllerSession final ControllerConnection::Status connection_status() const; std::shared_ptr descriptor() const; - ControllerType controller_type() const; + ControllerClass controller_class() const noexcept; + ControllerType controller_type() const noexcept; std::string status_text() const; std::optional index() const{ diff --git a/SerialPrograms/Source/Controllers/NullController.h b/SerialPrograms/Source/Controllers/NullController.h index e802b328d0..0c4276bd9c 100644 --- a/SerialPrograms/Source/Controllers/NullController.h +++ b/SerialPrograms/Source/Controllers/NullController.h @@ -58,7 +58,7 @@ class NullController final : public AbstractController{ virtual const char* name() override{ return NAME; } - virtual ControllerClass controller_class() const override{ + virtual ControllerClass controller_class() const noexcept override{ return ControllerClass::None; } virtual ControllerPerformanceClass performance_class() const override{ diff --git a/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_PABotBase2.h b/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_PABotBase2.h index ed2623dd77..b95628a193 100644 --- a/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_PABotBase2.h +++ b/SerialPrograms/Source/Controllers/StandardHid/StandardHid_Keyboard_PABotBase2.h @@ -48,7 +48,7 @@ class PABotBase2_Keyboard final : // virtual ControllerType controller_type() const override{ // return ControllerType::HID_Keyboard; // } - virtual ControllerClass controller_class() const override{ + virtual ControllerClass controller_class() const noexcept override{ return ControllerClass::HID_Keyboard; } diff --git a/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.cpp b/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.cpp index 00a344c9af..2dfe7b1ae1 100644 --- a/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.cpp +++ b/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.cpp @@ -105,6 +105,7 @@ std::string ConsoleSystemSession::status() const{ return m_status_text; } + JsonValue ConsoleSystemSession::to_json() const{ std::lock_guard lg(m_lock); return m_option.to_json(); diff --git a/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.h b/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.h index 2de1cf43c4..316ad30393 100644 --- a/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.h +++ b/SerialPrograms/Source/GameConsole/Framework/ConsoleSystemSession.h @@ -86,14 +86,12 @@ class ConsoleSystemSession VideoOverlaySession& overlay(){ return m_overlay; } const StreamHistorySession& stream_history() const{ return m_history; } - size_t controllers() const{ return m_controllers.size(); } - ControllerSession& controller(size_t index){ return m_controllers[index].session; } - public: - virtual VideoFeed& video_feed() override{ return video(); } - virtual AudioFeed& audio_feed() override{ return audio(); } - virtual ControllerSession& controller() override{ return ConsoleSystemSession::controller(0); }; + virtual VideoFeed& video_feed() noexcept override{ return video(); } + virtual AudioFeed& audio_feed() noexcept override{ return audio(); } + virtual size_t controllers() const noexcept override{ return m_controllers.size(); } + virtual ControllerSession& controller(size_t index) noexcept override{ return m_controllers[index].session; } virtual JsonValue to_json() const; virtual void load_json(const JsonValue& json); diff --git a/SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.cpp b/SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.cpp index 1e6f401a4b..b4a16b1028 100644 --- a/SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.cpp +++ b/SerialPrograms/Source/Integrations/DiscordSocial/DiscordSocial.cpp @@ -16,6 +16,10 @@ #include "Integrations/ProgramTracker.h" #include "DiscordSocial.h" +//#include +//using std::cout; +//using std::endl; + using namespace discordpp; namespace PokemonAutomation{ namespace Integration{ @@ -86,6 +90,7 @@ void DiscordSocial::thread_loop(){ } void DiscordSocial::update_rich_presence(){ +// cout << "DiscordSocial::update_rich_presence()" << endl; try{ std::string details = m_activity.Details().value(); std::string state = m_activity.State().value(); @@ -120,7 +125,11 @@ void DiscordSocial::update_rich_presence(){ details = item.second.program_name; } - m_timestamps.SetStart(std::chrono::duration_cast(item.second.start_time.time_since_epoch()).count()); + m_timestamps.SetStart( + std::chrono::duration_cast( + item.second.last_state_change.time_since_epoch() + ).count() + ); } m_activity.SetTimestamps(m_timestamps); diff --git a/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.cpp b/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.cpp index f410e6acc7..0012a1c6b5 100644 --- a/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.cpp +++ b/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.cpp @@ -193,18 +193,46 @@ bool Handler::check_if_empty(const DiscordSettingsOption& settings){ return true; } -void Handler::create_unified_commands(commandhandler& handler){ - handler - .add_command( +uint8_t Handler::get_min_parameters(const dpp::parameter_registration_t& params){ + uint8_t c = 0; + for (const auto& item : params){ + if (item.second.optional){ + break; + } + c++; + } + return c; +} + +void Handler::add_command_hi(dpp::commandhandler& handler){ + handler.add_command( + "hi", + {}, + [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ + log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); + message message; + message.set_content((std::string)GlobalSettings::instance().DISCORD->integration.hello_message); + if (src.message_event.has_value()){ + message.set_reference(src.message_event.value().msg.id); + } + handler.reply(message, src); + }, + "Hi!" + ); +} +void Handler::add_command_ping(dpp::commandhandler& handler){ + handler.add_command( "ping", {}, [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); handler.reply(message("Pong! :ping_pong:"), src); }, - "Ping pong!") - - .add_command( + "Ping pong!" + ); +} +void Handler::add_command_about(dpp::commandhandler& handler){ + handler.add_command( "about", {}, [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ @@ -232,28 +260,112 @@ void Handler::create_unified_commands(commandhandler& handler){ message.add_embed(embed); handler.reply(message, src); }, - "Some info about me!") - - .add_command( - "hi", + "Some info about me!" + ); +} +void Handler::add_command_help(dpp::commandhandler& handler){ + handler .add_command( + "help", {}, [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); message message; - message.set_content((std::string)GlobalSettings::instance().DISCORD->integration.hello_message); - if (src.message_event.has_value()){ - message.set_reference(src.message_event.value().msg.id); + embed embed; + embed.set_color((uint32_t)color).set_title("Command List"); + + static const std::unordered_set base_commands{ + "hi", "ping", "about", "status", "help" + }; + static const std::unordered_set button_commands{ + "click", "joystick" + }; + + std::unordered_set allowed = base_commands; + if (GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users){ + allowed.insert(button_commands.begin(), button_commands.end()); + } + + auto& commands = handler.commands; + for (auto& cmd : commands){ + const std::string& cmd_name = cmd.first; + if (src.issuer.id != owner.id && allowed.find(cmd_name) == allowed.end()){ + continue; + } + + auto& params = cmd.second.parameters; + std::string signature; + if (!params.empty()){ + signature = cmd_name + "("; + for (size_t i = 0; i < params.size(); ++i){ + signature += params[i].first; + if (i + 1 < params.size()){ + signature += ", "; + } + } + signature += ")"; + }else{ + signature = cmd_name; + } + + std::string param_details; + for (auto& param : params){ + param_details += ("\n-" + param.first + ": " + param.second.description); + if (!param.second.choices.empty()){ + std::string choices; + for (auto& c : param.second.choices){ + choices += c.second + ", "; + } + if (!choices.empty()){ + choices = choices.substr(0, choices.size() - 2); + } + param_details += " (" + choices + ")"; + } + } + embed.add_field(signature, param_details); } + embed_footer footer; + footer.set_text("Commands are case-sensitive!"); + embed.set_footer(footer); + message.add_embed(embed); handler.reply(message, src); }, - "Hi!") - - .add_command( - "resetserial", - { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")} + "View the command list." + ); +} +void Handler::add_command_status(dpp::commandhandler& handler){ + handler.add_command( + "status", + {}, + [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ + log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); + message message; + embed embed; + embed.set_color((uint32_t)color).set_description(Integration::status()).set_title("Program Status"); + message.add_embed(embed); + handler.reply(message, src); }, - [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ + "View program status." + ); +} +void Handler::add_command_screenshot(dpp::commandhandler& handler, bool full_version){ + parameter_registration_t parameters; + if (full_version){ + parameters.insert(parameters.end(), {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}); + } + parameters.insert(parameters.end(), { + {"format", param_info(pt_string, false, "Image format.", + { + {"0", "png"}, + {"1", "jpg"}, + } + )}, + }); + uint8_t min_parameters = get_min_parameters(parameters); + + handler.add_command( + full_version ? "screenshotX" : "screenshot", + parameters, + [=, &handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); if (!GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users && src.issuer.id != owner.id){ handler.reply(message("You do not have permission to use this command."), src); @@ -262,58 +374,55 @@ void Handler::create_unified_commands(commandhandler& handler){ message message; embed embed; - embed.set_color((uint32_t)color).set_title("Command Response"); + embed.set_color((uint32_t)color).set_title("Program Screenshot"); - int64_t id = Utility::sanitize_integer_input(params, 0); - std::string response = Integration::reset_serial(id); - if (!response.empty()){ - embed.set_description(response); + if (params.size() < min_parameters){ + embed.set_description("Missing command arguments."); message.add_embed(embed); handler.reply(message, src); return; } - embed.set_description("Reset the serial connection for console ID " + std::to_string(id) + "."); - message.add_embed(embed); - handler.reply(message, src); - }, - "Reset the serial connection.") + handler.thinking(src); + std::string name = "None"; - .add_command( - "resetcamera", - { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")} - }, - [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ - log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); - if (!GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users && src.issuer.id != owner.id){ - handler.reply(message("You do not have permission to use this command."), src); - return; + uint8_t c = 0; + int64_t id = -1; + if (full_version){ + id = Utility::sanitize_integer_input(params, c++); } + std::string button_input = std::get(params[c++].second); + int64_t format = Utility::get_value_from_input(handler, command, min_parameters - 1, button_input, name); - message message; - embed embed; - embed.set_color((uint32_t)color).set_title("Command Response"); + std::string path; + if (format == 0){ + path = "screenshot_slash.png"; + }else{ + path = "screenshot_slash.jpg"; + } - int64_t id = Utility::sanitize_integer_input(params, 0); - std::string response = Integration::reset_camera(id); + std::string response = Integration::screenshot(id, path.c_str()); if (!response.empty()){ embed.set_description(response); - message.add_embed(embed); - handler.reply(message, src); + Handler::update_response(src, embed, "", nullptr); return; } - embed.set_description("Reset the camera for console ID " + std::to_string(id) + "."); - message.add_embed(embed); - handler.reply(message, src); - }, - "Reset the camera.") + std::shared_ptr file(new PendingFileSend(path, true)); + embed_footer footer; + footer.set_text("Console ID: " + std::to_string(id) + " (" + name + ")"); - .add_command( + embed.set_footer(footer); + Handler::update_response(src, embed, "", std::move(file)); + }, + "Take and upload a screenshot from the specified console." + ); +} +void Handler::add_command_start(dpp::commandhandler& handler){ + handler.add_command( "start", { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")} + {"id", param_info(pt_integer, true, "Console ID. Find yours by using the \"status\" command.")} }, [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); @@ -326,7 +435,7 @@ void Handler::create_unified_commands(commandhandler& handler){ embed embed; embed.set_color((uint32_t)color).set_title("Command Response"); - int64_t id = Utility::sanitize_integer_input(params, 0); + int64_t id = Utility::sanitize_optional_integer_input(params, 0).value_or(-1); std::string response = Integration::start_program(id); if (!response.empty()){ embed.set_description(response); @@ -339,12 +448,14 @@ void Handler::create_unified_commands(commandhandler& handler){ message.add_embed(embed); handler.reply(message, src); }, - "Start the currently selected program.") - - .add_command( + "Start the currently selected program." + ); +} +void Handler::add_command_stop(dpp::commandhandler& handler){ + handler.add_command( "stop", { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")} + {"id", param_info(pt_integer, true, "Console ID. Find yours by using the \"status\" command.")} }, [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); @@ -357,7 +468,7 @@ void Handler::create_unified_commands(commandhandler& handler){ embed embed; embed.set_color((uint32_t)color).set_title("Command Response"); - int64_t id = Utility::sanitize_integer_input(params, 0); + int64_t id = Utility::sanitize_optional_integer_input(params, 0).value_or(-1); std::string response = Integration::stop_program(id); if (!response.empty()){ embed.set_description(response); @@ -370,46 +481,14 @@ void Handler::create_unified_commands(commandhandler& handler){ message.add_embed(embed); handler.reply(message, src); }, - "Stop the currently running program.") - - .add_command( - "status", - {}, - [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ - log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); - message message; - embed embed; - embed.set_color((uint32_t)color).set_description(Integration::status()).set_title("Program Status"); - message.add_embed(embed); - handler.reply(message, src); - }, - "View program status.") - - .add_command( - "click", + "Stop the currently running program." + ); +} +void Handler::add_command_resetcamera(dpp::commandhandler& handler){ + handler.add_command( + "resetcamera", { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}, - {"button", param_info(pt_string, false, "Switch console button.", - {{"0", "Y"}, - {"1", "B"}, - {"2", "A"}, - {"3", "X"}, - {"4", "L"}, - {"5", "R"}, - {"6", "ZL"}, - {"7", "ZR"}, - {"8", "Minus"}, - {"9", "Plus"}, - {"10", "LStick"}, - {"11", "RStick"}, - {"12", "Home"}, - {"13", "Capture"}, - {"14", "DUP"}, - {"15", "DDOWN"}, - {"16", "DLEFT"}, - {"17", "DRIGHT"},} - )}, - {"ticks", param_info(pt_integer, false, "How long to hold the button for, in ticks.")}, + {"id", param_info(pt_integer, true, "Console ID. Find yours by using the \"status\" command.")} }, [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); @@ -422,34 +501,43 @@ void Handler::create_unified_commands(commandhandler& handler){ embed embed; embed.set_color((uint32_t)color).set_title("Command Response"); - if (params.size() < 3){ - embed.set_description("Missing command arguments."); + int64_t id = Utility::sanitize_optional_integer_input(params, 0).value_or(-1); + std::string response = Integration::reset_camera(id); + if (!response.empty()){ + embed.set_description(response); message.add_embed(embed); handler.reply(message, src); return; } - int64_t id = Utility::sanitize_integer_input(params, 0); - std::string button_input = std::get(params[1].second); - - std::string name = "None"; - int64_t button = Utility::get_value_from_input(handler, command, button_input, name); - int64_t ticks = Utility::sanitize_integer_input(params, 2); - - if (button < 0){ - embed.set_description("No such button found: " + button_input); - message.add_embed(embed); - handler.reply(message, src); + embed.set_description("Reset the camera for console ID " + std::to_string(id) + "."); + message.add_embed(embed); + handler.reply(message, src); + }, + "Reset the camera." + ); +} +void Handler::add_command_resetcontroller(dpp::commandhandler& handler){ + handler.add_command( + "resetcontroller", + { + {"id", param_info(pt_integer, true, "Console ID. Find yours by using the \"status\" command.")}, + {"index", param_info(pt_integer, true, "Controller index.")}, + }, + [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ + log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); + if (!GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users && src.issuer.id != owner.id){ + handler.reply(message("You do not have permission to use this command."), src); return; } - std::string response; - if (button > 13){ - response = Integration::press_dpad(id, Utility::get_button(button), ticks); - }else{ - response = Integration::press_button(id, Utility::get_button(button), ticks); - } + message message; + embed embed; + embed.set_color((uint32_t)color).set_title("Command Response"); + int64_t id = Utility::sanitize_optional_integer_input(params, 0).value_or(-1); + int64_t index = Utility::sanitize_optional_integer_input(params, 1).value_or(0); + std::string response = Integration::reset_controller(id, index); if (!response.empty()){ embed.set_description(response); message.add_embed(embed); @@ -457,25 +545,60 @@ void Handler::create_unified_commands(commandhandler& handler){ return; } - embed.set_description("Console ID " + std::to_string(id) + " pressed button " + name + "."); + embed.set_description("Reset the serial connection for console ID " + std::to_string(id) + "."); message.add_embed(embed); handler.reply(message, src); }, - "Click a button for the specified console.") - - .add_command( - "joystick", - { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}, - {"stick", param_info(pt_string, false, "Switch console joystick.", - {{"0", "LStick"}, - {"1", "RStick"},} - )}, - {"magnitude_x", param_info(pt_integer, false, "Movement amount in the horizontal direction. \"Left\" is 0, \"right\" is 255, \"neutral\" is 127.")}, - {"magnitude_y", param_info(pt_integer, false, "Movement amount in the vertical direction. \"Down\" is 0, \"up\" is 255, \"neutral\" is 127.")}, - {"ticks", param_info(pt_integer, false, "How long to hold the stick for, in ticks.")}, - }, - [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ + "Reset the serial connection." + ); +} +void Handler::add_command_click(dpp::commandhandler& handler, bool full_version){ + parameter_registration_t parameters; + if (full_version){ + parameters.insert(parameters.end(), {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}); + parameters.insert(parameters.end(), {"index", param_info(pt_integer, false, "Controller index.")}); + } + parameters.insert(parameters.end(), { + {"button", param_info(pt_string, false, "Switch console button.",{ + {"0", "Y"}, + {"1", "B"}, + {"2", "A"}, + {"3", "X"}, + {"4", "L"}, + {"5", "R"}, + {"6", "ZL"}, + {"7", "ZR"}, + {"8", "Minus"}, + {"9", "Plus"}, + {"10", "LStick"}, + {"11", "RStick"}, + {"12", "Home"}, + {"13", "Capture"}, + {"14", "GR"}, + {"15", "GL"}, + {"16", "UP"}, + {"17", "RIGHT"}, + {"18", "DOWN"}, + {"19", "LEFT"}, + {"20", "LEFT_SL"}, + {"21", "LEFT_SR"}, + {"22", "RIGHT_SL"}, + {"23", "RIGHT_SR"}, + {"24", "C"}, + +// {"25", "DUP"}, +// {"26", "DDOWN"}, +// {"27", "DLEFT"}, +// {"28", "DRIGHT"}, + })}, + {"milliseconds", param_info(pt_integer, true, "How long to hold the button for, in milliseconds. (defaults to 100ms)")} + }); + uint8_t min_parameters = get_min_parameters(parameters); + + handler.add_command( + full_version ? "clickX" : "click", + parameters, + [=, &handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); if (!GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users && src.issuer.id != owner.id){ handler.reply(message("You do not have permission to use this command."), src); @@ -486,34 +609,38 @@ void Handler::create_unified_commands(commandhandler& handler){ embed embed; embed.set_color((uint32_t)color).set_title("Command Response"); - if (params.size() < 5){ + if (params.size() < min_parameters){ embed.set_description("Missing command arguments."); message.add_embed(embed); handler.reply(message, src); return; } + uint8_t c = 0; + int64_t id = -1; + int64_t index = 0; + if (full_version){ + id = Utility::sanitize_integer_input(params, c++); + index = Utility::sanitize_optional_integer_input(params, c++).value_or(0); + } + std::string button_input = std::get(params[c++].second); + std::string name = "None"; - int64_t id = Utility::sanitize_integer_input(params, 0); - std::string stick_input = std::get(params[1].second); - int64_t stick = Utility::get_value_from_input(handler, command, stick_input, name); + int64_t button = Utility::get_value_from_input(handler, command, full_version ? 2 : 0, button_input, name); + uint32_t milliseconds = (uint32_t)Utility::sanitize_optional_integer_input(params, c++).value_or(100); - if (stick < 0){ - embed.set_description("No such joystick found: " + stick_input); + if (button < 0){ + embed.set_description("No such button found: " + button_input); message.add_embed(embed); handler.reply(message, src); return; } - int64_t x = Utility::sanitize_integer_input(params, 2); - int64_t y = Utility::sanitize_integer_input(params, 3); - int64_t ticks = Utility::sanitize_integer_input(params, 4); - std::string response; - if (stick == 0){ - response = Integration::press_left_joystick(id, x, y, ticks); + if (button >= 25){ + response = Integration::press_dpad(id, index, milliseconds, Utility::get_button(button)); }else{ - response = Integration::press_right_joystick(id, x, y, ticks); + response = Integration::press_button(id, index, milliseconds, Utility::get_button(button)); } if (!response.empty()){ @@ -523,22 +650,46 @@ void Handler::create_unified_commands(commandhandler& handler){ return; } - embed.set_description("Console ID " + std::to_string(id) + " moved " + name + " (X: " + std::to_string(x) + ", Y: " + std::to_string(y) + ") for " + std::to_string(ticks) + " ticks."); + embed.set_description("Console ID " + std::to_string(id) + " pressed button " + name + "."); message.add_embed(embed); handler.reply(message, src); }, - "Click a button for the specified console.") - - .add_command( - "screenshot", - { - {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}, - {"format", param_info(pt_string, false, "Image format.", - {{"0", "png"}, - {"1", "jpg"},} - )}, - }, - [&handler, this](const std::string& command, const parameter_list_t& params, command_source src){ + "Click a button for the specified console." + ); +} +void Handler::add_command_joystick(dpp::commandhandler& handler, bool full_version, JoystickSide side){ + std::string side_str = ""; + switch (side){ + case JoystickSide::NEITHER: + side_str = "joystick"; + break; + case JoystickSide::LEFT: + side_str = "Lstick"; + break; + case JoystickSide::RIGHT: + side_str = "Rstick"; + break; + } + if (full_version){ + side_str += "X"; + } + + parameter_registration_t parameters; + if (full_version){ + parameters.insert(parameters.end(), {"id", param_info(pt_integer, false, "Console ID. Find yours by using the \"status\" command.")}); + parameters.insert(parameters.end(), {"index", param_info(pt_integer, false, "Controller index.")}); + } + parameters.insert(parameters.end(), { + {"magnitude_x", param_info(pt_double, false, "Movement amount in the horizontal direction. \"Left\" is -1.0, \"right\" is +1.0, \"neutral\" is 0.0.")}, + {"magnitude_y", param_info(pt_double, false, "Movement amount in the vertical direction. \"Down\" is -1.0, \"up\" is +1.0, \"neutral\" is 0.0.")}, + {"milliseconds", param_info(pt_integer, true, "How long to hold the stick for, in milliseconds. (defaults to 100ms)")}, + }); + uint8_t min_parameters = get_min_parameters(parameters); + + handler.add_command( + side_str, + parameters, + [=, &handler, this](const std::string& command, const parameter_list_t& params, command_source src){ log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); if (!GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users && src.issuer.id != owner.id){ handler.reply(message("You do not have permission to use this command."), src); @@ -547,110 +698,65 @@ void Handler::create_unified_commands(commandhandler& handler){ message message; embed embed; - embed.set_color((uint32_t)color).set_title("Program Screenshot"); + embed.set_color((uint32_t)color).set_title("Command Response"); - if (params.size() < 2){ + if (params.size() < min_parameters){ embed.set_description("Missing command arguments."); message.add_embed(embed); handler.reply(message, src); return; } - handler.thinking(src); - std::string name = "None"; - int64_t id = Utility::sanitize_integer_input(params, 0); - std::string button_input = std::get(params[1].second); - int64_t format = Utility::get_value_from_input(handler, command, button_input, name); - - std::string path; - if (format == 0){ - path = "screenshot_slash.png"; - }else{ - path = "screenshot_slash.jpg"; + uint8_t c = 0; + int64_t id = -1; + int64_t index = 0; + if (full_version){ + id = Utility::sanitize_integer_input(params, c++); + index = Utility::sanitize_optional_integer_input(params, c++).value_or(0); } - std::string response = Integration::screenshot(id, path.c_str()); + double x = std::get(params[c++].second); + double y = std::get(params[c++].second); + uint32_t milliseconds = (uint32_t)Utility::sanitize_optional_integer_input(params, c++).value_or(100); + + std::string response = Integration::press_joystick(id, index, milliseconds, side, x, y); if (!response.empty()){ embed.set_description(response); - Handler::update_response(src, embed, "", nullptr); + message.add_embed(embed); + handler.reply(message, src); return; } - std::shared_ptr file(new PendingFileSend(path, true)); - embed_footer footer; - footer.set_text("Console ID: " + std::to_string(id) + " (" + name + ")"); - - embed.set_footer(footer); - Handler::update_response(src, embed, "", std::move(file)); - }, - "Take and upload a screenshot from the specified console.") - - .add_command( - "help", - {}, - [&handler, this](const std::string& command, const parameter_list_t&, command_source src){ - log_dpp("Executing " + command + "...", "Unified Command Handler", ll_info); - message message; - embed embed; - embed.set_color((uint32_t)color).set_title("Command List"); - - static const std::unordered_set base_commands{ - "hi", "ping", "about", "status", "help" - }; - static const std::unordered_set button_commands{ - "click", "joystick" - }; - - std::unordered_set allowed = base_commands; - if (GlobalSettings::instance().DISCORD->integration.allow_buttons_from_users){ - allowed.insert(button_commands.begin(), button_commands.end()); - } - - auto& commands = handler.commands; - for (auto& cmd : commands){ - const std::string& cmd_name = cmd.first; - if (src.issuer.id != owner.id && allowed.find(cmd_name) == allowed.end()){ - continue; - } - - auto& params = cmd.second.parameters; - std::string signature; - if (!params.empty()){ - signature = cmd_name + "("; - for (size_t i = 0; i < params.size(); ++i){ - signature += params[i].first; - if (i + 1 < params.size()){ - signature += ", "; - } - } - signature += ")"; - }else{ - signature = cmd_name; - } - - std::string param_details; - for (auto& param : params){ - param_details += ("\n-" + param.first + ": " + param.second.description); - if (!param.second.choices.empty()){ - std::string choices; - for (auto& c : param.second.choices){ - choices += c.second + ", "; - } - if (!choices.empty()){ - choices = choices.substr(0, choices.size() - 2); - } - param_details += " (" + choices + ")"; - } - } - embed.add_field(signature, param_details); - } - embed_footer footer; - footer.set_text("Commands are case-sensitive!"); - embed.set_footer(footer); + embed.set_description( + "Console ID " + std::to_string(id) + " moved (X: " + std::to_string(x) + + ", Y: " + std::to_string(y) + ") for " + std::to_string(milliseconds) + "ms." + ); message.add_embed(embed); handler.reply(message, src); }, - "View the command list."); + "Click a button for the specified console." + ); +} + +void Handler::create_unified_commands(commandhandler& handler){ + add_command_ping(handler); + add_command_about(handler); + add_command_hi(handler); + add_command_status(handler); + add_command_screenshot(handler, false); + add_command_screenshot(handler, true); + add_command_start(handler); + add_command_stop(handler); + add_command_resetcamera(handler); + add_command_resetcontroller(handler); + add_command_click(handler, false); + add_command_click(handler, true); + add_command_joystick(handler, false, JoystickSide::NEITHER); + add_command_joystick(handler, false, JoystickSide::LEFT); + add_command_joystick(handler, false, JoystickSide::RIGHT); + add_command_joystick(handler, true, JoystickSide::NEITHER); + add_command_joystick(handler, true, JoystickSide::LEFT); + add_command_joystick(handler, true, JoystickSide::RIGHT); } } diff --git a/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.h b/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.h index d90a8a9b59..3c18866164 100644 --- a/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.h +++ b/SerialPrograms/Source/Integrations/DppIntegration/DppCommandHandler.h @@ -12,6 +12,7 @@ #include "CommonFramework/Notifications/MessageAttachment.h" #include "CommonFramework/GlobalSettingsPanel.h" #include "CommonFramework/Tools/GlobalThreadPools.h" +#include "Integrations/IntegrationsAPI.h" #include "Integrations/DiscordSettingsOption.h" namespace PokemonAutomation{ @@ -58,6 +59,22 @@ class Handler : DppUtility::Utility{ ); private: + static uint8_t get_min_parameters(const dpp::parameter_registration_t& params); + + void add_command_hi(dpp::commandhandler& handler); + void add_command_ping(dpp::commandhandler& handler); + void add_command_about(dpp::commandhandler& handler); + void add_command_help(dpp::commandhandler& handler); + void add_command_status(dpp::commandhandler& handler); + void add_command_screenshot(dpp::commandhandler& handler, bool full_version); + void add_command_start(dpp::commandhandler& handler); + void add_command_stop(dpp::commandhandler& handler); + void add_command_resetcamera(dpp::commandhandler& handler); + void add_command_resetcontroller(dpp::commandhandler& handler); + void add_command_click(dpp::commandhandler& handler, bool full_version); + void add_command_joystick(dpp::commandhandler& handler, bool full_version, JoystickSide side); + + void create_unified_commands(dpp::commandhandler& handler); void update_response( const dpp::command_source& src, diff --git a/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.cpp b/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.cpp index a2e9c973a6..8ebd5d38a4 100644 --- a/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.cpp +++ b/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.cpp @@ -72,29 +72,31 @@ void Utility::get_user_counts(cluster& bot, const guild_create_t& event){ #endif } -uint16_t Utility::get_button(const uint16_t& bt){ - if (bt > 13){ +uint32_t Utility::get_button(uint32_t bt){ + if (bt >= 25){ uint8_t dpad = 0; switch (bt){ - case 14: dpad = 0; break; // DUP - case 15: dpad = 4; break; // DDown - case 16: dpad = 6; break; // DLeft - case 17: dpad = 2; break; // DRight + case 25: dpad = 0; break; // DUP + case 26: dpad = 4; break; // DDown + case 27: dpad = 6; break; // DLeft + case 28: dpad = 2; break; // DRight default: dpad = 0; break; }; return dpad; } - return 1 << bt; +// cout << "get_button(): " << bt << endl; + return (uint32_t)1 << bt; } int64_t Utility::get_value_from_input( const commandhandler& handler, const std::string& command_name, + uint8_t param_index, const std::string& input, std::string& out ){ auto cmd = handler.commands.find(command_name); - auto& choices = cmd->second.parameters[1].second.choices; + auto& choices = cmd->second.parameters[param_index].second.choices; for (auto& choice : choices){ std::string val = std::get(choice.first); if (val == input || choice.second == input){ @@ -113,6 +115,26 @@ int64_t Utility::sanitize_integer_input(const parameter_list_t& params, const ui return val; } +std::optional Utility::sanitize_optional_integer_input(const dpp::parameter_list_t& params, const uint8_t& index){ + if (index >= params.size()){ + return std::nullopt; + } + + const auto& value_variant = params[index].second; + + // Check if the parameter was omitted + if (std::holds_alternative(value_variant)) { + return std::nullopt; + } + + // Extract and sanitize the integer + int64_t val = std::get(value_variant); + if (val < 0){ + return 0; + } + return val; +} + } diff --git a/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.h b/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.h index a957053549..d16a03aa7b 100644 --- a/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.h +++ b/SerialPrograms/Source/Integrations/DppIntegration/DppUtility.h @@ -37,11 +37,13 @@ class Utility{ int64_t get_value_from_input( const dpp::commandhandler& handler, const std::string& cmd, + uint8_t param_index, const std::string& input, std::string& out ); int64_t sanitize_integer_input(const dpp::parameter_list_t& params, const uint8_t& index); - uint16_t get_button(const uint16_t& bt); + std::optional sanitize_optional_integer_input(const dpp::parameter_list_t& params, const uint8_t& index); + uint32_t get_button(uint32_t bt); private: Logger& dpp_logger(); diff --git a/SerialPrograms/Source/Integrations/IntegrationsAPI.cpp b/SerialPrograms/Source/Integrations/IntegrationsAPI.cpp index 5114810bc8..ecefea6f65 100644 --- a/SerialPrograms/Source/Integrations/IntegrationsAPI.cpp +++ b/SerialPrograms/Source/Integrations/IntegrationsAPI.cpp @@ -48,7 +48,9 @@ void pai_status(DllSafeString& description){ case ProgramState::RUNNING: str += "Running"; str += " ("; - str += duration_to_string(std::chrono::duration_cast(now - item.second.start_time)); + str += duration_to_string( + std::chrono::duration_cast(now - item.second.last_state_change) + ); str += ")"; break; case ProgramState::STOPPING: @@ -95,8 +97,8 @@ void pai_screenshot(DllSafeString& error, uint64_t console_id, const char* path) void pai_reset_camera(DllSafeString& error, uint64_t console_id){ error = ProgramTracker::instance().reset_camera(console_id); } -void pai_reset_serial(DllSafeString& error, uint64_t console_id){ - error = ProgramTracker::instance().reset_serial(console_id); +void pai_reset_controller(DllSafeString& error, uint64_t console_id, uint64_t controller_index){ + error = ProgramTracker::instance().reset_controller(console_id, controller_index); } void pai_start_program(DllSafeString& error, uint64_t program_id){ @@ -106,17 +108,42 @@ void pai_stop_program(DllSafeString& error, uint64_t program_id){ error = ProgramTracker::instance().stop_program(program_id); } -void pai_nsw_press_button(DllSafeString& error, uint64_t console_id, uint16_t button, uint16_t ticks){ - error = ProgramTracker::instance().nsw_press_button(console_id, (NintendoSwitch::Button)button, ticks); +void pai_nsw_press_button( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint32_t button +){ + error = ProgramTracker::instance().nsw_press_button( + console_id, controller_index, + Milliseconds(milliseconds), + (NintendoSwitch::Button)button + ); } -void pai_nsw_press_dpad(DllSafeString& error, uint64_t console_id, uint8_t position, uint16_t ticks){ - error = ProgramTracker::instance().nsw_press_dpad(console_id, (NintendoSwitch::DpadPosition)position, ticks); +void pai_nsw_press_dpad( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint8_t position +){ + error = ProgramTracker::instance().nsw_press_dpad( + console_id, controller_index, + Milliseconds(milliseconds), + (NintendoSwitch::DpadPosition)position + ); } -void pai_nsw_press_left_joystick(DllSafeString& error, uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ - error = ProgramTracker::instance().nsw_press_left_joystick(console_id, x, y, ticks); -} -void pai_nsw_press_right_joystick(DllSafeString& error, uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ - error = ProgramTracker::instance().nsw_press_right_joystick(console_id, x, y, ticks); +void pai_nsw_press_joystick( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + JoystickSide side, + double x, double y +){ + error = ProgramTracker::instance().nsw_press_joystick( + console_id, controller_index, + Milliseconds(milliseconds), + side, JoystickPosition{x, y} + ); } diff --git a/SerialPrograms/Source/Integrations/IntegrationsAPI.h b/SerialPrograms/Source/Integrations/IntegrationsAPI.h index 1cf861a4e8..81d5a02d20 100644 --- a/SerialPrograms/Source/Integrations/IntegrationsAPI.h +++ b/SerialPrograms/Source/Integrations/IntegrationsAPI.h @@ -14,6 +14,11 @@ namespace PokemonAutomation{ namespace Integration{ extern "C" { +enum JoystickSide{ + NEITHER, + LEFT, + RIGHT, +}; // Empty error means no error. @@ -23,15 +28,30 @@ void pai_status (DllSafeString& description); void pai_screenshot (DllSafeString& error, uint64_t console_id, const char* path); void pai_reset_camera (DllSafeString& error, uint64_t console_id); -void pai_reset_serial (DllSafeString& error, uint64_t console_id); +void pai_reset_controller (DllSafeString& error, uint64_t console_id, uint64_t controller_index); void pai_start_program (DllSafeString& error, uint64_t program_id); void pai_stop_program (DllSafeString& error, uint64_t program_id); -void pai_nsw_press_button (DllSafeString& error, uint64_t console_id, uint16_t button, uint16_t ticks); -void pai_nsw_press_dpad (DllSafeString& error, uint64_t console_id, uint8_t position, uint16_t ticks); -void pai_nsw_press_left_joystick (DllSafeString& error, uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks); -void pai_nsw_press_right_joystick (DllSafeString& error, uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks); +void pai_nsw_press_button( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint32_t button +); +void pai_nsw_press_dpad( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint8_t position +); +void pai_nsw_press_joystick( + DllSafeString& error, + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + JoystickSide side, + double x, double y +); } @@ -53,9 +73,9 @@ inline std::string reset_camera(uint64_t console_id){ pai_reset_camera(error, console_id); return error; } -inline std::string reset_serial(uint64_t console_id){ +inline std::string reset_controller(uint64_t console_id, uint64_t controller_index){ DllSafeString error; - pai_reset_serial(error, console_id); + pai_reset_controller(error, console_id, controller_index); return error; } inline std::string start_program(uint64_t program_id){ @@ -68,24 +88,32 @@ inline std::string stop_program(uint64_t program_id){ pai_stop_program(error, program_id); return error; } -inline std::string press_button(uint64_t console_id, uint16_t button, uint16_t ticks){ +inline std::string press_button( + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint32_t button +){ DllSafeString error; - pai_nsw_press_button(error, console_id, button, ticks); + pai_nsw_press_button(error, console_id, controller_index, milliseconds, button); return error; } -inline std::string press_dpad(uint64_t console_id, uint8_t position, uint16_t ticks){ +inline std::string press_dpad( + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + uint8_t position +){ DllSafeString error; - pai_nsw_press_dpad(error, console_id, position, ticks); + pai_nsw_press_dpad(error, console_id, controller_index, milliseconds, position); return error; } -inline std::string press_left_joystick(uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ +inline std::string press_joystick( + uint64_t console_id, uint64_t controller_index, + uint32_t milliseconds, + JoystickSide side, + double x, double y +){ DllSafeString error; - pai_nsw_press_left_joystick(error, console_id, x, y, ticks); - return error; -} -inline std::string press_right_joystick(uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ - DllSafeString error; - pai_nsw_press_right_joystick(error, console_id, x, y, ticks); + pai_nsw_press_joystick(error, console_id, controller_index, milliseconds, side, x, y); return error; } diff --git a/SerialPrograms/Source/Integrations/ProgramTracker.cpp b/SerialPrograms/Source/Integrations/ProgramTracker.cpp index 91f9b1e7da..9191321afc 100644 --- a/SerialPrograms/Source/Integrations/ProgramTracker.cpp +++ b/SerialPrograms/Source/Integrations/ProgramTracker.cpp @@ -7,9 +7,9 @@ #include "CommonFramework/Logging/Logger.h" #include "CommonFramework/ImageTypes/ImageRGB32.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" -#include "Controllers/JoystickTools.h" #include "Controllers/ControllerSession.h" #include "NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h" +#include "NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.h" #include "ProgramTracker.h" //#include @@ -47,7 +47,7 @@ std::map ProgramTracker::all_programs(){ info[item.first] = ProgramTrackingState{ item.second->program.identifier(), item.second->console_ids, - item.second->program.timestamp(), + item.second->program.last_state_change(), item.second->program.current_state(), item.second->program.current_stats() }; @@ -55,192 +55,306 @@ std::map ProgramTracker::all_programs(){ return info; } +std::string ProgramTracker::make_header(const std::string& function_name, uint64_t id){ + if (id == (uint64_t)-1){ + return function_name + "()"; + } + return function_name + "(ID = " + std::to_string(id) + ")"; +} +TrackableProgram* ProgramTracker::get_program( + std::string& error, + const std::string& header, + uint64_t program_id +){ + auto iter = m_programs.end(); + if (m_programs.empty()){ + error = header + ": No programs found."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + }else if (program_id != (uint64_t)-1){ + iter = m_programs.find(program_id); + }else if (m_programs.size() == 1){ + iter = m_programs.begin(); + }else{ + error = header + ": Multiple programs found. Please specify an ID."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + } + if (iter == m_programs.end()){ + error = header + ": ID not found."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + } + + return &iter->second->program; +} +TrackableConsole* ProgramTracker::get_console( + std::string& error, + const std::string& header, + uint64_t console_id +){ + auto iter = m_consoles.end(); + if (m_consoles.empty()){ + error = header + ": No consoles found."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + }else if (console_id != (uint64_t)-1){ + iter = m_consoles.find(console_id); + }else if (m_consoles.size() == 1){ + iter = m_consoles.begin(); + }else{ + error = header + ": Multiple consoles found. Please specify an ID."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + } + if (iter == m_consoles.end()){ + error = header + ": ID not found."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + } + + return iter->second.first; +} +ControllerSession* ProgramTracker::get_controller( + std::string& error, + const std::string& header, + uint64_t console_id, + uint64_t controller_index +){ + TrackableConsole* console = get_console(error, header, console_id); + if (console == nullptr){ + return nullptr; + } + if (controller_index >= console->controllers()){ + error = "reset_serial(" + std::to_string(console_id) + ") - Index out of bounds."; + global_logger_tagged().log("ProgramTracker::" + error, COLOR_RED); + return nullptr; + } + return &console->controller(controller_index); +} + std::string ProgramTracker::grab_screenshot(uint64_t console_id, std::shared_ptr& image){ + std::string header = make_header("grab_screenshot", console_id); std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = "grab_screenshot(" + std::to_string(console_id) + ") - ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + std::string error; + TrackableConsole* console = get_console(error, header, console_id); + if (console == nullptr){ return error; } - VideoSnapshot snapshot = iter->second.first->video_feed().snapshot(); + VideoSnapshot snapshot = console->video_feed().snapshot(); image = std::move(snapshot.frame); return ""; } std::string ProgramTracker::reset_camera(uint64_t console_id){ + std::string header = make_header("reset_camera", console_id); std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = "reset_camera(" + std::to_string(console_id) + ") - ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + std::string error; + TrackableConsole* console = get_console(error, header, console_id); + if (console == nullptr){ return error; } - iter->second.first->video_feed().reset(); + console->video_feed().reset(); return ""; } -std::string ProgramTracker::reset_serial(uint64_t console_id){ +std::string ProgramTracker::reset_controller(uint64_t console_id, uint64_t controller_index){ + std::string header = make_header("reset_controller", console_id); std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = "reset_serial(" + std::to_string(console_id) + ") - ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + std::string error; + ControllerSession* controller = get_controller(error, header, console_id, controller_index); + if (controller == nullptr){ return error; } - std::string error = iter->second.first->controller().reset(false); + error = controller->reset(false); return error.empty() ? "Controller was reset." : error; } std::string ProgramTracker::start_program(uint64_t program_id){ + std::string header = make_header("start_program", program_id); std::lock_guard lg(m_lock); - auto iter = m_programs.find(program_id); - if (iter == m_programs.end()){ - std::string error = "start_program(ID = " + std::to_string(program_id) + ") - ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + std::string error; + TrackableProgram* program = get_program(error, header, program_id); + if (program == nullptr){ return error; } - iter->second->program.async_start(); + program->async_start(); return ""; } std::string ProgramTracker::stop_program(uint64_t program_id){ + std::string header = make_header("stop_program", program_id); std::lock_guard lg(m_lock); - auto iter = m_programs.find(program_id); - if (iter == m_programs.end()){ - std::string error = "stop_program(ID = " + std::to_string(program_id) + ") - ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + std::string error; + TrackableProgram* program = get_program(error, header, program_id); + if (program == nullptr){ return error; } - iter->second->program.async_stop(); + program->async_stop(); return ""; } -std::string ProgramTracker::nsw_press_button(uint64_t console_id, NintendoSwitch::Button button, uint16_t ticks){ +std::string ProgramTracker::nsw_press_button( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + NintendoSwitch::Button button +){ using namespace NintendoSwitch; - std::string header = "press_button(ID = " + std::to_string(console_id) + ")"; + std::string header = make_header("press_button", console_id); + std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = header + ": ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + + std::string error; + ControllerSession* controller = get_controller(error, header, console_id, controller_index); + if (controller == nullptr){ return error; } - Milliseconds duration = ticks * 8ms; - std::string err; + +// cout << "button = " << button << endl; + try{ - err = iter->second.first->controller().try_run( - [=](ProController& controller){ - controller.issue_buttons(nullptr, duration, duration, 0ms, button); - } - ); + switch (controller->controller_class()){ + case ControllerClass::NintendoSwitch_ProController: + error = controller->try_run( + [=](ProController& controller){ + controller.issue_buttons(nullptr, duration, duration, 0ms, button); + } + ); + break; + case ControllerClass::NintendoSwitch_LeftJoycon: + error = controller->try_run( + [=](LeftJoycon& controller){ + controller.issue_buttons(nullptr, duration, duration, 0ms, button); + } + ); + break; + case ControllerClass::NintendoSwitch_RightJoycon: + error = controller->try_run( + [=](RightJoycon& controller){ + controller.issue_buttons(nullptr, duration, duration, 0ms, button); + } + ); + break; + default: + error = "Incompatible controller class."; + } }catch (Exception& e){ e.log(global_logger_tagged()); - err = e.to_str(); + error = e.to_str(); } - if (err.empty()){ - global_logger_tagged().log("SwitchProgramTracker::" + header, COLOR_BLUE); + if (error.empty()){ + global_logger_tagged().log("ProgramTracker::" + header, COLOR_BLUE); return ""; }else{ - global_logger_tagged().log("SwitchProgramTracker::" + header + ": " + err, COLOR_RED); - return err; + global_logger_tagged().log("ProgramTracker::" + header + ": " + error, COLOR_RED); + return error; } } -std::string ProgramTracker::nsw_press_dpad(uint64_t console_id, NintendoSwitch::DpadPosition position, uint16_t ticks){ +std::string ProgramTracker::nsw_press_dpad( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + NintendoSwitch::DpadPosition position +){ using namespace NintendoSwitch; - std::string header = "press_dpad(ID = " + std::to_string(console_id) + ")"; + std::string header = make_header("press_dpad", console_id); + std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = header + ": ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + + std::string error; + ControllerSession* controller = get_controller(error, header, console_id, controller_index); + if (controller == nullptr){ return error; } - Milliseconds duration = ticks * 8ms; - std::string err; + try{ - err = iter->second.first->controller().try_run( + error = controller->try_run( [=](ProController& controller){ controller.issue_dpad(nullptr, duration, duration, 0ms, position); } ); }catch (Exception& e){ e.log(global_logger_tagged()); - err = e.to_str(); + error = e.to_str(); } - if (err.empty()){ - global_logger_tagged().log("SwitchProgramTracker::" + header, COLOR_BLUE); + if (error.empty()){ + global_logger_tagged().log("ProgramTracker::" + header, COLOR_BLUE); return ""; }else{ - global_logger_tagged().log("SwitchProgramTracker::" + header + ": " + err, COLOR_RED); - return err; - } -} -std::string ProgramTracker::nsw_press_left_joystick(uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ - using namespace NintendoSwitch; - std::string header = "press_left_joystick(ID = " + std::to_string(console_id) + ")"; - std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = header + ": ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + global_logger_tagged().log("ProgramTracker::" + header + ": " + error, COLOR_RED); return error; } - Milliseconds duration = ticks * 8ms; - std::string err; - try{ - err = iter->second.first->controller().try_run( - [=](ProController& controller){ - controller.issue_left_joystick( - nullptr, duration, duration, 0ms, - { - JoystickTools::linear_u8_to_float(x), - -JoystickTools::linear_u8_to_float(y) - } - ); - } - ); - }catch (Exception& e){ - e.log(global_logger_tagged()); - err = e.to_str(); - } - if (err.empty()){ - global_logger_tagged().log("SwitchProgramTracker::" + header, COLOR_BLUE); - return ""; - }else{ - global_logger_tagged().log("SwitchProgramTracker::" + header + ": " + err, COLOR_RED); - return err; - } } -std::string ProgramTracker::nsw_press_right_joystick(uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks){ +std::string ProgramTracker::nsw_press_joystick( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + Integration::JoystickSide side, + JoystickPosition position +){ using namespace NintendoSwitch; - std::string header = "press_right_joystick(ID = " + std::to_string(console_id) + ")"; + std::string header = make_header("press_left_joystick", console_id); + std::lock_guard lg(m_lock); - auto iter = m_consoles.find(console_id); - if (iter == m_consoles.end()){ - std::string error = header + ": ID not found."; - global_logger_tagged().log("SwitchProgramTracker::" + error, COLOR_RED); + + std::string error; + ControllerSession* controller = get_controller(error, header, console_id, controller_index); + if (controller == nullptr){ return error; } - Milliseconds duration = ticks * 8ms; - std::string err; + + ControllerClass type = controller->controller_class(); try{ - err = iter->second.first->controller().try_run( - [=](ProController& controller){ - controller.issue_right_joystick( - nullptr, duration, duration, 0ms, - { - JoystickTools::linear_u8_to_float(x), - -JoystickTools::linear_u8_to_float(y) - } - ); + auto procon_left = [=](ProController& controller){ + controller.issue_left_joystick(nullptr, duration, duration, 0ms, position); + }; + auto procon_right = [=](ProController& controller){ + controller.issue_right_joystick(nullptr, duration, duration, 0ms, position); + }; + auto joycon = [=](JoyconController& controller){ + controller.issue_joystick(nullptr, duration, duration, 0ms, position); + }; + + switch (type){ + case ControllerClass::NintendoSwitch_ProController: + switch (side){ + case PokemonAutomation::Integration::JoystickSide::LEFT: + error = controller->try_run(procon_left); + break; + case PokemonAutomation::Integration::JoystickSide::RIGHT: + error = controller->try_run(procon_right); + break; + default: + error = "Incompatible controller class."; } - ); + break; + case ControllerClass::NintendoSwitch_LeftJoycon: + switch (side){ + case PokemonAutomation::Integration::JoystickSide::NEITHER: + case PokemonAutomation::Integration::JoystickSide::LEFT: + error = controller->try_run(joycon); + break; + default: + error = "Incompatible controller class."; + } + break; + case ControllerClass::NintendoSwitch_RightJoycon: + switch (side){ + case PokemonAutomation::Integration::JoystickSide::NEITHER: + case PokemonAutomation::Integration::JoystickSide::RIGHT: + error = controller->try_run(joycon); + break; + default: + error = "Incompatible controller class."; + } + break; + default: + error = "Incompatible controller class."; + } + }catch (Exception& e){ e.log(global_logger_tagged()); - err = e.to_str(); + error = e.to_str(); } - if (err.empty()){ - global_logger_tagged().log("SwitchProgramTracker::" + header, COLOR_BLUE); + if (error.empty()){ + global_logger_tagged().log("ProgramTracker::" + header, COLOR_BLUE); return ""; }else{ - global_logger_tagged().log("SwitchProgramTracker::" + header + ": " + err, COLOR_RED); - return err; + global_logger_tagged().log("ProgramTracker::" + header + ": " + error, COLOR_RED); + return error; } } diff --git a/SerialPrograms/Source/Integrations/ProgramTracker.h b/SerialPrograms/Source/Integrations/ProgramTracker.h index af030507ca..3a620f612b 100644 --- a/SerialPrograms/Source/Integrations/ProgramTracker.h +++ b/SerialPrograms/Source/Integrations/ProgramTracker.h @@ -15,7 +15,9 @@ #include #include "Common/Cpp/Concurrency/Mutex.h" #include "CommonFramework/Globals.h" +#include "Controllers/Joystick.h" #include "NintendoSwitch/Controllers/NintendoSwitch_ControllerButtons.h" +#include "IntegrationsAPI.h" #include "ProgramTrackerInterfaces.h" namespace PokemonAutomation{ @@ -29,7 +31,7 @@ class AudioFeed; struct ProgramTrackingState{ std::string program_name; std::vector console_ids; - WallClock start_time; + WallClock last_state_change; ProgramState state; std::string stats; }; @@ -44,7 +46,7 @@ class ProgramTracker{ std::string grab_screenshot (uint64_t console_id, std::shared_ptr& image); std::string reset_camera (uint64_t console_id); - std::string reset_serial (uint64_t console_id); + std::string reset_controller (uint64_t console_id, uint64_t controller_index); // void change_program (uint64_t program_id, std::string program_identifier); std::string start_program (uint64_t program_id); std::string stop_program (uint64_t program_id); @@ -52,10 +54,22 @@ class ProgramTracker{ public: // Nintendo Switch - std::string nsw_press_button (uint64_t console_id, NintendoSwitch::Button button, uint16_t ticks); - std::string nsw_press_dpad (uint64_t console_id, NintendoSwitch::DpadPosition position, uint16_t ticks); - std::string nsw_press_left_joystick (uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks); - std::string nsw_press_right_joystick(uint64_t console_id, uint8_t x, uint8_t y, uint16_t ticks); + std::string nsw_press_button( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + NintendoSwitch::Button button + ); + std::string nsw_press_dpad( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + NintendoSwitch::DpadPosition position + ); + std::string nsw_press_joystick( + uint64_t console_id, uint64_t controller_index, + Milliseconds duration, + Integration::JoystickSide side, + JoystickPosition position + ); private: @@ -63,6 +77,7 @@ class ProgramTracker{ ProgramTracker(const ProgramTracker&) = delete; void operator=(const ProgramTracker&) = delete; + public: uint64_t add_program(TrackableProgram& program); void remove_program(uint64_t program_id); @@ -73,6 +88,26 @@ class ProgramTracker{ void remove_console(std::optional console_id); +private: + static std::string make_header(const std::string& function_name, uint64_t id); + TrackableProgram* get_program( + std::string& error, + const std::string& header, + uint64_t program_id + ); + TrackableConsole* get_console( + std::string& error, + const std::string& header, + uint64_t console_id + ); + ControllerSession* get_controller( + std::string& error, + const std::string& header, + uint64_t console_id, + uint64_t controller_index + ); + + private: struct ProgramData; diff --git a/SerialPrograms/Source/Integrations/ProgramTrackerInterfaces.h b/SerialPrograms/Source/Integrations/ProgramTrackerInterfaces.h index a6383dac29..9d4f4c3125 100644 --- a/SerialPrograms/Source/Integrations/ProgramTrackerInterfaces.h +++ b/SerialPrograms/Source/Integrations/ProgramTrackerInterfaces.h @@ -23,15 +23,16 @@ class BotBaseHandle; class TrackableConsole{ public: - virtual VideoFeed& video_feed() = 0; - virtual AudioFeed& audio_feed() = 0; - virtual ControllerSession& controller() = 0; + virtual VideoFeed& video_feed() noexcept = 0; + virtual AudioFeed& audio_feed() noexcept = 0; + virtual size_t controllers() const noexcept = 0; + virtual ControllerSession& controller(size_t index) noexcept = 0; }; class TrackableProgram{ public: virtual const std::string& identifier() const = 0; - virtual WallClock timestamp() const = 0; + virtual WallClock last_state_change() const = 0; virtual ProgramState current_state() const = 0; virtual std::string current_stats() const = 0; diff --git a/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.cpp b/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.cpp index 77fa62786b..20251c4fe2 100644 --- a/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.cpp @@ -113,10 +113,10 @@ void JoyconController::on_rumble(double magnitude){ -ControllerClass LeftJoycon::controller_class() const{ +ControllerClass LeftJoycon::controller_class() const noexcept{ return ControllerClass::NintendoSwitch_LeftJoycon; } -ControllerClass RightJoycon::controller_class() const{ +ControllerClass RightJoycon::controller_class() const noexcept{ return ControllerClass::NintendoSwitch_RightJoycon; } diff --git a/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.h b/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.h index 60338c12c9..be17d13948 100644 --- a/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.h +++ b/SerialPrograms/Source/NintendoSwitch/Controllers/Joycon/NintendoSwitch_Joycon.h @@ -207,7 +207,7 @@ class LeftJoycon : public JoyconController{ virtual const char* name() override{ return NAME; }; - virtual ControllerClass controller_class() const override; + virtual ControllerClass controller_class() const noexcept override; }; class RightJoycon : public JoyconController{ public: @@ -217,7 +217,7 @@ class RightJoycon : public JoyconController{ virtual const char* name() override{ return NAME; }; - virtual ControllerClass controller_class() const override; + virtual ControllerClass controller_class() const noexcept override; }; diff --git a/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.cpp b/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.cpp index 8c7309b064..e0c74ae164 100644 --- a/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.cpp @@ -60,7 +60,7 @@ ProController::ProController(Logger& logger) ProController::~ProController(){ } -ControllerClass ProController::controller_class() const{ +ControllerClass ProController::controller_class() const noexcept{ return ControllerClass::NintendoSwitch_ProController; } diff --git a/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h b/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h index 608645aff6..cb5a82294e 100644 --- a/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h +++ b/SerialPrograms/Source/NintendoSwitch/Controllers/Procon/NintendoSwitch_ProController.h @@ -78,7 +78,7 @@ class ProController : public AbstractController{ virtual const char* name() override{ return NAME; }; - virtual ControllerClass controller_class() const override; + virtual ControllerClass controller_class() const noexcept override; virtual ControllerPlayerNumber get_player_number(Cancellable& cancellable){ return ControllerPlayerNumber::UNKNOWN; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.cpp index c61792972a..a7e18c10ee 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.cpp @@ -77,6 +77,10 @@ ConfigOption& MultiSwitchProgramSession::options(){ +std::string MultiSwitchProgramSession::check_validity() const{ + auto ScopeCheck = m_sanitizer.check_scope(); + return m_instance->check_validity(); +} void MultiSwitchProgramSession::restore_defaults(){ auto ScopeCheck = m_sanitizer.check_scope(); std::lock_guard lg(program_lock()); @@ -87,10 +91,6 @@ void MultiSwitchProgramSession::restore_defaults(){ logger().log("Restoring settings to defaults..."); m_instance->restore_defaults(); } -std::string MultiSwitchProgramSession::check_validity() const{ - auto ScopeCheck = m_sanitizer.check_scope(); - return m_instance->check_validity(); -} JsonValue MultiSwitchProgramSession::to_json() const{ JsonObject obj = std::move(*m_instance->to_json().to_object()); obj["SwitchSetup"] = m_system_option.to_json(); @@ -124,10 +124,14 @@ void MultiSwitchProgramSession::run_program_instance(MultiSwitchProgramEnvironme std::deque> contexts; size_t consoles = m_system.active_consoles(); for (size_t console = 0; console < consoles; console++){ + size_t controllers = env.consoles[console].controllers(); + // Startup Checks - m_instance->start_program_controller_check( - m_system[console].controller(), console - ); + if (controllers > 0){ + m_instance->start_program_controller_check( + m_system[console].controller(0), console + ); + } m_instance->start_program_feedback_check( env.consoles[console], console, m_descriptor.feedback() @@ -138,7 +142,6 @@ void MultiSwitchProgramSession::run_program_instance(MultiSwitchProgramEnvironme ); // Attach all the controllers to the scope so they can be cancelled from the top. - size_t controllers = env.consoles[console].controllers(); for (size_t controller = 0; controller < controllers; controller++){ contexts.emplace_back(scope, env.consoles[console].controller(controller)); } @@ -213,7 +216,7 @@ void MultiSwitchProgramSession::internal_run_program(){ identifier(), m_descriptor.category(), m_descriptor.display_name(), - timestamp() + last_state_change() ); size_t consoles = m_system.active_consoles(); diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.h b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.h index 058bd39c24..c9e3a25f14 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_MultiSwitchProgramSession.h @@ -62,8 +62,8 @@ class MultiSwitchProgramSession final public: - virtual void restore_defaults() override; virtual std::string check_validity() const override; + virtual void restore_defaults() override; virtual JsonValue to_json() const override; virtual void load_json(const JsonValue& json) override; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SingleSwitchProgramSession.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SingleSwitchProgramSession.cpp index 9db92cd358..903b9172ba 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SingleSwitchProgramSession.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SingleSwitchProgramSession.cpp @@ -101,10 +101,14 @@ void SingleSwitchProgramSession::run_program_instance(SingleSwitchProgramEnviron } } + size_t controllers = env.console.controllers(); + // Startup Checks - m_instance->start_program_controller_check( - m_system.controller() - ); + if (controllers > 0){ + m_instance->start_program_controller_check( + m_system.controller(0) + ); + } m_instance->start_program_feedback_check( env.console, m_descriptor.feedback() @@ -115,7 +119,6 @@ void SingleSwitchProgramSession::run_program_instance(SingleSwitchProgramEnviron ); // Attach all the controllers to the scope so they can be cancelled from the top. - size_t controllers = env.console.controllers(); FixedLimitVector> contexts(controllers); for (size_t c = 0; c < controllers; c++){ contexts.emplace_back(scope, env.console.controller(c)); @@ -181,7 +184,7 @@ void SingleSwitchProgramSession::internal_run_program(){ identifier(), m_descriptor.category(), m_descriptor.display_name(), - timestamp() + last_state_change() ); CancellableHolder scope; SingleSwitchProgramEnvironment env( diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SwitchSystemSession.h b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SwitchSystemSession.h index 2cbb7b831d..baf09c332a 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SwitchSystemSession.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/NintendoSwitch_SwitchSystemSession.h @@ -31,7 +31,10 @@ class SwitchSystemOption; -class SwitchSystemSession final : public UiState{ +class SwitchSystemSession final + : public UiState +{ public: virtual bool try_shutdown() noexcept override; ~SwitchSystemSession();