From 8f8c6f17cbfa486e21da60ca4500413635a233ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lower?= Date: Sun, 7 Jan 2024 23:48:51 +0100 Subject: [PATCH] Add ability to execute console commands from file Certain game profiles need CVars from outside the scope provided by UEVR. Editing engine.ini makes profile sharing more complicated. This changeset adds ability to execute commands from `user_script.txt` located inside game profile folder. Syntax and effects are the same as commands were entered in console. Namely one command per line, space separated arguments. This allows flexible customization bundled in profiles that need it. Example is Abzu where addding snippet `r.WaterSurfaceReflections 0` to `user_script.txt` disables distracting visual effects. --- src/mods/vr/CVarManager.cpp | 108 ++++++++++++++++++++++++------------ src/mods/vr/CVarManager.hpp | 2 + 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/mods/vr/CVarManager.cpp b/src/mods/vr/CVarManager.cpp index 09b552dc..3612575b 100644 --- a/src/mods/vr/CVarManager.cpp +++ b/src/mods/vr/CVarManager.cpp @@ -20,6 +20,7 @@ constexpr std::string_view cvars_standard_txt_name = "cvars_standard.txt"; constexpr std::string_view cvars_data_txt_name = "cvars_data.txt"; +constexpr std::string_view user_script_txt_name = "user_script.txt"; CVarManager::CVarManager() { ZoneScopedN(__FUNCTION__); @@ -168,6 +169,7 @@ void CVarManager::on_config_load(const utility::Config& cfg, bool set_defaults) } // TODO: Add arbitrary cvars from the other configs the user can add. + execute_console_script(user_script_txt_name.data()); } void CVarManager::dump_commands() { @@ -324,41 +326,7 @@ void CVarManager::display_console() { if (m_console.input_buffer[0] != '\0') { const auto entire_command = std::string_view{ m_console.input_buffer.data() }; - // Split the command into the arguments via ' ' (space). - std::vector args{}; - - // Use getline - std::stringstream ss{ entire_command.data() }; - while (ss.good()) { - std::string arg{}; - std::getline(ss, arg, ' '); - args.push_back(arg); - } - - // Execute the command. - if (args.size() >= 2) { - auto object = console_manager->find(utility::widen(args[0])); - const auto is_command = object != nullptr && object->AsCommand() != nullptr; - - if (object != nullptr && !is_command) { - auto var = (sdk::IConsoleVariable*)object; - - GameThreadWorker::get().enqueue([var, value = utility::widen(args[1])]() { - var->Set(value.c_str()); - }); - } else if (object != nullptr && is_command) { - auto command = (sdk::IConsoleCommand*)object; - - std::vector widened_args{}; - for (auto i = 1; i < args.size(); ++i) { - widened_args.push_back(utility::widen(args[i])); - } - - GameThreadWorker::get().enqueue([command, widened_args]() { - command->Execute(widened_args); - }); - } - } + execute_console_cmd(entire_command.data()); m_console.history.push_back(m_console.input_buffer.data()); m_console.history_index = m_console.history.size(); @@ -403,6 +371,76 @@ void CVarManager::display_console() { } } +void CVarManager::execute_console_cmd(const std::string& cmd) { + ZoneScopedN(__FUNCTION__); + + const auto console_manager = sdk::FConsoleManager::get(); + if (console_manager == nullptr) { + spdlog::error("[execute_console_cmd] Failed to get console_manager"); + } + + std::vector args{}; + std::stringstream ss{ cmd }; + while (ss.good()) { + std::string arg{}; + std::getline(ss, arg, ' '); + args.push_back(arg); + } + + // Execute the command. + if (args.size() >= 2) { + auto object = console_manager->find(utility::widen(args[0])); + const auto is_command = object != nullptr && object->AsCommand() != nullptr; + + if (object != nullptr && !is_command) { + auto var = (sdk::IConsoleVariable*)object; + + GameThreadWorker::get().enqueue([var, value = utility::widen(args[1])]() { + var->Set(value.c_str()); + }); + } else if (object != nullptr && is_command) { + auto command = (sdk::IConsoleCommand*)object; + + std::vector widened_args{}; + for (auto i = 1; i < args.size(); ++i) { + widened_args.push_back(utility::widen(args[i])); + } + + GameThreadWorker::get().enqueue([command, widened_args]() { + command->Execute(widened_args); + }); + } + else { + spdlog::error("[execute_console_cmd] \"{}\" is not a valid console command", cmd); + } + } else { + spdlog::error("[execute_console_cmd] \"{}\" needs at least 2 space separated arguments", cmd); + } +} + +void CVarManager::execute_console_script(const std::string& filename) { + ZoneScopedN(__FUNCTION__); + + spdlog::info("[execute_console_script] Loading {}...", filename); + + const auto cscript_txt = Framework::get_persistent_dir(filename); + + if (!std::filesystem::exists(cscript_txt)) { + return; + } + + std::ifstream cscript_file(utility::widen(cscript_txt.string())); + + if (!cscript_file) { + spdlog::error("[execute_console_script] Failed to open file {}...", filename); + return; + } + + for (std::string line{}; getline(cscript_file, line); ) { + execute_console_cmd(line); + } +} + std::string CVarManager::CVar::get_key_name() { ZoneScopedN(__FUNCTION__); diff --git a/src/mods/vr/CVarManager.hpp b/src/mods/vr/CVarManager.hpp index 94cc6960..6e681ef3 100644 --- a/src/mods/vr/CVarManager.hpp +++ b/src/mods/vr/CVarManager.hpp @@ -22,6 +22,8 @@ class CVarManager final : public ModComponent { void dump_commands(); void spawn_console(); + void execute_console_cmd(const std::string& cmd); + void execute_console_script(const std::string& filename); bool is_hzbo_frozen_and_enabled() const { if (m_hzbo == nullptr) {