From e247733d04bcad18e95e2467f531aa4a3eee1c55 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Thu, 11 Jan 2018 01:35:27 -0800 Subject: [PATCH] Initial `Console` implementation --- hecl/include/hecl/CVar.hpp | 4 +- hecl/include/hecl/CVarCommons.hpp | 6 +- hecl/include/hecl/CVarManager.hpp | 6 ++ hecl/include/hecl/Console.hpp | 52 ++++++++++++ hecl/lib/CMakeLists.txt | 2 + hecl/lib/CVar.cpp | 1 + hecl/lib/CVarManager.cpp | 27 ++++++ hecl/lib/Console.cpp | 134 ++++++++++++++++++++++++++++++ 8 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 hecl/include/hecl/Console.hpp create mode 100644 hecl/lib/Console.cpp diff --git a/hecl/include/hecl/CVar.hpp b/hecl/include/hecl/CVar.hpp index d93e749c6..f3427bc23 100755 --- a/hecl/include/hecl/CVar.hpp +++ b/hecl/include/hecl/CVar.hpp @@ -31,7 +31,8 @@ enum EFlags Hidden = (1 << 5), ReadOnly = (1 << 6), Archive = (1 << 7), - Modified = (1 << 8) + Modified = (1 << 8), + ModifyRestart = (1 << 9) /*!< If this bit is set, any modification will inform the user that a restart is required */ }; ENABLE_BITWISE_ENUM(EFlags) @@ -98,6 +99,7 @@ public: bool isLiteral() const { return m_type == EType::Literal; } bool isVec4f() const { return m_type == EType::Vec4f; } bool isModified() const; + bool modificationRequiresRestart() const; bool isReadOnly() const; bool isCheat() const; bool isHidden() const; diff --git a/hecl/include/hecl/CVarCommons.hpp b/hecl/include/hecl/CVarCommons.hpp index fd0fb5e97..0f29f2a42 100644 --- a/hecl/include/hecl/CVarCommons.hpp +++ b/hecl/include/hecl/CVarCommons.hpp @@ -30,13 +30,13 @@ public: { m_graphicsApi = m_mgr.findOrMakeCVar("graphicsApi"sv, "API to use for rendering graphics"sv, - DEFAULT_GRAPHICS_API, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); + DEFAULT_GRAPHICS_API, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); m_drawSamples = m_mgr.findOrMakeCVar("drawSamples"sv, "Number of MSAA samples to use for render targets"sv, - 1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); + 1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); m_texAnisotropy = m_mgr.findOrMakeCVar("texAnisotropy"sv, "Number of anisotropic samples to use for sampling textures"sv, - 1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive); + 1, hecl::CVar::EFlags::System | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ModifyRestart); } std::string getGraphicsApi() const diff --git a/hecl/include/hecl/CVarManager.hpp b/hecl/include/hecl/CVarManager.hpp index b4f6d39d4..68854b0db 100755 --- a/hecl/include/hecl/CVarManager.hpp +++ b/hecl/include/hecl/CVarManager.hpp @@ -71,6 +71,12 @@ public: static CVarManager* instance(); + void list(const std::vector& args); + void setCVar(const std::vector& args); + void getCVar(const std::vector& args); + + + bool restartRequired() const; private: bool suppressDeveloper(); void restoreDeveloper(bool oldDeveloper); diff --git a/hecl/include/hecl/Console.hpp b/hecl/include/hecl/Console.hpp new file mode 100644 index 000000000..ae9224128 --- /dev/null +++ b/hecl/include/hecl/Console.hpp @@ -0,0 +1,52 @@ +#ifndef __URDE_CONSOLE_HPP__ +#define __URDE_CONSOLE_HPP__ + +#include +#include +#include +#include + +namespace hecl +{ + +struct SConsoleCommand +{ + std::string m_displayName; + std::string m_helpString; + std::string m_usage; + std::function&)> m_func; +}; + +class Console +{ +public: + static Console* m_instance; + enum class Level + { + Info, /**< Non-error informative message */ + Warning, /**< Non-error warning message */ + Error, /**< Recoverable error message */ + Fatal /**< Non-recoverable error message (Kept for compatibility with logvisor) */ + }; +private: + std::unordered_map m_commands; + std::vector> m_log; +public: + Console(class CVarManager*); + void registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function&)>&& func); + + void executeString(const std::string& strToExec); + + void help(const std::vector& args); + void listCommands(const std::vector& args); + bool commandExists(std::string_view cmd); + + + void print(Level level, const char *fmt, va_list list); + void print(Level, const char* fmt, ...); + void dumpLog(); + static Console* instance(); +}; +} + +#endif // __URDE_CONSOLE_HPP__ diff --git a/hecl/lib/CMakeLists.txt b/hecl/lib/CMakeLists.txt index 5cad1c6d6..839fb582f 100644 --- a/hecl/lib/CMakeLists.txt +++ b/hecl/lib/CMakeLists.txt @@ -27,6 +27,7 @@ endif() set(HECL_HEADERS ../include/hecl/CVar.hpp ../include/hecl/CVarManager.hpp + ../include/hecl/Console.hpp ../include/hecl/CVarCommons.hpp ../include/hecl/hecl.hpp ../include/hecl/FourCC.hpp @@ -56,6 +57,7 @@ set(COMMON_SOURCES HumanizeNumber.cpp CVar.cpp CVarManager.cpp + Console.cpp ClientProcess.cpp SteamFinder.cpp WideStringConvert.cpp diff --git a/hecl/lib/CVar.cpp b/hecl/lib/CVar.cpp index 376fc2234..7a3312684 100755 --- a/hecl/lib/CVar.cpp +++ b/hecl/lib/CVar.cpp @@ -352,6 +352,7 @@ bool CVar::fromLiteral(std::wstring_view val) } bool CVar::isModified() const { return int(m_flags & EFlags::Modified) != 0;} +bool CVar::modificationRequiresRestart() const { return int(m_flags & EFlags::ModifyRestart) != 0; } bool CVar::isReadOnly() const { return int(m_flags & EFlags::ReadOnly) != 0; } diff --git a/hecl/lib/CVarManager.cpp b/hecl/lib/CVarManager.cpp index fa974c5d2..ccc19780a 100755 --- a/hecl/lib/CVarManager.cpp +++ b/hecl/lib/CVarManager.cpp @@ -1,5 +1,6 @@ #include "hecl/CVarManager.hpp" #include "hecl/CVar.hpp" +#include "hecl/Console.hpp" #include #include #include @@ -175,6 +176,32 @@ CVarManager* CVarManager::instance() return m_instance; } +void CVarManager::list(const std::vector &args) +{ + +} + +void CVarManager::setCVar(const std::vector &args) +{ + +} + +void CVarManager::getCVar(const std::vector &args) +{ + +} + +bool CVarManager::restartRequired() const +{ + for (const auto& cv : m_cvars) + { + if (cv.second->isModified() && cv.second->modificationRequiresRestart()) + return true; + } + + return false; +} + bool CVarManager::suppressDeveloper() { bool oldDeveloper = com_developer->toBoolean(); diff --git a/hecl/lib/Console.cpp b/hecl/lib/Console.cpp new file mode 100644 index 000000000..c804b67e0 --- /dev/null +++ b/hecl/lib/Console.cpp @@ -0,0 +1,134 @@ +#include "hecl/Console.hpp" +#include "hecl/CVarManager.hpp" +#include "hecl/CVar.hpp" +#include "athena/Utility.hpp" + +namespace hecl +{ +Console* Console::m_instance = nullptr; +Console::Console(CVarManager* cvarMgr) +{ + m_instance = this; + registerCommand("help", "Prints information about a given function", "", std::bind(&Console::help, this, std::placeholders::_1)); + registerCommand("listCommands", "Prints a list of all available Commands", "", std::bind(&Console::listCommands, this, std::placeholders::_1)); + registerCommand("listCVars", "Lists all available CVars", "", std::bind(&CVarManager::list, cvarMgr, std::placeholders::_1)); + registerCommand("setCVar", "Sets a given Console Variable to the specified value", " ", std::bind(&CVarManager::setCVar, cvarMgr, std::placeholders::_1)); + registerCommand("getCVar", "Prints the value stored in the specified Console Variable", "", std::bind(&CVarManager::getCVar, cvarMgr, std::placeholders::_1)); +} + +void Console::registerCommand(std::string_view name, std::string_view helpText, std::string_view usage, const std::function &)>&& func) +{ + 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)}; +} + +void Console::executeString(const std::string& str) +{ + if (str.empty()) + return; + + /* First let's split semi-colon delimited commands */ + std::vector commands = athena::utility::split(str, ';'); + + if (commands.empty()) + return; + + for (std::string command : commands) + { + command = athena::utility::trim(command); + std::vector args = athena::utility::split(command, ' '); + + if (args.empty()) + continue; + + std::string commandName = args[0]; + args.erase(args.begin()); + + std::string lowComName = commandName; + athena::utility::tolower(lowComName); + if (m_commands.find(lowComName) != m_commands.end()) + m_commands[lowComName].m_func(args); + else + print(Level::Error, "Command '%s' is not valid!", commandName.c_str()); + } +} + +void Console::help(const std::vector& args) +{ + if (args.empty()) + { + print(Level::Info, "Expected usage: help "); + return; + } + std::string cmd = args.front(); + athena::utility::tolower(cmd); + auto it = m_commands.find(cmd); + if (it == m_commands.end()) + { + print(Level::Error, "No such command '%s'", args.front().c_str()); + return; + } + + print(Level::Info, "%s: %s", it->second.m_displayName.c_str(), it->second.m_helpString.c_str()); + if (!it->second.m_usage.empty()) + print(Level::Info, "Usage: %s %s", it->second.m_displayName.c_str(), it->second.m_usage.c_str()); +} + +void Console::listCommands(const std::vector& /*args*/) +{ + for (const auto& comPair : m_commands) + print(Level::Info, "'%s': %s", comPair.second.m_displayName.c_str(), comPair.second.m_helpString.c_str()); +} + +bool Console::commandExists(std::string_view cmd) +{ + std::string cmdName = cmd.data(); + athena::utility::tolower(cmdName); + + return m_commands.find(cmdName) != m_commands.end(); +} + +void Console::print(Level level, const char* fmt, va_list list) +{ + char tmp[2048]; + vsnprintf(tmp, 2048, fmt, list); + m_log.emplace_back(std::string(tmp), level); +} + +void Console::print(Level level, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + print(level, fmt, ap); + va_end(ap); +} + +void Console::dumpLog() +{ + for (const auto& l : m_log) + { + switch(l.second) + { + case Level::Info: + printf("%s\n", l.first.c_str()); + break; + case Level::Warning: + printf("[Warning] %s\n", l.first.c_str()); + break; + case Level::Error: + printf("[ Error ] %s\n", l.first.c_str()); + break; + case Level::Fatal: + printf("[ Fatal ] %s\n", l.first.c_str()); + break; + } + } +} + +Console* Console::instance() +{ + return m_instance; +} +}