From 47fb36def66d68cceb675beb57cf2c373a38f276 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 2 Dec 2015 11:11:10 -1000 Subject: [PATCH] Moved CVars to HECL --- hecl/extern/libBoo | 2 +- hecl/include/HECL/CVar.hpp | 151 ++++++++++++ hecl/include/HECL/CVarManager.hpp | 68 +++++ hecl/lib/CMakeLists.txt | 6 + hecl/lib/CVar.cpp | 397 ++++++++++++++++++++++++++++++ hecl/lib/CVarManager.cpp | 181 ++++++++++++++ 6 files changed, 804 insertions(+), 1 deletion(-) create mode 100755 hecl/include/HECL/CVar.hpp create mode 100755 hecl/include/HECL/CVarManager.hpp create mode 100755 hecl/lib/CVar.cpp create mode 100755 hecl/lib/CVarManager.cpp diff --git a/hecl/extern/libBoo b/hecl/extern/libBoo index cd4df5cb2..13cf33d75 160000 --- a/hecl/extern/libBoo +++ b/hecl/extern/libBoo @@ -1 +1 @@ -Subproject commit cd4df5cb270be177740bd6ab1fed60f68481002b +Subproject commit 13cf33d75f60b8929e5d1794a3e597722d084ed0 diff --git a/hecl/include/HECL/CVar.hpp b/hecl/include/HECL/CVar.hpp new file mode 100755 index 000000000..cb77a466f --- /dev/null +++ b/hecl/include/HECL/CVar.hpp @@ -0,0 +1,151 @@ +#ifndef CVAR_HPP +#define CVAR_HPP + +#include +#include +#include +#include +#include + +namespace HECL +{ +namespace DNACVAR +{ +enum class EType : atUint8 +{ + Boolean, + Integer, + Float, + Literal, + Vec4f +}; + +enum EFlags +{ + All = -1, // NOTE: is this really necessary? It seems rather overkill + System = (1 << 0), + Game = (1 << 1), + Editor = (1 << 2), + Gui = (1 << 3), + Cheat = (1 << 4), + Hidden = (1 << 5), + ReadOnly = (1 << 6), + Archive = (1 << 7), + Modified = (1 << 8) +}; +ENABLE_BITWISE_ENUM(EFlags) + +class CVar : public Athena::io::DNAYaml +{ +public: + DECL_YAML + String<-1> m_name; + String<-1> m_value; + Value m_type; +}; + +struct CVarContainer : public Athena::io::DNAYaml +{ + DECL_YAML + Value magic = 'CVAR'; + Value cvarCount; + Vector cvars; +}; + +} + +class CVarManager; +class CVar : protected DNACVAR::CVar +{ + friend class CVarManager; + Delete _d; + +public: + typedef std::function ListenerFunc; + + using EType = DNACVAR::EType; + using EFlags = DNACVAR::EFlags; + + CVar(const std::string& name, const std::string& value, const std::string& help, EType type, EFlags flags, CVarManager& parent); + CVar(const std::string& name, const std::string& value, const std::string& help, EFlags flags, CVarManager& parent); + CVar(const std::string& name, float value, const std::string& help, EFlags flags, CVarManager& parent); + CVar(const std::string& name, bool value, const std::string& help, EFlags flags, CVarManager& parent); + CVar(const std::string& name, int value, const std::string& help, EFlags flags, CVarManager& parent); + CVar(const std::string& name, const atVec4f& value, const std::string& help, EFlags flags, CVarManager& parent); + + + const std::string& name() const { return m_name; } + std::string help() const; + + atVec4f toVec4f(bool* isValid = nullptr) const; + float toFloat(bool* isValid = nullptr) const; + bool toBoolean(bool* isValid = nullptr) const; + int toInteger(bool* isValid = nullptr) const; + const std::wstring toWideLiteral(bool* isValid = nullptr) const; + const std::string toLiteral(bool* isValid = nullptr) const; + + bool fromVec4f(const atVec4f& val); + bool fromFloat(float val); + bool fromBoolean(bool val); + bool fromInteger(int val); + bool fromLiteral(const std::string& val); + bool fromLiteral(const std::wstring& val); + + bool isFloat() const { return m_type == EType::Float; } + bool isBoolean() const { return m_type == EType::Boolean; } + bool isInteger() const { return m_type == EType::Integer; } + bool isLiteral() const { return m_type == EType::Literal; } + bool isVec4f() const { return m_type == EType::Vec4f; } + bool isModified() const; + bool isReadOnly() const; + bool isCheat() const; + bool isHidden() const; + bool isArchive() const; + void clearModified(); + void setModified(); + + EType type() const { return m_type; } + EFlags flags() const { return m_flags; } + + /*! + * \brief Unlocks the CVar for writing if it is ReadOnly. + * Handle with care!!! if you use unlock(), make sure + * you lock the cvar using lock() + * \see lock + */ + void unlock(); + + /*! + * \brief Locks the CVar to prevent writing if it is ReadOnly. + * Unlike its partner function unlock, lock is harmless + * \see unlock + */ + void lock(); + + void addListener(ListenerFunc func) { m_listeners.push_back(func); } + +private: + void dispatch(); + std::string m_help; + std::string m_defaultValue; + EFlags m_flags; + bool m_allowedWrite; + + CVarManager& m_mgr; + + std::vector m_listeners; +}; + + + +class CVarUnlocker +{ + CVar* m_cvar; +public: + CVarUnlocker(CVar* cvar) : m_cvar(cvar) { if (m_cvar) m_cvar->unlock(); } + ~CVarUnlocker() { if (m_cvar) m_cvar->lock(); } +}; + +} +#endif // CVAR_HPP + diff --git a/hecl/include/HECL/CVarManager.hpp b/hecl/include/HECL/CVarManager.hpp new file mode 100755 index 000000000..5ab91a1ad --- /dev/null +++ b/hecl/include/HECL/CVarManager.hpp @@ -0,0 +1,68 @@ +#ifndef CVARMANAGER_HPP +#define CVARMANAGER_HPP + +#include +#include +#include "CVar.hpp" + +namespace HECL +{ +namespace Runtime +{ +class FileStoreManager; +} + +class CVarManager +{ + using CVarContainer = DNACVAR::CVarContainer; + template + CVar* _newCVar(const std::string& name, const std::string& help, const T& value, CVar::EFlags flags) + { + CVar* ret(new CVar(name, value, help, flags, *this)); + if (registerCVar(ret)) + { + if (ret->isArchive()) + deserialize(ret); + return ret; + } + delete ret; + return nullptr; + } + + HECL::Runtime::FileStoreManager& m_store; + bool m_useBinary; +public: + CVarManager(HECL::Runtime::FileStoreManager& store, bool useBinary = false); + ~CVarManager(); + + void update(); + CVar* newCVar(const std::string& name, const std::string& help, const atVec4f& value, CVar::EFlags flags) + { return _newCVar(name, help, value, flags); } + CVar* newCVar(const std::string& name, const std::string& help, const std::string& value, CVar::EFlags flags) + { return _newCVar(name, help, value, flags); } + CVar* newCVar(const std::string& name, const std::string& help, bool value, CVar::EFlags flags) + { return _newCVar(name, help, value, flags); } + CVar* newCVar(const std::string& name, const std::string& help, float value, CVar::EFlags flags) + { return _newCVar(name, help, value, flags); } + CVar* newCVar(const std::string& name, const std::string& help, int value, CVar::EFlags flags) + { return _newCVar(name, help, value, flags); } + + bool registerCVar(CVar* cvar); + + CVar* findCVar(std::string name); + + std::vector archivedCVars() const; + std::vector cvars() const; + + void deserialize(CVar* cvar); + void serialize(); +private: + bool suppressDeveloper(); + void restoreDeveloper(bool oldDeveloper); + + std::unordered_map m_cvars; +}; + +} + +#endif // CVARMANAGER_HPP diff --git a/hecl/lib/CMakeLists.txt b/hecl/lib/CMakeLists.txt index 14076944c..5c18cb0b3 100644 --- a/hecl/lib/CMakeLists.txt +++ b/hecl/lib/CMakeLists.txt @@ -10,11 +10,16 @@ endif() atdna(atdna_HMDLMeta.cpp ../include/HECL/HMDLMeta.hpp) atdna(atdna_Frontend.cpp ../include/HECL/Frontend.hpp) atdna(atdna_Runtime.cpp ../include/HECL/Runtime.hpp) +atdna(atdna_CVar.cpp ../include/HECL/CVar.hpp) add_library(HECLCommon HECL.cpp ProjectPath.cpp WideStringConvert.cpp + CVar.cpp + CVarManager.cpp + ../include/HECL/CVar.hpp + ../include/HECL/CVarManager.hpp ../include/HECL/HECL.hpp ../include/HECL/HMDLMeta.hpp ../include/HECL/Backend/Backend.hpp @@ -29,5 +34,6 @@ add_library(HECLCommon atdna_HMDLMeta.cpp atdna_Frontend.cpp atdna_Runtime.cpp + atdna_CVar.cpp ${PLAT_SRCS}) diff --git a/hecl/lib/CVar.cpp b/hecl/lib/CVar.cpp new file mode 100755 index 000000000..9a7c0f82b --- /dev/null +++ b/hecl/lib/CVar.cpp @@ -0,0 +1,397 @@ +#include "HECL/HECL.hpp" +#include "HECL/CVar.hpp" +#include "HECL/CVarManager.hpp" + +#include +#include + +namespace HECL +{ +extern CVar* com_developer; +extern CVar* com_enableCheats; + +CVar::CVar(const std::string& name, const std::string &value, const std::string &help, EType type, EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + m_name= name; + m_value = value; + m_defaultValue = value; + m_help = help; + m_type = type; + m_flags = flags; + m_allowedWrite = false; +} + +CVar::CVar(const std::string& name, const std::string& value, const std::string& help, CVar::EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + // Unlock the cvar for writing if readonly + unlock(); + + m_name= name; + m_help = help; + m_type = EType::Literal; + m_flags = flags; + m_allowedWrite = false; + + fromLiteral(value); + m_defaultValue = m_value; + + // Lock the cvar + lock(); + // Clear the modified flag, just incase lock didn't do it. + m_flags = flags; +} + +CVar::CVar(const std::string& name, const atVec4f& value, const std::string& help, EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + // Unlock the cvar for writing if readonly + unlock(); + + m_name= name; + m_help = help; + m_type = EType::Vec4f; + m_flags = flags; + m_allowedWrite = false; + + fromVec4f(value); + m_defaultValue = m_value; + + // Lock the cvar + lock(); + // Clear the modified flag, just incase lock didn't do it. + m_flags = flags; +} + +CVar::CVar(const std::string& name, float value, const std::string& help, EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + // Unlock the cvar for writing if readonly + unlock(); + + m_name= name; + m_help = help; + m_type = EType::Float; + m_flags = flags; + m_allowedWrite = false; + + fromFloat(value); + m_defaultValue = m_value; + + // Lock the cvar + lock(); + // Clear the modified flag, just incase lock didn't do it. + m_flags = flags; +} + +CVar::CVar(const std::string& name, bool value, const std::string& help, CVar::EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + // Unlock the cvar for writing if readonly + unlock(); + + m_name= name; + m_help = help; + m_type = EType::Boolean; + m_flags = flags; + m_allowedWrite = false; + + fromBoolean(value); + m_defaultValue = m_value; + + // Lock the cvar + lock(); + // Clear the modified flag, just incase lock didn't do it. + m_flags = flags; +} + +CVar::CVar(const std::string& name, int value, const std::string& help, CVar::EFlags flags, CVarManager& parent) + : m_mgr(parent) +{ + // Unlock the cvar for writing if readonly + unlock(); + + m_name= name; + m_help = help; + m_type = EType::Integer; + m_flags = flags; + m_allowedWrite = false; + + fromInteger(value); + m_defaultValue = m_value; + + // Lock the cvar + lock(); + // Clear the modified flag, just incase lock didn't do it. + m_flags = flags; +} + +std::string CVar::help() const +{ + return std::string(m_help + (m_defaultValue != std::string() ? "\ndefault: " + m_defaultValue : "") + + (isReadOnly() ? "[ReadOnly]" : "")); +} + +atVec4f CVar::toVec4f(bool* isValid) const +{ + if (m_type != EType::Vec4f) + { + if (isValid != nullptr) + *isValid = false; + + return {}; + } + + if (isValid != NULL) + *isValid = true; + + atVec4f vec; + std::sscanf(m_value.c_str(), "%f %f %f %f", &vec.vec[0], &vec.vec[1], &vec.vec[2], &vec.vec[3]); + + return vec; +} + +float CVar::toFloat(bool* isValid) const +{ + if (m_type != EType::Float) + { + if (isValid) + *isValid = false; + return 0.0f; + } + + return strtof(m_value.c_str(), nullptr); +} + +bool CVar::toBoolean(bool* isValid) const +{ + if (m_type != EType::Boolean) + { + if (isValid) + *isValid = false; + + return false; + } + + // We don't want to modify the original value; + std::string tmp = m_value; + std::transform(tmp.begin(), tmp.end(), tmp.begin(), ::tolower); + + if (!tmp.compare("yes") || !tmp.compare("true") || !tmp.compare("1")) + { + if (isValid) + *isValid = true; + return true; + } + else if (!tmp.compare("no") || !tmp.compare("false") || !tmp.compare("0")) + { + if (isValid) + *isValid = true; + return false; + } + + if (isValid) + *isValid = false; + + return false; +} + +int CVar::toInteger(bool* isValid) const +{ + if (m_type != EType::Integer) + { + if (isValid) + *isValid = false; + return 0; + } + + return strtol(m_value.c_str(), nullptr, 0); +} + +const std::string CVar::toLiteral(bool* isValid) const +{ + if (m_type != EType::Literal && (com_developer && com_developer->toBoolean())) + { + if (isValid != nullptr) + *isValid = false; + } + else if (isValid != nullptr) + *isValid = true; + + // Even if it's not a literal, it's still safe to return + return m_value; +} + +const std::wstring CVar::toWideLiteral(bool* isValid) const +{ + if (m_type != EType::Literal && (com_developer && com_developer->toBoolean())) + { + if (isValid != nullptr) + *isValid = false; + } + else if (isValid != nullptr) + *isValid = true; + + // Even if it's not a literal, it's still safe to return + return HECL::UTF8ToWide(m_value); +} + +bool CVar::fromVec4f(const atVec4f& val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Vec4f) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + m_value.assign(HECL::Format("%f %f %f %f", val.vec[0], val.vec[1], val.vec[2], val.vec[3])); + m_flags |= EFlags::Modified; + return true; +} + +bool CVar::fromFloat(float val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Float) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + m_value.assign(HECL::Format("%f", val)); + setModified(); + return true; +} + +bool CVar::fromBoolean(bool val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Boolean) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + if (val) + m_value = "true"; + else + m_value = "false"; + + setModified(); + return true; +} + +bool CVar::fromInteger(int val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Integer) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + m_value = HECL::Format("%i", val); + setModified(); + return true; +} + +bool CVar::fromLiteral(const std::string& val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Literal) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + m_value.assign(val); + setModified(); + return true; +} + +bool CVar::fromLiteral(const std::wstring& val) +{ + if (isCheat() && (com_developer && !com_developer->toBoolean() && !com_enableCheats->toBoolean())) + return false; + else if (isCheat()) + return false; + + if (m_type != EType::Literal) + return false; + + if (isReadOnly() && (com_developer && !com_developer->toBoolean())) + return false; + + m_value.assign(HECL::WideToUTF8(val)); + setModified(); + return true; +} + +bool CVar::isModified() const { return int(m_flags & EFlags::Modified) != 0;} + +bool CVar::isReadOnly() const { return int(m_flags & EFlags::ReadOnly) != 0; } + +bool CVar::isCheat() const { return int(m_flags & EFlags::Cheat) != 0; } + +bool CVar::isHidden() const { return int(m_flags & EFlags::Hidden) != 0; } + +bool CVar::isArchive() const { return int(m_flags & EFlags::Archive) != 0; } + +void CVar::clearModified() { m_flags &= ~EFlags::Modified; } + +void CVar::setModified() { m_flags |= EFlags::Modified; } + +void CVar::unlock() +{ + if (!isReadOnly()) + return; + + if (!m_allowedWrite) + { + m_allowedWrite = true; + m_flags &= ~EFlags::ReadOnly; + } +} + +void CVar::lock() +{ + if (!isReadOnly()) + return; + + if (m_allowedWrite) + { + m_flags |= EFlags::ReadOnly; + m_allowedWrite = false; + clearModified(); + } +} + +void CVar::dispatch() +{ + for (const ListenerFunc& listen : m_listeners) + listen(this); +} +} + diff --git a/hecl/lib/CVarManager.cpp b/hecl/lib/CVarManager.cpp new file mode 100755 index 000000000..f7b81c29a --- /dev/null +++ b/hecl/lib/CVarManager.cpp @@ -0,0 +1,181 @@ +#include "HECL/CVarManager.hpp" +#include "HECL/CVar.hpp" +#include +#include +#include +#include + +namespace HECL +{ + +CVar* com_developer = nullptr; +CVar* com_configfile = nullptr; +CVar* com_enableCheats = nullptr; + +LogVisor::LogModule CVarLog("CVarManager"); +CVarManager::CVarManager(HECL::Runtime::FileStoreManager& store, bool useBinary) + : m_store(store), + m_useBinary(useBinary) +{ + com_configfile = newCVar("config", "File to store configuration", std::string("config"), CVar::EFlags::System); + com_developer = newCVar("developer", "Enables developer mode", false, (CVar::EFlags::System | CVar::EFlags::Cheat | CVar::EFlags::ReadOnly)); + com_enableCheats = newCVar("iamaweiner", "Enable cheats", false, (CVar::EFlags::System | CVar::EFlags::ReadOnly | CVar::EFlags::Hidden)); +} + +CVarManager::~CVarManager() +{ +} + +void CVarManager::update() +{ + for (const std::pair& pair : m_cvars) + if (pair.second->isModified()) + { + pair.second->dispatch(); + pair.second->clearModified(); + } +} + +bool CVarManager::registerCVar(CVar* cvar) +{ + std::string tmp = cvar->name(); + Athena::utility::tolower(tmp); + if (m_cvars.find(tmp) != m_cvars.end()) + return false; + + m_cvars[tmp] = cvar; + return true; +} + +CVar* CVarManager::findCVar(std::string name) +{ + Athena::utility::tolower(name); + if (m_cvars.find(name) == m_cvars.end()) + return nullptr; + + return m_cvars[name]; +} + +std::vector CVarManager::archivedCVars() const +{ + std::vector ret; + for (const std::pair& pair : m_cvars) + if (pair.second->isArchive()) + ret.push_back(pair.second); + + return ret; +} + +std::vector CVarManager::cvars() const +{ + std::vector ret; + for (const std::pair& pair : m_cvars) + ret.push_back(pair.second); + + return ret; +} + +void CVarManager::deserialize(CVar* cvar) +{ + if (!cvar || !cvar->isArchive()) + return; + + CVarContainer container; +#if _WIN32 + HECL::SystemString filename = m_store.getStoreRoot() + _S('/') + com_configfile->toWideLiteral(); +#else + HECL::SystemString filename = m_store.getStoreRoot() + _S('/') + com_configfile->toLiteral(); +#endif + HECL::Sstat st; + + if (m_useBinary) + { + filename += _S(".bin"); + if (HECL::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode)) + return; + Athena::io::FileReader reader(filename); + if (reader.isOpen()) + container.read(reader); + } + else + { + filename += _S(".yaml"); + if (HECL::Stat(filename.c_str(), &st) || !S_ISREG(st.st_mode)) + return; + FILE* f = HECL::Fopen(filename.c_str(), _S("rb")); + if (f) + container.fromYAMLFile(f); + fclose(f); + } + + + if (container.cvars.size() > 0) + { + auto serialized = std::find_if(container.cvars.begin(), container.cvars.end(), [&cvar](const DNACVAR::CVar& c) -> bool + { return c.m_name == cvar->name(); }); + + if (serialized != container.cvars.end()) + { + DNACVAR::CVar& tmp = *serialized; + if (tmp.m_type != cvar->type()) + { + CVarLog.report(LogVisor::Error, _S("Stored type for %s does not match actual type!"), tmp.m_name.c_str()); + return; + } + + cvar->m_value = tmp.m_value; + } + } +} + +void CVarManager::serialize() +{ + CVarContainer container; + for (const std::pair& pair : m_cvars) + if (pair.second->isArchive()) + { + CVar tmp = *pair.second; + container.cvars.push_back(tmp); + } + + container.cvarCount = container.cvars.size(); + +#if _WIN32 + HECL::SystemString filename = m_store.getStoreRoot() + _S('/') + com_configfile->toWideLiteral(); +#else + HECL::SystemString filename = m_store.getStoreRoot() + _S('/') + com_configfile->toLiteral(); +#endif + + if (m_useBinary) + { + filename += _S(".bin"); + Athena::io::FileWriter writer(filename); + if (writer.isOpen()) + container.write(writer); + } + else + { + filename += _S(".yaml"); + FILE* f = HECL::Fopen(filename.c_str(), _S("wb")); + if (f) + container.toYAMLFile(f); + fclose(f); + } +} + +bool CVarManager::suppressDeveloper() +{ + bool oldDeveloper = com_developer->toBoolean(); + CVarUnlocker unlock(com_developer); + com_developer->fromBoolean(false); + + return oldDeveloper; +} + +void CVarManager::restoreDeveloper(bool oldDeveloper) +{ + CVarUnlocker unlock(com_developer); + com_developer->fromBoolean(oldDeveloper); +} + +}