Skip to content

Commit

Permalink
Add ability to execute console commands from file
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
keton committed Jan 26, 2024
1 parent 6e8457c commit 21330ea
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 35 deletions.
108 changes: 73 additions & 35 deletions src/mods/vr/CVarManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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__);
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<std::string> 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<std::wstring> 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();
Expand Down Expand Up @@ -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<std::string> 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<std::wstring> 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__);

Expand Down
2 changes: 2 additions & 0 deletions src/mods/vr/CVarManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 21330ea

Please sign in to comment.