diff --git a/hecl/include/hecl/CVarManager.hpp b/hecl/include/hecl/CVarManager.hpp index acf729d22..ff92bfdd1 100755 --- a/hecl/include/hecl/CVarManager.hpp +++ b/hecl/include/hecl/CVarManager.hpp @@ -4,6 +4,7 @@ #include #include #include "CVar.hpp" +#include "hecl/SystemChar.hpp" namespace hecl { @@ -78,6 +79,8 @@ public: void setDeveloperMode(bool v, bool setDeserialized=false); bool restartRequired() const; + + void parseCommandLine(const std::vector& args); private: bool suppressDeveloper(); void restoreDeveloper(bool oldDeveloper); diff --git a/hecl/include/hecl/Console.hpp b/hecl/include/hecl/Console.hpp index 6e6cd6f64..8f0676368 100644 --- a/hecl/include/hecl/Console.hpp +++ b/hecl/include/hecl/Console.hpp @@ -14,11 +14,19 @@ class CVarManager; class CVar; struct SConsoleCommand { + enum class ECommandFlags + { + Normal = 0, + Cheat = (1 << 0), + Developer = (1 << 1) + }; std::string m_displayName; std::string m_helpString; std::string m_usage; std::function&)> m_func; + ECommandFlags m_flags; }; +ENABLE_BITWISE_ENUM(SConsoleCommand::ECommandFlags) class Console { @@ -77,9 +85,12 @@ private: CVar* m_conHeight; float m_cachedConSpeed; float m_cachedConHeight; + bool m_showCursor = true; + float m_cursorTime = 0.f; public: Console(CVarManager*); - void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function&)>&& func); + void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function&)>&& func, SConsoleCommand::ECommandFlags cmdFlags = SConsoleCommand::ECommandFlags::Normal); + void unregisterCommand(std::string_view name); void executeString(const std::string& strToExec); diff --git a/hecl/lib/CVar.cpp b/hecl/lib/CVar.cpp index 9cf5c41b3..6a3d3c8c0 100755 --- a/hecl/lib/CVar.cpp +++ b/hecl/lib/CVar.cpp @@ -127,7 +127,7 @@ CVar::CVar(std::string_view name, int value, std::string_view help, CVar::EFlags std::string CVar::help() const { return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") + - (isReadOnly() ? "[ReadOnly]" : "")); + (isReadOnly() ? " [ReadOnly]" : "")); } atVec4f CVar::toVec4f(bool* isValid) const diff --git a/hecl/lib/CVarManager.cpp b/hecl/lib/CVarManager.cpp index 29cf05036..5ac9d5f0a 100755 --- a/hecl/lib/CVarManager.cpp +++ b/hecl/lib/CVarManager.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include namespace hecl { @@ -12,6 +14,7 @@ CVar* com_developer = nullptr; CVar* com_configfile = nullptr; CVar* com_enableCheats = nullptr; +static const std::regex cmdLineRegex("\\+(\\w+)=([\\w\\.\\-]+)"); CVarManager* CVarManager::m_instance = nullptr; static logvisor::Module CVarLog("CVarManager"); @@ -269,11 +272,42 @@ bool CVarManager::restartRequired() const return false; } +void CVarManager::parseCommandLine(const std::vector& args) +{ + bool oldDeveloper = suppressDeveloper(); + std::string developerName = com_developer->name().data(); + athena::utility::tolower(developerName); + for (const SystemString& arg : args) + { + if (arg[0] == _S('+')) + { + std::string tmp = SystemUTF8Conv(arg).c_str(); + + std::smatch matches; + if (std::regex_match(tmp, matches, cmdLineRegex)) + { + std::string cvarName = matches[1].str(); + std::string cvarValue = matches[2].str(); + if (CVar* cv = findCVar(cvarName)) + { + cv->fromLiteralToType(cvarValue); + athena::utility::tolower(cvarName); + if (developerName == cvarName) + /* Make sure we're not overriding developer mode when we restore */ + oldDeveloper = com_developer->toBoolean(); + } + } + } + } + + restoreDeveloper(oldDeveloper); +} + bool CVarManager::suppressDeveloper() { bool oldDeveloper = com_developer->toBoolean(); CVarUnlocker unlock(com_developer); - com_developer->fromBoolean(false); + com_developer->fromBoolean(true); return oldDeveloper; } diff --git a/hecl/lib/Console.cpp b/hecl/lib/Console.cpp index a6d03c330..30e5b0509 100644 --- a/hecl/lib/Console.cpp +++ b/hecl/lib/Console.cpp @@ -24,12 +24,20 @@ Console::Console(CVarManager* cvarMgr) } -void Console::registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function &)>&& func) +void Console::registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function &)>&& func, SConsoleCommand::ECommandFlags cmdFlags) { std::string lowName = name.data(); athena::utility::tolower(lowName); if (m_commands.find(lowName) == m_commands.end()) - m_commands[lowName] = SConsoleCommand{name.data(), helpText.data(), usage.data(), std::move(func)}; + m_commands[lowName] = SConsoleCommand{name.data(), helpText.data(), usage.data(), std::move(func), cmdFlags}; +} + +void Console::unregisterCommand(std::string_view name) +{ + std::string lowName = name.data(); + athena::utility::tolower(lowName); + if (m_commands.find(lowName) != m_commands.end()) + m_commands.erase(m_commands.find(lowName)); } void Console::executeString(const std::string& str) @@ -57,7 +65,21 @@ void Console::executeString(const std::string& str) std::string lowComName = commandName; athena::utility::tolower(lowComName); if (m_commands.find(lowComName) != m_commands.end()) + { + const SConsoleCommand& cmd = m_commands[lowComName]; + if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Developer) && !com_developer->toBoolean()) + { + report(Level::Error, "This command can only be executed in developer mode", commandName.c_str()); + return; + } + + if (bool(cmd.m_flags & SConsoleCommand::ECommandFlags::Cheat) && !com_enableCheats->toBoolean()) + { + report(Level::Error, "This command can only be executed with cheats enabled", commandName.c_str()); + return; + } m_commands[lowComName].m_func(this, args); + } else report(Level::Error, "Command '%s' is not valid!", commandName.c_str()); } @@ -102,7 +124,12 @@ void Console::report(Level level, const char* fmt, va_list list) { char tmp[2048]; vsnprintf(tmp, 2048, fmt, list); - m_log.emplace_back(std::string(tmp), level); + std::vector lines = athena::utility::split(tmp, '\n'); + for (const std::string& line : lines) + { + std::string v = athena::utility::sprintf("%s", line.c_str()); + m_log.emplace_back(v, level); + } printf("%s\n", tmp); } @@ -258,8 +285,8 @@ void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, b m_cursorPosition = -1; m_commandHistory.insert(m_commandHistory.begin(), m_commandString); m_commandString.clear(); - //m_showCursor = true; - //m_cursorTime = 0; + m_showCursor = true; + m_cursorTime = 0.f; break; } case boo::ESpecialKey::Left: @@ -272,8 +299,8 @@ void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, b else m_cursorPosition--; - //m_showCursor = true; - //m_cursorTime = 0; + m_showCursor = true; + m_cursorTime = 0.f; break; } case boo::ESpecialKey::Right: @@ -295,8 +322,8 @@ void Console::handleSpecialKeyDown(boo::ESpecialKey sp, boo::EModifierKey mod, b else m_cursorPosition++; - // m_showCursor = true; - // m_cursorTime = 0; + m_showCursor = true; + m_cursorTime = 0.f; break; } @@ -350,16 +377,24 @@ void Console::LogVisorAdapter::report(const char* modName, logvisor::Level sever { char tmp[2048]; vsnprintf(tmp, 2048, format, ap); - std::string v = athena::utility::sprintf("[%s] %s", modName, tmp); - m_con->m_log.emplace_back(v, Console::Level(severity)); + std::vector lines = athena::utility::split(tmp, '\n'); + for (const std::string& line : lines) + { + std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str()); + m_con->m_log.emplace_back(v, Console::Level(severity)); + } } void Console::LogVisorAdapter::report(const char* modName, logvisor::Level severity, const wchar_t* format, va_list ap) { wchar_t tmp[2048]; vswprintf(tmp, 2048, format, ap); - std::string v = athena::utility::sprintf("[%s] %s", modName, athena::utility::wideToUtf8(tmp).c_str()); - m_con->m_log.emplace_back(v, Console::Level(severity)); + std::vector lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n'); + for (const std::string& line : lines) + { + std::string v = athena::utility::sprintf("[%s] %s", modName, line.c_str()); + m_con->m_log.emplace_back(v, Console::Level(severity)); + } } void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level severity, const char* file, unsigned linenum, const char* format, va_list ap) @@ -374,8 +409,12 @@ void Console::LogVisorAdapter::reportSource(const char* modName, logvisor::Level { wchar_t tmp[2048]; vswprintf(tmp, 2048, format, ap); - std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, athena::utility::wideToUtf8(tmp).c_str(), file, linenum); - m_con->m_log.emplace_back(v, Console::Level(severity)); + std::vector lines = athena::utility::split(athena::utility::wideToUtf8(tmp), '\n'); + for (const std::string& line : lines) + { + std::string v = athena::utility::sprintf("[%s] %s %s:%i", modName, line.c_str(), file, linenum); + m_con->m_log.emplace_back(v, Console::Level(severity)); + } } void Console::dumpLog()