Skip to content

Commit

Permalink
Add ability to execute console commands from file (#96)
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.

This allows flexible customization bundled in profiles that need it.

Syntax and effects are the same as commands were entered in native console.
Namely one command per line, space separated arguments.

Comments are supported in form of whole line comments starting with '#'
or ';'. Additionally inline comments are supported in form of
`command param # comment`

Example is Abzu where addding snippet `r.WaterSurfaceReflections 0` to
`user_script.txt` disables distracting visual effects.
  • Loading branch information
keton committed Mar 3, 2024
1 parent c20c76e commit 03237f9
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 1 deletion.
73 changes: 72 additions & 1 deletion 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 @@ -85,6 +86,11 @@ void CVarManager::on_pre_engine_tick(sdk::UGameEngine* engine, float delta) {
cvar->update();
cvar->freeze();
}

if(m_should_execute_console_script) {
execute_console_script(engine, user_script_txt_name.data());
m_should_execute_console_script = false;
}
}

void CVarManager::on_draw_ui() {
Expand Down Expand Up @@ -168,6 +174,9 @@ void CVarManager::on_config_load(const utility::Config& cfg, bool set_defaults)
}

// TODO: Add arbitrary cvars from the other configs the user can add.

// calling UEngine::exec here causes a crash, defer to on_pre_engine_tick()
m_should_execute_console_script = true;
}

void CVarManager::dump_commands() {
Expand Down Expand Up @@ -758,4 +767,66 @@ void CVarManager::CVarData::draw_ui() try {
}
} catch (...) {
ImGui::TextWrapped("Failed to read cvar data: %s", utility::narrow(m_name).c_str());
}
}

static inline void trim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch);
}));

s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch);
}).base(), s.end());
}

void CVarManager::execute_console_script(sdk::UGameEngine* engine, const std::string& filename) {
ZoneScopedN(__FUNCTION__);

if(!engine) {
spdlog::error("[execute_console_script] engine is null");
return;
}

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); ) {
trim(line);

// handle comments
if(line.starts_with('#') || line.starts_with(';')) {
continue;
}

if(line.contains('#')) {
line = line.substr(0, line.find_first_of('#'));
trim(line);
}

if(line.contains(';')) {
line = line.substr(0, line.find_first_of(';'));
trim(line);
}

if(line.length() == 0) {
continue;
}

spdlog::debug("[execute_console_script] Attempting to execute \"{}\"", line);
engine->exec(utility::widen(line));
}

spdlog::debug("[execute_console_script] done");
}
3 changes: 3 additions & 0 deletions src/mods/vr/CVarManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class CVarManager final : public ModComponent {
void dump_commands();
void spawn_console();

void execute_console_script(sdk::UGameEngine* engine, const std::string& filename);

bool is_hzbo_frozen_and_enabled() const {
if (m_hzbo == nullptr) {
return false;
Expand Down Expand Up @@ -194,6 +196,7 @@ class CVarManager final : public ModComponent {

bool m_wants_display_console{false};
bool m_native_console_spawned{false};
bool m_should_execute_console_script{false};

static inline std::vector<std::shared_ptr<CVarStandard>> s_default_standard_cvars {
// Bools
Expand Down

0 comments on commit 03237f9

Please sign in to comment.