From 33a1d790e8a5f67c73d0eef4a141f936345f104f Mon Sep 17 00:00:00 2001 From: german77 Date: Mon, 5 Jul 2021 20:58:52 -0500 Subject: [PATCH] input_common/tas: Document the main class --- src/common/settings.h | 11 +- src/input_common/tas/tas_input.cpp | 3 +- src/input_common/tas/tas_input.h | 108 ++++++++++++++++++ src/input_common/tas/tas_poller.h | 4 +- src/yuzu/configuration/config.cpp | 39 +++---- .../configuration/configure_filesystem.cpp | 1 - .../configure_input_player_widget.cpp | 22 ++-- src/yuzu/configuration/configure_tas.cpp | 16 +-- 8 files changed, 153 insertions(+), 51 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index cf12c325c5..c53d5acc3d 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -512,17 +512,14 @@ struct Values { "motion_device"}; BasicSetting udp_input_servers{"127.0.0.1:26760", "udp_input_servers"}; - BasicSetting pause_tas_on_load { false, "pause_tas_on_load" }; - BasicSetting tas_enable{ false, "tas_enable" }; - BasicSetting tas_loop{ false, "tas_loop" }; - BasicSetting tas_swap_controllers{ false, "tas_swap_controllers" }; - BasicSetting is_cpu_boosted{ false, "is_cpu_boosted" }; -" }; + BasicSetting pause_tas_on_load{true, "pause_tas_on_load"}; + BasicSetting tas_enable{false, "tas_enable"}; + BasicSetting tas_loop{false, "tas_loop"}; + BasicSetting tas_swap_controllers{true, "tas_swap_controllers"}; BasicSetting mouse_panning{false, "mouse_panning"}; BasicRangedSetting mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"}; BasicSetting mouse_enabled{false, "mouse_enabled"}; - std::string mouse_device; MouseButtonsRaw mouse_buttons; diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp index aceb13adcc..877d350884 100644 --- a/src/input_common/tas/tas_input.cpp +++ b/src/input_common/tas/tas_input.cpp @@ -14,6 +14,7 @@ namespace TasInput { +// Supported keywords and buttons from a TAS file constexpr std::array, 20> text_to_tas_button = { std::pair{"KEY_A", TasButton::BUTTON_A}, {"KEY_B", TasButton::BUTTON_B}, @@ -214,7 +215,7 @@ void Tas::UpdateThread() { } } } else { - is_running = Settings::values.tas_loop; + is_running = Settings::values.tas_loop.GetValue(); current_command = 0; tas_data.fill({}); if (!is_running) { diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h index e1f3512511..52d000db4a 100644 --- a/src/input_common/tas/tas_input.h +++ b/src/input_common/tas/tas_input.h @@ -11,6 +11,38 @@ #include "core/frontend/input.h" #include "input_common/main.h" +/* +To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below +Emulation -> Configure TAS. The file itself has normal text format and has to be called +script0-1.txt for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players). + +A script file has the same format as TAS-nx uses, so final files will look like this: + +1 KEY_B 0;0 0;0 +6 KEY_ZL 0;0 0;0 +41 KEY_ZL;KEY_Y 0;0 0;0 +43 KEY_X;KEY_A 32767;0 0;0 +44 KEY_A 32767;0 0;0 +45 KEY_A 32767;0 0;0 +46 KEY_A 32767;0 0;0 +47 KEY_A 32767;0 0;0 + +After placing the file at the correct location, it can be read into Yuzu with the (default) hotkey +CTRL+F6 (refresh). In the bottom left corner, it will display the amount of frames the script file +has. Playback can be started or stopped using CTRL+F5. + +However, for playback to actually work, the correct input device has to be selected: In the Controls +menu, select TAS from the device list for the controller that the script should be played on. + +Recording a new script file is really simple: Just make sure that the proper device (not TAS) is +connected on P1, and press CTRL+F7 to start recording. When done, just press the same keystroke +again (CTRL+F7). The new script will be saved at the location previously selected, as the filename +record.txt. + +For debugging purposes, the common controller debugger can be used (View -> Debugging -> Controller +P1). +*/ + namespace TasInput { constexpr size_t PLAYER_NUMBER = 8; @@ -64,12 +96,26 @@ public: Tas(); ~Tas(); + // Changes the input status that will be stored in each frame void RecordInput(u32 buttons, const std::array, 2>& axes); + + // Main loop that records or executes input void UpdateThread(); + // Sets the flag to start or stop the TAS command excecution and swaps controllers profiles void StartStop(); + + // Sets the flag to reload the file and start from the begining in the next update void Reset(); + + /** + * Sets the flag to enable or disable recording of inputs + * @return Returns true if the current recording status is enabled + */ bool Record(); + + // Saves contents of record_commands on a file if overwrite is enabled player 1 will be + // overwritten with the recorded commands void SaveRecording(bool overwrite_file); /** @@ -80,7 +126,11 @@ public: * Total length of script file currently loaded or amount of frames (so far) for Recording */ std::tuple GetStatus() const; + + // Retuns an array of the default button mappings InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const; + + // Retuns an array of the default analog mappings InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const; [[nodiscard]] const TasData& GetTasState(std::size_t pad) const; @@ -90,23 +140,81 @@ private: TasAnalog l_axis{}; TasAnalog r_axis{}; }; + + // Loads TAS files from all players void LoadTasFiles(); + + // Loads TAS file from the specified player void LoadTasFile(size_t player_index); + + // Writes a TAS file from the recorded commands void WriteTasFile(std::u8string file_name); + + /** + * Parses a string containing the axis values with the following format "x;y" + * X and Y have a range from -32767 to 32767 + * @return Returns a TAS analog object with axis values with range from -1.0 to 1.0 + */ TasAnalog ReadCommandAxis(const std::string& line) const; + + /** + * Parses a string containing the button values with the following format "a;b;c;d..." + * Each button is represented by it's text format specified in text_to_tas_button array + * @return Returns a u32 with each bit representing the status of a button + */ u32 ReadCommandButtons(const std::string& line) const; + + /** + * Converts an u32 containing the button status into the text equivalent + * @return Returns a string with the name of the buttons to be written to the file + */ std::string WriteCommandButtons(u32 data) const; + + /** + * Converts an TAS analog object containing the axis status into the text equivalent + * @return Returns a string with the value of the axis to be written to the file + */ std::string WriteCommandAxis(TasAnalog data) const; + // Inverts the Y axis polarity std::pair FlipAxisY(std::pair old); + /** + * Converts an u32 containing the button status into the text equivalent + * @return Returns a string with the name of the buttons to be printed on console + */ std::string DebugButtons(u32 buttons) const; + + /** + * Converts an TAS analog object containing the axis status into the text equivalent + * @return Returns a string with the value of the axis to be printed on console + */ std::string DebugJoystick(float x, float y) const; + + /** + * Converts the given TAS status into the text equivalent + * @return Returns a string with the value of the TAS status to be printed on console + */ std::string DebugInput(const TasData& data) const; + + /** + * Converts the given TAS status of multiple players into the text equivalent + * @return Returns a string with the value of the status of all TAS players to be printed on + * console + */ std::string DebugInputs(const std::array& arr) const; + + /** + * Converts an u32 containing the button status into the text equivalent + * @return Returns a string with the name of the buttons + */ std::string ButtonsToString(u32 button) const; + // Stores current controller configuration and sets a TAS controller for every active controller + // to the current config void SwapToTasController(); + + // Sets the stored controller configuration to the current config void SwapToStoredController(); size_t script_length{0}; diff --git a/src/input_common/tas/tas_poller.h b/src/input_common/tas/tas_poller.h index 1bc0d173ba..09e426cef5 100644 --- a/src/input_common/tas/tas_poller.h +++ b/src/input_common/tas/tas_poller.h @@ -11,7 +11,7 @@ namespace InputCommon { /** - * A button device factory representing a mouse. It receives mouse events and forward them + * A button device factory representing a tas bot. It receives tas events and forward them * to all button devices it created. */ class TasButtonFactory final : public Input::Factory { @@ -29,7 +29,7 @@ private: std::shared_ptr tas_input; }; -/// An analog device factory that creates analog devices from mouse +/// An analog device factory that creates analog devices from tas class TasAnalogFactory final : public Input::Factory { public: explicit TasAnalogFactory(std::shared_ptr tas_input_); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index a0dfa06dfa..27b67fd9e9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -567,8 +567,10 @@ void Config::ReadControlValues() { Settings::values.mouse_panning = false; ReadBasicSetting(Settings::values.mouse_panning_sensitivity); - ReadBasicSetting(Settings::values.tas_enable = false); - ReadBasicSetting(Settings::values.tas_reset = false); + ReadBasicSetting(Settings::values.tas_enable); + ReadBasicSetting(Settings::values.tas_loop); + ReadBasicSetting(Settings::values.tas_swap_controllers); + ReadBasicSetting(Settings::values.pause_tas_on_load); ReadGlobalSetting(Settings::values.use_docked_mode); @@ -667,20 +669,16 @@ void Config::ReadDataStorageValues() { QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) .toString() .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::TASFile, - qt_config - ->value(QStringLiteral("tas_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile))) - .toString() - .toStdString()); - - ReadBasicSetting(Settings::values.pauseTasOnLoad); + FS::SetYuzuPath(FS::YuzuPath::TASDir, + qt_config + ->value(QStringLiteral("tas_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) + .toString() + .toStdString()); ReadBasicSetting(Settings::values.gamecard_inserted); ReadBasicSetting(Settings::values.gamecard_current_game); ReadBasicSetting(Settings::values.gamecard_path); - qt_config->endGroup(); } @@ -1205,11 +1203,11 @@ void Config::SaveControlValues() { WriteBasicSetting(Settings::values.emulate_analog_keyboard); WriteBasicSetting(Settings::values.mouse_panning_sensitivity); - WriteSetting(QStringLiteral("enable_tas"), Settings::values.tas_enable, false); - WriteSetting(QStringLiteral("loop_tas"), Settings::values.tas_loop, false); - WriteSetting(QStringLiteral("swap_tas_controllers"), Settings::values.tas_swap_controllers, - true); - WriteSetting(QStringLiteral("tas_pause_on_load"), Settings::values.pause_tas_on_load, true); + WriteBasicSetting(Settings::values.tas_enable); + WriteBasicSetting(Settings::values.tas_loop); + WriteBasicSetting(Settings::values.tas_swap_controllers); + WriteBasicSetting(Settings::values.pause_tas_on_load); + qt_config->endGroup(); } @@ -1237,10 +1235,9 @@ void Config::SaveDataStorageValues() { WriteSetting(QStringLiteral("dump_directory"), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - WriteSetting(QStringLiteral("tas_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASFile))); - WriteSetting(QStringLiteral("tas_pause_on_load"), Settings::values.pauseTasOnLoad, true); + WriteSetting(QStringLiteral("tas_directory"), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), + QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); WriteBasicSetting(Settings::values.gamecard_inserted); WriteBasicSetting(Settings::values.gamecard_current_game); diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 013de02dbc..9cb3178229 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -52,7 +52,6 @@ void ConfigureFilesystem::setConfiguration() { ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted.GetValue()); ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game.GetValue()); - ui->tas_pause_on_load->setChecked(Settings::values.pauseTasOnLoad); ui->dump_exefs->setChecked(Settings::values.dump_exefs.GetValue()); ui->dump_nso->setChecked(Settings::values.dump_nso.GetValue()); diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index ba3720c033..38c59263cb 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -222,23 +222,23 @@ void PlayerControlPreview::UpdateInput() { if (input_changed) { update(); - ControllerInput input{ - .axis_values = - {std::pair{axis_values[Settings::NativeAnalog::LStick].value.x(), - axis_values[Settings::NativeAnalog::LStick].value.y()}, - std::pair{axis_values[Settings::NativeAnalog::RStick].value.x(), - axis_values[Settings::NativeAnalog::RStick].value.y()}}, - .button_values = button_values, - .changed = true, - }; - if (controller_callback.input != nullptr) { + ControllerInput input{ + .axis_values = {std::pair{ + axis_values[Settings::NativeAnalog::LStick].value.x(), + axis_values[Settings::NativeAnalog::LStick].value.y()}, + std::pair{ + axis_values[Settings::NativeAnalog::RStick].value.x(), + axis_values[Settings::NativeAnalog::RStick].value.y()}}, + .button_values = button_values, + .changed = true, + }; controller_callback.input(std::move(input)); } } if (controller_callback.update != nullptr) { - controller_callback.update(std::move(true)); + controller_callback.update(true); } if (mapping_active) { diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index f2f91d84a6..00d6c1ba5c 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp @@ -30,18 +30,18 @@ ConfigureTasDialog::~ConfigureTasDialog() = default; void ConfigureTasDialog::LoadConfiguration() { ui->tas_path_edit->setText( QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::TASDir))); - ui->tas_enable->setChecked(Settings::values.tas_enable); - ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers); - ui->tas_loop_script->setChecked(Settings::values.tas_loop); - ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load); + ui->tas_enable->setChecked(Settings::values.tas_enable.GetValue()); + ui->tas_control_swap->setChecked(Settings::values.tas_swap_controllers.GetValue()); + ui->tas_loop_script->setChecked(Settings::values.tas_loop.GetValue()); + ui->tas_pause_on_load->setChecked(Settings::values.pause_tas_on_load.GetValue()); } void ConfigureTasDialog::ApplyConfiguration() { Common::FS::SetYuzuPath(Common::FS::YuzuPath::TASDir, ui->tas_path_edit->text().toStdString()); - Settings::values.tas_enable = ui->tas_enable->isChecked(); - Settings::values.tas_swap_controllers = ui->tas_control_swap->isChecked(); - Settings::values.tas_loop = ui->tas_loop_script->isChecked(); - Settings::values.pause_tas_on_load = ui->tas_pause_on_load->isChecked(); + Settings::values.tas_enable.SetValue(ui->tas_enable->isChecked()); + Settings::values.tas_swap_controllers.SetValue(ui->tas_control_swap->isChecked()); + Settings::values.tas_loop.SetValue(ui->tas_loop_script->isChecked()); + Settings::values.pause_tas_on_load.SetValue(ui->tas_pause_on_load->isChecked()); } void ConfigureTasDialog::SetDirectory(DirectoryTarget target, QLineEdit* edit) {