diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 2f783609..00e22a36 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -150,7 +150,7 @@ jobs: continue-on-error: true - name: Run clang-tidy - if: ${{ steps.check-compile-commands.outcome == 'success' }} && ${{ steps.list-files.outcome == 'success' }} + if: ${{ steps.check-compile-commands.outcome == 'success' && steps.list-files.outcome == 'success' }} shell: bash run: | cat files.txt | xargs clang-tidy --quiet --config-file=.clang-tidy -p=${{ github.workspace }}/build diff --git a/.gitignore b/.gitignore index 981c71f0..84ca6194 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ dev/ # Logging logs/ *.log + +# Configuration +config/config.ini diff --git a/.vscode/settings.json b/.vscode/settings.json index ecb5cdd0..7fc493d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "C_Cpp.loggingLevel": "Debug", // "clangd.arguments": [ - "-log=verbose", + "--log=verbose", "--clang-tidy", ], // diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b8d0c4b8..4678ecbd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,7 +1,4 @@ { - //! Executing tasks on Windows requires Git Bash (or other unix-like shell) to be installed. - // Default shell on Windows is either Powershell or CMD, which do not support grep or xargs. - // Basically it is easier to require specific shell than to maintain multiplatform tasks. "version": "2.0.0", "tasks": [ { @@ -17,17 +14,17 @@ "clear": true, "panel": "dedicated", "reveal": "silent", - "showReuseMessage": false, - }, + "showReuseMessage": false + } }, { - "label": "Run clang-tidy", + "label": "Run clang-tidy (all files)", "detail": "Run clang-tidy on all C++ files in src/ directory.", "type": "shell", "command": "cmake -P ./cmake/clang-tidy.cmake", "icon": { "color": "terminal.ansiCyan", - "id": "sparkle", + "id": "sparkle" }, "problemMatcher": { "source": "clang-tidy", @@ -42,26 +39,60 @@ "column": 3, "severity": 4, "message": 5, - "code": 6, + "code": 6 } - ], + ] }, "presentation": { "clear": true, "panel": "dedicated", "reveal": "silent", - "showReuseMessage": false, + "showReuseMessage": false + } + }, + { + "label": "Run clang-tidy (current file)", + "detail": "Run clang-tidy on the currently active open file.", + "type": "shell", + "command": "clang-tidy --quiet --config-file=.clang-tidy -p=build/ ${file}", + "icon": { + "color": "terminal.ansiWhite", + "id": "sparkle" + }, + "problemMatcher": { + "source": "clang-tidy", + "owner": "clang-tidy", + "applyTo": "openDocuments", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(.*):([0-9]{1,6}):([0-9]{1,6}):\\s([a-z]+):\\s(.*)\\[(.*)\\]$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5, + "code": 6 + } + ] }, + "presentation": { + "clear": true, + "echo": true, + "panel": "dedicated", + "reveal": "silent", + "showReuseMessage": false + } }, { "label": "Setup hooks", "detail": "Copy git hooks to .git/hooks/ directory.", "type": "shell", "linux": { - "command": "cp -r .githooks/* .git/hooks/", + "command": "cp -r .githooks/* .git/hooks/" }, "windows": { - "command": "Copy-Item -Path '.githooks/*' -Destination '.git/hooks/' -Exclude '.*'", + "command": "Copy-Item -Path '.githooks/*' -Destination '.git/hooks/' -Exclude '.*'" }, "icon": { "color": "terminal.ansiYellow", @@ -70,7 +101,7 @@ "presentation": { "clear": true, "reveal": "silent", - "showReuseMessage": false, + "showReuseMessage": false }, "hide": true, "runOptions": { @@ -95,7 +126,7 @@ "clear": true, "panel": "dedicated", "reveal": "silent", - "showReuseMessage": false, + "showReuseMessage": false } }, { @@ -116,7 +147,7 @@ "clear": true, "panel": "dedicated", "reveal": "silent", - "showReuseMessage": false, + "showReuseMessage": false } } ] diff --git a/CMakeLists.txt b/CMakeLists.txt index a7ee995c..6a538fb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ endif() if(CMAKE_BUILD_TYPE EQUAL "") set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) endif() -string(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" DEBUG_MODE) +string(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" DEBUG_BUILD) include(${PROJECT_SOURCE_DIR}/cmake/version.cmake) diff --git a/config/config.ini b/config/config.ini deleted file mode 100644 index 0f5b8f07..00000000 --- a/config/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[engine] -UseOpenGL = true diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt index 4e23cb41..cef9ae3b 100644 --- a/src/engine/CMakeLists.txt +++ b/src/engine/CMakeLists.txt @@ -5,7 +5,7 @@ project(MONOENGINE LANGUAGES CXX C) if(CMAKE_BUILD_TYPE EQUAL "") set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) endif() -string(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" DEBUG_MODE) +string(COMPARE EQUAL "${CMAKE_BUILD_TYPE}" "Debug" DEBUG_BUILD) option(MOLD_LINKER_PATH "Path to the mold linker executable (Unix only)" "") @@ -70,6 +70,7 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/module/resource") add_subdirectory("${PROJECT_SOURCE_DIR}/module/time") add_subdirectory("${PROJECT_SOURCE_DIR}/module/config") add_subdirectory("${PROJECT_SOURCE_DIR}/module/util") +add_subdirectory("${PROJECT_SOURCE_DIR}/module/dev_ui") # Use mold linker if present in the system (to speed up linking time) diff --git a/src/engine/module/audio/CMakeLists.txt b/src/engine/module/audio/CMakeLists.txt index 2c7c6a29..23c4011c 100644 --- a/src/engine/module/audio/CMakeLists.txt +++ b/src/engine/module/audio/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_audio) add_library(mono::audio ALIAS mono_module_audio) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_audio - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/camera/CMakeLists.txt b/src/engine/module/camera/CMakeLists.txt index 7c2f7297..6cb61287 100644 --- a/src/engine/module/camera/CMakeLists.txt +++ b/src/engine/module/camera/CMakeLists.txt @@ -3,10 +3,9 @@ include_guard() add_library(mono_module_camera) add_library(mono::camera ALIAS mono_module_camera) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_camera - - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/config/CMakeLists.txt b/src/engine/module/config/CMakeLists.txt index a05a2017..f7165b52 100644 --- a/src/engine/module/config/CMakeLists.txt +++ b/src/engine/module/config/CMakeLists.txt @@ -9,14 +9,21 @@ target_compile_features(mono_module_config target_sources(mono_module_config PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/src/ConfigLoader.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Config.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/ConfigItem.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/ConfigStorage.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/CallbackGuard.cpp" ) target_include_directories(mono_module_config + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/mono" INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include" ) target_link_libraries(mono_module_config PUBLIC inifile::inifile spdlog::spdlog + glm::glm + mono::traits + mono::log ) diff --git a/src/engine/module/config/include/config/ConfigLoader.hpp b/src/engine/module/config/include/config/ConfigLoader.hpp deleted file mode 100644 index 15507306..00000000 --- a/src/engine/module/config/include/config/ConfigLoader.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#include - -namespace mono -{ - -class ConfigLoader final -{ - public: - static ConfigLoader& get(); - - ~ConfigLoader() = default; - - ConfigLoader(const ConfigLoader&) = delete; - ConfigLoader(ConfigLoader&&) = delete; - - ConfigLoader& operator=(const ConfigLoader&) = delete; - ConfigLoader& operator=(ConfigLoader&&) = delete; - - bool loadFromFile(const std::filesystem::path& path); - - private: - ConfigLoader(); - void updateRuntimeConfiguration() const; - - private: - ini::IniFile m_iniFile{}; -}; - -} // namespace mono diff --git a/src/engine/module/config/include/config/RuntimeConfiguration.hpp b/src/engine/module/config/include/config/RuntimeConfiguration.hpp deleted file mode 100644 index bf88a9c8..00000000 --- a/src/engine/module/config/include/config/RuntimeConfiguration.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace mono::config::runtime -{ - -bool useOpenGl = true; - -} diff --git a/src/engine/module/config/include/config/StaticConfiguration.hpp b/src/engine/module/config/include/config/StaticConfiguration.hpp deleted file mode 100644 index c5b1b71d..00000000 --- a/src/engine/module/config/include/config/StaticConfiguration.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#ifndef DEBUG_MODE - #define DEBUG_MODE 0 -#endif - -namespace mono::config::constant -{ - -constexpr bool debugMode = DEBUG_MODE; - -} diff --git a/src/engine/module/config/include/mono/config/CallbackGuard.hpp b/src/engine/module/config/include/mono/config/CallbackGuard.hpp new file mode 100644 index 00000000..0beaf711 --- /dev/null +++ b/src/engine/module/config/include/mono/config/CallbackGuard.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace mono::config +{ + +class ConfigItem; + +class CallbackGuard +{ + public: + explicit CallbackGuard(ConfigItem& config_item); + ~CallbackGuard() noexcept; + + CallbackGuard(const CallbackGuard& other) = delete; + CallbackGuard(CallbackGuard&& other) noexcept = delete; + CallbackGuard& operator=(const CallbackGuard& other) = delete; + CallbackGuard& operator=(CallbackGuard&& other) noexcept = delete; + + private: + ConfigItem& m_configItem; +}; + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/Config.hpp b/src/engine/module/config/include/mono/config/Config.hpp new file mode 100644 index 00000000..1485ca74 --- /dev/null +++ b/src/engine/module/config/include/mono/config/Config.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "ConfigStorage.hpp" +#include "mono/log/priv/BufferedSink.hpp" +#include "priv/StaticConfiguration.hpp" // IWYU pragma: export +#include "priv/converters/GlmVec2Converter.hpp" // IWYU pragma: export +#include "priv/converters/SpdlogLevelConverter.hpp" // IWYU pragma: export + +namespace mono::config +{ + + +namespace priv +{ + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +inline std::shared_ptr buffered_sink; + +} // namespace priv + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +inline ConfigStorage runtime; + +void initialize(); + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/ConfigItem.hpp b/src/engine/module/config/include/mono/config/ConfigItem.hpp new file mode 100644 index 00000000..5729eed8 --- /dev/null +++ b/src/engine/module/config/include/mono/config/ConfigItem.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "CallbackGuard.hpp" +#include "priv/IniCompliantTrait.hpp" + +namespace mono::config +{ + +using ConfigItemSetCallback = + std::function; + +class ConfigItem +{ + public: + ConfigItem(ini::IniFile& ini_storage, const std::string& section, const std::string& key); + ConfigItem(const ConfigItem&) = default; + ConfigItem(ConfigItem&&) = default; + virtual ~ConfigItem() = default; + + ConfigItem& operator=(const ConfigItem& copy); + ConfigItem& operator=(ConfigItem&& move) noexcept; + + virtual bool isValid() const = 0; + virtual std::string getType() const = 0; + + std::string_view getSection() const; + std::string_view getKey() const; + template + std::optional getValue() const; + bool setValue(const std::string& value); + bool setValue(const IniEncodableTrait auto& value); + + [[nodiscard]] CallbackGuard setOnSetCallback(ConfigItemSetCallback&& callback); + void removeOnSetCallback(); + + protected: + std::string getNativeValue() const; + + private: + std::string m_section; + std::string m_key; + ini::IniFile& m_iniStorage; + std::optional m_setCallback; +}; + +template +std::optional ConfigItem::getValue() const +{ + if(not m_iniStorage.contains(m_section)) + { + return std::nullopt; + } + + const auto& section = m_iniStorage.at(m_section); + if(not section.contains(m_key)) + { + return std::nullopt; + } + + return section.at(m_key).as(); +} + +bool ConfigItem::setValue(const IniEncodableTrait auto& value) +{ + std::string new_value; + ini::Convert>{}.encode(value, new_value); + + return this->setValue(new_value); +} + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/ConfigStorage.hpp b/src/engine/module/config/include/mono/config/ConfigStorage.hpp new file mode 100644 index 00000000..fc8bd183 --- /dev/null +++ b/src/engine/module/config/include/mono/config/ConfigStorage.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include +#include + +#include + +#include "ConfigItem.hpp" +#include "types/BasicConfigItem.hpp" // IWYU pragma: keep +#include "types/MultiNumberConfigItem.hpp" // IWYU pragma: keep +#include "types/OptionStringConfigItem.hpp" // IWYU pragma: keep +#include "types/RangeNumberConfigItem.hpp" // IWYU pragma: keep + +namespace mono::config +{ + +class ConfigStorage final +{ + public: + ConfigStorage() = default; + explicit ConfigStorage(const std::filesystem::path& path); + + template T> + T& addConfigItem(auto&&... args) + requires std::constructible_from; + + template T> + std::optional> get( + const std::string& section, + const std::string& key); + bool validate() const; + auto begin(); + auto end(); + auto begin() const; + auto end() const; + + bool loadFromFile(const std::filesystem::path& path); + + private: + std::vector> m_items{}; + ini::IniFile m_iniFile; +}; + +template T> +T& ConfigStorage::addConfigItem(auto&&... args) +requires std::constructible_from +{ + std::unique_ptr item = + std::make_unique(m_iniFile, std::forward(args)...); + m_items.push_back(std::move(item)); + return static_cast(*m_items.back()); +} + +template T> +std::optional> ConfigStorage::get( + const std::string& section, + const std::string& key) +{ + auto result = std::ranges::find_if( + m_items, + [§ion, &key](const std::unique_ptr& item_ptr) { + const auto item = item_ptr.get(); + return (item->getSection().compare(section) == 0) + and (item->getKey().compare(key) == 0); + }); + if(result == m_items.end()) + { + return std::nullopt; + } + + return static_cast(**result); +} + +inline auto ConfigStorage::begin() +{ + return m_items.begin(); +} + +inline auto ConfigStorage::end() +{ + return m_items.end(); +} + +inline auto ConfigStorage::begin() const +{ + return m_items.begin(); +} + +inline auto ConfigStorage::end() const +{ + return m_items.end(); +} + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/priv/IniCompliantTrait.hpp b/src/engine/module/config/include/mono/config/priv/IniCompliantTrait.hpp new file mode 100644 index 00000000..a5c6e504 --- /dev/null +++ b/src/engine/module/config/include/mono/config/priv/IniCompliantTrait.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "inicpp.h" + +namespace mono::config +{ + +template +concept IniEncodableTrait = requires(T value, std::string target) { + { ini::Convert{}.encode(value, target) }; +}; + +template +concept IniDecodableTrait = requires(T target, std::string value) { + { ini::Convert{}.decode(value, target) }; +}; + +template +concept IniCompliantTrait = IniEncodableTrait and IniDecodableTrait; + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/priv/StaticConfiguration.hpp b/src/engine/module/config/include/mono/config/priv/StaticConfiguration.hpp new file mode 100644 index 00000000..bb7c503a --- /dev/null +++ b/src/engine/module/config/include/mono/config/priv/StaticConfiguration.hpp @@ -0,0 +1,10 @@ +#pragma once + +#ifndef DEBUG_BUILD + #define DEBUG_BUILD 0 +#endif + +namespace mono::config::constant +{ +constexpr bool debugBuild = DEBUG_BUILD; +} diff --git a/src/engine/module/config/include/mono/config/priv/converters/GlmVec2Converter.hpp b/src/engine/module/config/include/mono/config/priv/converters/GlmVec2Converter.hpp new file mode 100644 index 00000000..03d805d2 --- /dev/null +++ b/src/engine/module/config/include/mono/config/priv/converters/GlmVec2Converter.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include + +namespace ini +{ + +template +struct Convert> +{ + using thisvec = glm::vec; + + void encode(const thisvec& value, std::string& target) const + { + target.clear(); + for(glm::length_t i = 0; i < L; i++) + { + target += std::to_string(value[i]); + if(i < L - 1) + { + target += "x"; + } + } + } + + void decode(const std::string& value, thisvec& target) const + { + auto split_view = value | std::ranges::views::split('x') + | std::ranges::views::transform([](auto&& subrange) { + return std::string{subrange.begin(), subrange.end()}; + }); + for(glm::length_t i{0}; const auto& dim : split_view) + { + target[i++] = std::stof(dim); + } + } +}; + +} // namespace ini diff --git a/src/engine/module/config/include/mono/config/priv/converters/SpdlogLevelConverter.hpp b/src/engine/module/config/include/mono/config/priv/converters/SpdlogLevelConverter.hpp new file mode 100644 index 00000000..f6587b7f --- /dev/null +++ b/src/engine/module/config/include/mono/config/priv/converters/SpdlogLevelConverter.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include +#include + +namespace ini +{ + +template<> +struct Convert +{ + void encode(const spdlog::level& value, std::string& target) const + { + switch(value) + { + case spdlog::level::trace: target = "trace"; break; + case spdlog::level::debug: target = "debug"; break; + case spdlog::level::info: target = "info"; break; + case spdlog::level::warn: target = "warning"; break; + case spdlog::level::err: target = "error"; break; + case spdlog::level::critical: target = "critical"; break; + case spdlog::level::off: target = "off"; break; + default: target = "off"; break; + } + } + + void decode(const std::string& value, spdlog::level& target) const + { + if(value.compare("trace") == 0) + { + target = spdlog::level::trace; + } + else if(value.compare("debug") == 0) + { + target = spdlog::level::debug; + } + else if(value.compare("info") == 0) + { + target = spdlog::level::info; + } + else if(value.compare("warning") == 0) + { + target = spdlog::level::warn; + } + else if(value.compare("error") == 0) + { + target = spdlog::level::err; + } + else if(value.compare("critical") == 0) + { + target = spdlog::level::critical; + } + else if(value.compare("off") == 0) + { + target = spdlog::level::off; + } + else + { + target = spdlog::level::off; + } + } +}; + +} // namespace ini diff --git a/src/engine/module/config/include/mono/config/types/BasicConfigItem.hpp b/src/engine/module/config/include/mono/config/types/BasicConfigItem.hpp new file mode 100644 index 00000000..07e50c0c --- /dev/null +++ b/src/engine/module/config/include/mono/config/types/BasicConfigItem.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include + +#include "../ConfigItem.hpp" +#include "../priv/IniCompliantTrait.hpp" + +namespace mono::config +{ + +template +class BasicConfigItem : public ConfigItem +{ + public: + BasicConfigItem(ini::IniFile& ini_storage, const std::string& section, const std::string& key) + : ConfigItem(ini_storage, section, key) + { } + + bool isValid() const override + { + ini::Convert converter{}; + T temp{}; + try + { + converter.decode(this->getNativeValue(), temp); + } + catch(const std::exception& e) + { + spdlog::error( + "Failed to decode BasicConfigItem[{}][{}]: {}", + this->getSection(), + this->getKey(), + e.what()); + return false; + } + return true; + } + + std::string getType() const override + { + return std::format("BasicConfigItem<{}>", typeid(T).name()); + } +}; + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/types/MultiNumberConfigItem.hpp b/src/engine/module/config/include/mono/config/types/MultiNumberConfigItem.hpp new file mode 100644 index 00000000..ea711c17 --- /dev/null +++ b/src/engine/module/config/include/mono/config/types/MultiNumberConfigItem.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include "../ConfigItem.hpp" +#include "traits/Arithmetic.hpp" + +namespace mono::config +{ + +template +class MultiNumberConfigItem : public ConfigItem +{ + public: + MultiNumberConfigItem( + ini::IniFile& ini_storage, + const std::string& section, + const std::string& key) + : ConfigItem(ini_storage, section, key) + { } + + bool isValid() const override + { + auto split_view = this->getNativeValue() | std::ranges::views::split(SEP) + | std::ranges::views::transform([](auto&& subrange) { + return std::string{subrange.begin(), subrange.end()}; + }); + + if(const auto dims = std::ranges::distance(split_view); dims != NUM) + { + spdlog::error( + "Config field [{}][{}] has {} dimensions, but expected {} dimensions", + this->getSection(), + this->getKey(), + dims, + NUM); + return false; + } + + ini::Convert converter{}; + T temp{}; + try + { + for(const auto& dim : split_view) + { + converter.decode(dim, temp); + } + } + catch(const std::exception&) + { + spdlog::error( + "Config field [{}][{}] has at least one element of unsupported type", + this->getSection(), + this->getKey()); + return false; + } + return true; + } + + std::string getType() const override + { + return std::format("MultiNumberConfigItem<{},{},'{}'>", NUM, typeid(T).name(), SEP); + }; +}; + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/types/OptionStringConfigItem.hpp b/src/engine/module/config/include/mono/config/types/OptionStringConfigItem.hpp new file mode 100644 index 00000000..1fd314c6 --- /dev/null +++ b/src/engine/module/config/include/mono/config/types/OptionStringConfigItem.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include + +#include "../ConfigItem.hpp" + +namespace mono::config +{ + +class OptionStringConfigItem final : public ConfigItem +{ + public: + OptionStringConfigItem( + ini::IniFile& ini_storage, + const std::string& section, + const std::string& key, + const std::vector& options) + : ConfigItem(ini_storage, section, key) + , m_options(options) + { } + + bool isValid() const override + { + const std::string value = this->getNativeValue(); + const bool result = std::ranges::any_of(m_options, [&value](const std::string& option) { + return value.compare(option) == 0; + }); + if(not result) + { + const std::string options_list = std::accumulate( + std::next(m_options.begin()), + m_options.end(), + m_options[0], + [](const std::string& a, const std::string& b) { + return a + ", " + b; + }); + spdlog::error( + "Config field [{}][{}] has value '{}' which is not among valid options: [{}]", + this->getSection(), + this->getKey(), + value, + options_list); + } + return result; + } + + std::string getType() const override { return "OptionStringConfigItem"; } + + const std::vector& getOptions() const { return m_options; } + + private: + const std::vector m_options{}; +}; + +} // namespace mono::config diff --git a/src/engine/module/config/include/mono/config/types/RangeNumberConfigItem.hpp b/src/engine/module/config/include/mono/config/types/RangeNumberConfigItem.hpp new file mode 100644 index 00000000..cb8c1a7e --- /dev/null +++ b/src/engine/module/config/include/mono/config/types/RangeNumberConfigItem.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "BasicConfigItem.hpp" +#include "traits/Arithmetic.hpp" + +namespace mono::config +{ + +template +class RangeNumberConfigItem : public BasicConfigItem +{ + public: + RangeNumberConfigItem( + ini::IniFile& ini_storage, + const std::string& section, + const std::string& key, + const T& min, + const T& max) + : BasicConfigItem(ini_storage, section, key) + , m_min(min) + , m_max(max) + { } + + bool isValid() const override + { + if(not BasicConfigItem::isValid()) + { + return false; + } + + const T value = this->template getValue().value(); + if(value < m_min or value > m_max) + { + spdlog::error( + "Config field [{}][{}] has value {} which is out of range [{}, {}]", + this->getSection(), + this->getKey(), + value, + m_min, + m_max); + return false; + } + + return true; + } + + std::string getType() const override + { + return std::format("RangeNumberConfigItem<{}>", typeid(T).name()); + } + + const T& getMin() const { return m_min; } + + const T& getMax() const { return m_max; } + + private: + const T m_min; + const T m_max; +}; + +} // namespace mono::config diff --git a/src/engine/module/config/src/CallbackGuard.cpp b/src/engine/module/config/src/CallbackGuard.cpp new file mode 100644 index 00000000..3fa70e8f --- /dev/null +++ b/src/engine/module/config/src/CallbackGuard.cpp @@ -0,0 +1,17 @@ +#include "config/CallbackGuard.hpp" + +#include "config/ConfigItem.hpp" + +namespace mono::config +{ + +CallbackGuard::CallbackGuard(ConfigItem& config_item) + : m_configItem(config_item) +{ } + +CallbackGuard::~CallbackGuard() noexcept +{ + m_configItem.removeOnSetCallback(); +} + +} // namespace mono::config diff --git a/src/engine/module/config/src/Config.cpp b/src/engine/module/config/src/Config.cpp new file mode 100644 index 00000000..5b9e69d4 --- /dev/null +++ b/src/engine/module/config/src/Config.cpp @@ -0,0 +1,60 @@ +#include "config/Config.hpp" + +#include +#include +#include +#include + +#include "config/types/BasicConfigItem.hpp" +#include "config/types/OptionStringConfigItem.hpp" +#include "mono/log/Logging.hpp" + +namespace mono::config +{ + +static void initTemporaryLogger() +{ + priv::buffered_sink = std::make_shared(); + spdlog::set_default_logger(std::make_shared("buffer", priv::buffered_sink)); +} + +static void flushTemporaryLoggerOnFail() +{ + // This is terrible, when single point of initialization is introduced, config::initialize() + // should return false. + mono::log::initialize(); // flush happens inside log::initialize() now + priv::buffered_sink.reset(); + priv::buffered_sink = nullptr; +} + +void initialize() +{ + initTemporaryLogger(); + runtime.loadFromFile("../config/config.ini"); + + runtime.addConfigItem>(std::string{"engine"}, std::string{"UseOpenGL"}); + runtime.addConfigItem( + std::string{"engine"}, + std::string{"LogLevel"}, + std::vector{"trace", "debug", "info", "warn", "error", "critical"}); + + runtime.addConfigItem( + std::string{"engine.window"}, + std::string{"Mode"}, + std::vector{"windowed", "fullscreen", "borderless"}); + runtime.addConfigItem>("engine.window", "UseVSync"); + runtime.addConfigItem>( + "engine.window", + "Resolution"); + + const auto success = runtime.validate(); + if(not success) + { + spdlog::error("Config validation failed"); + flushTemporaryLoggerOnFail(); + spdlog::shutdown(); // prevents crashes on exit - shuts down thread pool + std::exit(EXIT_FAILURE); + } +} + +} // namespace mono::config diff --git a/src/engine/module/config/src/ConfigItem.cpp b/src/engine/module/config/src/ConfigItem.cpp new file mode 100644 index 00000000..9b37ad81 --- /dev/null +++ b/src/engine/module/config/src/ConfigItem.cpp @@ -0,0 +1,100 @@ +#include "config/ConfigItem.hpp" + +#include + +namespace mono::config +{ + +ConfigItem::ConfigItem( + ini::IniFile& ini_storage, + const std::string& section, + const std::string& key) + : m_section(section) + , m_key(key) + , m_iniStorage(ini_storage) +{ } + +// NOLINTNEXTLINE(modernize-use-equals-default) +ConfigItem& ConfigItem::operator=(const ConfigItem& copy) +{ + m_section = copy.m_section; + m_key = copy.m_key; + m_iniStorage = copy.m_iniStorage; + m_setCallback = copy.m_setCallback; + return *this; +} + +ConfigItem& ConfigItem::operator=(ConfigItem&& move) noexcept +{ + m_section = std::move(move.m_section); + m_key = std::move(move.m_key); + m_iniStorage = move.m_iniStorage; + m_setCallback = std::move(move.m_setCallback); + return *this; +} + +bool ConfigItem::isValid() const +{ + return true; +} + +std::string ConfigItem::getType() const +{ + return "ConfigItem"; +} + +std::string_view ConfigItem::getSection() const +{ + return m_section; +} + +std::string_view ConfigItem::getKey() const +{ + return m_key; +} + +bool ConfigItem::setValue(const std::string& value) +{ + if(not m_iniStorage.contains(m_section)) + { + return false; + } + + auto& field = m_iniStorage.at(m_section).at(m_key); + const auto old_value = field.as(); + + spdlog::info( + "Setting ConfigItem[{}][{}] to '{}' (was '{}')", + m_section, + m_key, + value, + old_value); + field = value; + + if(m_setCallback.has_value()) + { + spdlog::debug("Invoking callback for ConfigItem[{}][{}]", m_section, m_key); + std::invoke(m_setCallback.value(), old_value, value); + } + return true; +} + +CallbackGuard ConfigItem::setOnSetCallback(ConfigItemSetCallback&& callback) +{ + spdlog::debug("Setting OnSet callback for ConfigItem[{}][{}]", m_section, m_key); + m_setCallback = std::move(callback); + return CallbackGuard{*this}; +} + +void ConfigItem::removeOnSetCallback() +{ + spdlog::debug("Removing OnSet callback for ConfigItem[{}][{}]", m_section, m_key); + m_setCallback.reset(); +} + +std::string ConfigItem::getNativeValue() const +{ + return m_iniStorage.at(m_section).at(m_key).as(); +} + +} // namespace mono::config diff --git a/src/engine/module/config/src/ConfigLoader.cpp b/src/engine/module/config/src/ConfigLoader.cpp deleted file mode 100644 index 439bb599..00000000 --- a/src/engine/module/config/src/ConfigLoader.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "../include/config/ConfigLoader.hpp" - -#include - -#include - -#include "../include/config/RuntimeConfiguration.hpp" - -namespace mono -{ - -ConfigLoader& ConfigLoader::get() -{ - static ConfigLoader instance{}; - return instance; -} - -bool ConfigLoader::loadFromFile(const std::filesystem::path& path) -{ - spdlog::info("Loading config file: {}", path.string()); - if(not std::filesystem::exists(path)) - { - spdlog::error("Config file '{}' does not exist", path.string()); - return false; - } - - try - { - m_iniFile.load(path.string()); - } - catch(const std::logic_error& err) - { - spdlog::error("Failed to load config file '{}'", err.what()); - return false; - } - - return true; -} - -ConfigLoader::ConfigLoader() -{ - if(bool success = this->loadFromFile("../config/config.ini"); success) - { - this->updateRuntimeConfiguration(); - } -} - -void ConfigLoader::updateRuntimeConfiguration() const -{ - config::runtime::useOpenGl = m_iniFile.at("engine").at("UseOpenGL").as(); -} - -} // namespace mono diff --git a/src/engine/module/config/src/ConfigStorage.cpp b/src/engine/module/config/src/ConfigStorage.cpp new file mode 100644 index 00000000..455c40ad --- /dev/null +++ b/src/engine/module/config/src/ConfigStorage.cpp @@ -0,0 +1,64 @@ +#include "config/ConfigStorage.hpp" + +#include + +#include + +namespace mono::config +{ + +ConfigStorage::ConfigStorage(const std::filesystem::path& path) +{ + this->loadFromFile(path); +} + +bool ConfigStorage::validate() const +{ + // TODO(vis4rd): Check for duplicate sections + // TODO(vis4rd): Check for duplicate keys in sections + for(const auto& item : m_items) + { + if(not m_iniFile.contains(std::string{item->getSection()})) + { + spdlog::error("File config.ini is missing config section '{}'", item->getSection()); + return false; + } + if(not m_iniFile.at(std::string{item->getSection()}).contains(std::string{item->getKey()})) + { + spdlog::error( + "File config.ini is missing config field '{}' in section '{}'", + item->getKey(), + item->getSection()); + return false; + } + if(not item->isValid()) + { + return false; + } + } + return true; +} + +bool ConfigStorage::loadFromFile(const std::filesystem::path& path) +{ + spdlog::info("Loading config file: {}", path.string()); + if(not std::filesystem::exists(path)) + { + spdlog::error("Config file '{}' does not exist", path.string()); + return false; + } + + try + { + m_iniFile.load(path.string()); + } + catch(const std::logic_error& err) + { + spdlog::error("Failed to load config file '{}'", err.what()); + return false; + } + + return true; +} + +} // namespace mono::config diff --git a/src/engine/module/dev_ui/CMakeLists.txt b/src/engine/module/dev_ui/CMakeLists.txt new file mode 100644 index 00000000..6db515c6 --- /dev/null +++ b/src/engine/module/dev_ui/CMakeLists.txt @@ -0,0 +1,29 @@ +include_guard() + +add_library(mono_module_dev_ui) +add_library(mono::dev_ui ALIAS mono_module_dev_ui) + +if(DEBUG_BUILD) + target_compile_definitions(mono_module_dev_ui + PRIVATE "DEBUG_BUILD" + ) +endif() + +target_compile_features(mono_module_dev_ui + PRIVATE "cxx_std_20" +) + +target_sources(mono_module_dev_ui + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/DevUI.cpp" +) + +target_include_directories(mono_module_dev_ui + INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/mono" +) + +target_link_libraries(mono_module_dev_ui + PUBLIC imgui::imgui + spdlog::spdlog + mono::config +) diff --git a/src/engine/module/dev_ui/include/mono/dev_ui/DevUI.hpp b/src/engine/module/dev_ui/include/mono/dev_ui/DevUI.hpp new file mode 100644 index 00000000..dc137aa1 --- /dev/null +++ b/src/engine/module/dev_ui/include/mono/dev_ui/DevUI.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace mono::dev_ui +{ + +void initialize(); +void render(); + +} // namespace mono::dev_ui diff --git a/src/engine/module/dev_ui/src/DevUI.cpp b/src/engine/module/dev_ui/src/DevUI.cpp new file mode 100644 index 00000000..a638fd2b --- /dev/null +++ b/src/engine/module/dev_ui/src/DevUI.cpp @@ -0,0 +1,158 @@ +#include "dev_ui/DevUI.hpp" + +#include +#include +#include + +#include "mono/config/Config.hpp" + +namespace mono::dev_ui +{ + +namespace state +{ +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) + +static bool settings_window = false; +// static bool demo_window = false; + +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) +} // namespace state + +void initialize() { } + +void render() +{ + if constexpr(mono::config::constant::debugBuild) + { + constexpr ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings; + + float previous_pos_y{}; + float previous_size_y{}; + const auto update_prev_window = [&previous_pos_y, &previous_size_y]() { + previous_pos_y = ImGui::GetWindowPos().y; + previous_size_y = ImGui::GetWindowSize().y; + }; + const auto y_after_previous = [&previous_pos_y, &previous_size_y](float offset) { + return previous_pos_y + previous_size_y + offset; + }; + const float right_window_edge = ImGui::GetIO().DisplaySize.x - 10.0f; + const ImVec2 right_align_pivot = {1.0f, 0.0f}; + + ImGui::SetNextWindowPos( + ImVec2(right_window_edge, 10.0f), + ImGuiCond_Always, + ImVec2(1.0f, 0.0f)); + ImGui::Begin("Dev UI", nullptr, window_flags); + { + update_prev_window(); + ImGui::Selectable("Settings", &state::settings_window); + // ImGui::Selectable("Demo", &state::demo_window); + } + ImGui::End(); + + if(state::settings_window) + { + ImGui::SetNextWindowPos( + {right_window_edge, y_after_previous(10.0f)}, + ImGuiCond_Always, + right_align_pivot); + ImGui::Begin("Settings", &state::settings_window, window_flags); + { + update_prev_window(); + ImGui::SeparatorText("Engine"); + + { + auto loglevel_config = + mono::config::runtime + .get("engine", "LogLevel") + .value() + .get(); + const auto current_loglevel = loglevel_config.getValue().value(); + if(ImGui::BeginCombo("LogLevel", current_loglevel.data())) + { + for(const auto& level : spdlog::level_string_views) + { + bool is_selected = (current_loglevel.compare(level) == 0); + if(ImGui::Selectable(level.data(), is_selected)) + { + bool success = loglevel_config.setValue(std::string{level}); + if(success) + { + spdlog::set_level(spdlog::level_from_str(std::string{level})); + } + } + if(is_selected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + ImGui::Dummy({150.0f, 0.0f}); + } + + ImGui::SeparatorText("Window"); + { + { + auto window_mode_config = + mono::config::runtime + .get("engine.window", "Mode") + .value() + .get(); + const auto current_window_mode = + window_mode_config.getValue().value(); + if(ImGui::BeginCombo("Mode", current_window_mode.data())) + { + ; + for(const auto& mode : window_mode_config.getOptions()) + { + const bool is_selected = (current_window_mode.compare(mode) == 0); + if(ImGui::Selectable(mode.c_str(), is_selected)) + { + window_mode_config.setValue(mode); + } + if(is_selected) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + } + { + auto vsync_config = + mono::config::runtime + .get>("engine.window", "UseVSync") + .value() + .get(); + bool current_vsync = vsync_config.getValue().value(); + if(ImGui::Checkbox("UseVSync", ¤t_vsync)) + { + vsync_config.setValue(current_vsync); + } + } + } + } + ImGui::End(); + } + + // if(state::demo_window) + // { + // ImGui::SetNextWindowPos( + // {right_window_edge, y_after_previous(10.0f)}, + // ImGuiCond_Always, + // right_align_pivot); + // ImGui::Begin("Test123", &state::demo_window, window_flags); + // { + // update_prev_window(); + // ImGui::Text("Hello, world!"); + // } + // ImGui::End(); + // } + } +} + +} // namespace mono::dev_ui diff --git a/src/engine/module/input/CMakeLists.txt b/src/engine/module/input/CMakeLists.txt index 157360ec..e9550a4d 100644 --- a/src/engine/module/input/CMakeLists.txt +++ b/src/engine/module/input/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_input) add_library(mono::input ALIAS mono_module_input) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_input - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/input/include/input/Input.hpp b/src/engine/module/input/include/input/Input.hpp index 6c9b5360..c2e646e4 100644 --- a/src/engine/module/input/include/input/Input.hpp +++ b/src/engine/module/input/include/input/Input.hpp @@ -5,7 +5,7 @@ #include -#include "config/StaticConfiguration.hpp" +#include "mono/config/Config.hpp" #include "priv/InputData.hpp" #include "priv/KeyState.hpp" diff --git a/src/engine/module/input/src/Input.cpp b/src/engine/module/input/src/Input.cpp index 7dbff5ef..c9bb952b 100644 --- a/src/engine/module/input/src/Input.cpp +++ b/src/engine/module/input/src/Input.cpp @@ -7,7 +7,7 @@ namespace mono::input bool isPressed(std::int32_t key) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } @@ -17,7 +17,7 @@ bool isPressed(std::int32_t key) bool isPressedOnce(std::int32_t key) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } @@ -27,7 +27,7 @@ bool isPressedOnce(std::int32_t key) bool isHeld(std::int32_t key) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } @@ -37,7 +37,7 @@ bool isHeld(std::int32_t key) bool isReleased(std::int32_t key) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } @@ -47,7 +47,7 @@ bool isReleased(std::int32_t key) bool isIdle(std::int32_t key) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } @@ -57,7 +57,7 @@ bool isIdle(std::int32_t key) void pollEvents() { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } diff --git a/src/engine/module/input/src/Input.inl b/src/engine/module/input/src/Input.inl index 39926ac4..7bd89dc0 100644 --- a/src/engine/module/input/src/Input.inl +++ b/src/engine/module/input/src/Input.inl @@ -4,7 +4,7 @@ namespace mono::input bool arePressed(std::same_as auto... keys) { - if constexpr(config::constant::debugMode) + if constexpr(config::constant::debugBuild) { priv::ensureGlfwIsInitialized(); } diff --git a/src/engine/module/log/CMakeLists.txt b/src/engine/module/log/CMakeLists.txt index 3dd114fb..136737ea 100644 --- a/src/engine/module/log/CMakeLists.txt +++ b/src/engine/module/log/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_log) add_library(mono::log ALIAS mono_module_log) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_log - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() @@ -19,9 +19,12 @@ target_sources(mono_module_log target_include_directories(mono_module_log INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include" + PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/mono" ) target_link_libraries(mono_module_log PUBLIC spdlog::spdlog glad + mono::config + mono::util::compiler ) diff --git a/src/engine/module/log/include/log/Logging.hpp b/src/engine/module/log/include/log/Logging.hpp deleted file mode 100644 index 7a5a9869..00000000 --- a/src/engine/module/log/include/log/Logging.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -namespace mono::log -{ - -namespace priv -{ - -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -inline spdlog::source_loc location = spdlog::source_loc::current(); - -} // namespace priv - -template -void setGlObjectLabel( - GLenum identifier, - GLuint object, - const std::format_string& label, - ARGS&&... args); -void setGlLogLocation(const std::source_location& location = std::source_location::current()); -void enableOpenGlLogging(); - -template -void setGlObjectLabel( - GLenum identifier, - GLuint object, - const std::format_string& label, - ARGS&&... args) -{ - const std::string label_str = std::format(label, std::forward(args)...); - setGlLogLocation(); - glObjectLabel(identifier, object, static_cast(label_str.size()), label_str.data()); -} - -} // namespace mono::log diff --git a/src/engine/module/log/include/mono/log/Logging.hpp b/src/engine/module/log/include/mono/log/Logging.hpp new file mode 100644 index 00000000..9434683b --- /dev/null +++ b/src/engine/module/log/include/mono/log/Logging.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "priv/LoggingData.hpp" + +namespace mono::log +{ + +template +void setGlObjectLabel( + GLenum identifier, + GLuint object, + const std::format_string& label, + ARGS&&... args); +void setGlLogLocation(const std::source_location& location = std::source_location::current()); +void enableOpenGlLogging(); +void initialize(); + +template +void setGlObjectLabel( + GLenum identifier, + GLuint object, + const std::format_string& label, + ARGS&&... args) +{ + const std::string label_str = std::format(label, std::forward(args)...); + setGlLogLocation(); + glObjectLabel(identifier, object, static_cast(label_str.size()), label_str.data()); +} + +void critical(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->critical(fmt, std::forward(args)...); +} + +void error(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->error(fmt, std::forward(args)...); +} + +void warning(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->warn(fmt, std::forward(args)...); +} + +void info(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->info(fmt, std::forward(args)...); +} + +void debug(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->debug(fmt, std::forward(args)...); +} + +void trace(spdlog::loc_with_fmt fmt, auto&&... args) +{ + data::app_logger->trace(fmt, std::forward(args)...); +} + +} // namespace mono::log diff --git a/src/engine/module/log/include/mono/log/priv/BufferedSink.hpp b/src/engine/module/log/include/mono/log/priv/BufferedSink.hpp new file mode 100644 index 00000000..7d6bf1e8 --- /dev/null +++ b/src/engine/module/log/include/mono/log/priv/BufferedSink.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace mono::log::priv +{ + +class BufferedSink final : public spdlog::sinks::base_sink +{ + public: + const std::vector& buffer() const { return m_buffer; } + + void clear() { m_buffer.clear(); } + + private: + void sink_it_(const spdlog::details::log_msg& msg) final { m_buffer.emplace_back(msg); } + + void flush_() final + { + // Do nothing, as this is a buffered sink. + } + + private: + std::vector m_buffer{}; +}; + +} // namespace mono::log::priv diff --git a/src/engine/module/log/include/mono/log/priv/LoggingData.hpp b/src/engine/module/log/include/mono/log/priv/LoggingData.hpp new file mode 100644 index 00000000..4acf5431 --- /dev/null +++ b/src/engine/module/log/include/mono/log/priv/LoggingData.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include +#include + +namespace mono::log::data +{ + +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) +inline spdlog::source_loc location = spdlog::source_loc::current(); +inline std::shared_ptr engine_logger = nullptr; +inline std::shared_ptr app_logger = nullptr; +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) + +} // namespace mono::log::data diff --git a/src/engine/module/log/src/Logging.cpp b/src/engine/module/log/src/Logging.cpp index 2d1e158b..850f6b42 100644 --- a/src/engine/module/log/src/Logging.cpp +++ b/src/engine/module/log/src/Logging.cpp @@ -1,4 +1,16 @@ -#include "../include/log/Logging.hpp" +#include "log/Logging.hpp" + +#include +#include +#include +#include + +#include +#include + +#include "mono/config/Config.hpp" +#include "mono/config/types/OptionStringConfigItem.hpp" +#include "mono/util/Compiler.hpp" namespace mono::log { @@ -56,7 +68,7 @@ void openGlDebugMessageCallback( const void *user_param) { spdlog::log( - priv::location, + data::location, glSeverityToSpdlogLevel(severity), "\b[#{}][{}: {}] {}", id, @@ -68,7 +80,7 @@ void openGlDebugMessageCallback( void setGlLogLocation(const std::source_location &location) { // line number + 1 because the caller is the one that is interesting - priv::location = + data::location = spdlog::source_loc{location.file_name(), location.line() + 1, location.function_name()}; } @@ -97,4 +109,122 @@ void enableOpenGlLogging() } } +static std::chrono::system_clock::time_point getLocalTime() +{ + if constexpr(mono::util::isGnuCompiler()) + { + const std::time_t now = + std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + std::tm *local_tm = std::localtime(&now); + + return std::chrono::sys_days{ + std::chrono::year{local_tm->tm_year + 1900} / (local_tm->tm_mon + 1) + / (local_tm->tm_mday)} + + std::chrono::hours{local_tm->tm_hour} + std::chrono::minutes{local_tm->tm_min} + + std::chrono::seconds{local_tm->tm_sec}; + } + else + { + // For some reason, clang crashes when checking local timezone (?) + // so current UTC time is returned instead. + return std::chrono::system_clock::now(); + } +} + +static std::string buildPattern() +{ + // formatting spec: https://github.com/gabime/spdlog/wiki/3.-Custom-formatting + const bool is_debug = + mono::config::runtime.get("engine", "LogLevel") + .value() + .get() + .getValue() + .value_or(spdlog::level::info) + == spdlog::level::debug; + const std::string_view debug_thread_file = is_debug ? "[t:%=5!t][%s:%#]" : ""; + const std::string_view debug_time_precision = is_debug ? "%f" : "%e"; + return std::format( + "[%Y-%m-%d %T.{}][%^%=5!l%$][%=6n]{} %v", + debug_time_precision, + debug_thread_file); +} + +static std::vector buildSinks(std::chrono::system_clock::time_point local_time) +{ + const auto formatted_time = std::format("{:%F_%H-%M-%S}", local_time).substr(0, 19); + const auto file_name_current = std::format("../logs/{}.log", formatted_time); + const std::string file_name_latest = "../logs/latest.log"; + + { + // create log files + std::fstream f1{file_name_current}; + std::fstream f2{file_name_latest}; + } + + auto current_file_sink = + std::make_shared(file_name_current, true); + auto latest_file_sink = + std::make_shared(file_name_latest, true); + auto console_sink = std::make_shared(); + return {console_sink, current_file_sink, latest_file_sink}; +} + +void initialize() +{ + namespace fs = std::filesystem; + fs::create_directory("../logs"); + + const auto engine_pattern = buildPattern(); + const auto app_pattern = buildPattern(); + + const auto local_time = getLocalTime(); + const auto engine_sinks = buildSinks(local_time); + const auto app_sinks = buildSinks(local_time); + + spdlog::init_thread_pool(8192, 1, []() { + if(spdlog::default_logger()) + { + spdlog::info("Starting logging thread"); + } + }); + data::app_logger = std::make_shared( + "app", + app_sinks.begin(), + app_sinks.end(), + spdlog::thread_pool(), + spdlog::async_overflow_policy::overrun_oldest); + const auto log_level = + mono::config::runtime.get("engine", "LogLevel") + .value() + .get() + .getValue() + .value_or(spdlog::level::info); + data::app_logger->set_level(log_level); + data::app_logger->set_pattern(app_pattern); + spdlog::register_logger(data::app_logger); + + data::engine_logger = std::make_shared( + "engine", + engine_sinks.begin(), + engine_sinks.end(), + spdlog::thread_pool(), + spdlog::async_overflow_policy::overrun_oldest); + data::engine_logger->set_level(log_level); + data::engine_logger->set_pattern(engine_pattern); + spdlog::register_logger(data::engine_logger); + + if(config::priv::buffered_sink) + { + // flush everything that was logged before logging was initialized + for(const auto &msg : config::priv::buffered_sink->buffer()) + { + data::engine_logger->log(msg.time, msg.source, msg.log_level, msg.payload); + } + } + + spdlog::set_default_logger(data::engine_logger); + spdlog::debug("Logging initialized"); + spdlog::info("Start timestamp: {:%F %T}", local_time); +} + } // namespace mono::log diff --git a/src/engine/module/renderer/opengl/CMakeLists.txt b/src/engine/module/renderer/opengl/CMakeLists.txt index 507aaf35..83c19e43 100644 --- a/src/engine/module/renderer/opengl/CMakeLists.txt +++ b/src/engine/module/renderer/opengl/CMakeLists.txt @@ -5,9 +5,9 @@ add_library(mono::renderer_opengl ALIAS mono_module_renderer_opengl_backend) message(STATUS "Renderer: using OpenGL backend") -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_renderer_opengl_backend - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/renderer/opengl/include/opengl/gl/ElementBuffer.hpp b/src/engine/module/renderer/opengl/include/opengl/gl/ElementBuffer.hpp index cdb1eb3c..3dc1ee24 100644 --- a/src/engine/module/renderer/opengl/include/opengl/gl/ElementBuffer.hpp +++ b/src/engine/module/renderer/opengl/include/opengl/gl/ElementBuffer.hpp @@ -5,7 +5,7 @@ #include -#include "log/Logging.hpp" +#include "mono/log/Logging.hpp" #include "traits/ContiguousContainer.hpp" namespace mono::gl diff --git a/src/engine/module/renderer/opengl/include/opengl/gl/ShaderStorageBufferAny.hpp b/src/engine/module/renderer/opengl/include/opengl/gl/ShaderStorageBufferAny.hpp index 8affbfd0..2aa40e1f 100644 --- a/src/engine/module/renderer/opengl/include/opengl/gl/ShaderStorageBufferAny.hpp +++ b/src/engine/module/renderer/opengl/include/opengl/gl/ShaderStorageBufferAny.hpp @@ -9,7 +9,7 @@ #include -#include "log/Logging.hpp" +#include "mono/log/Logging.hpp" #include "traits/ContiguousContainer.hpp" namespace mono::gl diff --git a/src/engine/module/renderer/opengl/include/opengl/gl/VertexBuffer.hpp b/src/engine/module/renderer/opengl/include/opengl/gl/VertexBuffer.hpp index 4e482339..cc7c3306 100644 --- a/src/engine/module/renderer/opengl/include/opengl/gl/VertexBuffer.hpp +++ b/src/engine/module/renderer/opengl/include/opengl/gl/VertexBuffer.hpp @@ -7,7 +7,7 @@ #include #include "ShaderAttributeLayout.hpp" -#include "log/Logging.hpp" +#include "mono/log/Logging.hpp" namespace mono::gl { diff --git a/src/engine/module/renderer/opengl/src/gl/FrameBuffer.cpp b/src/engine/module/renderer/opengl/src/gl/FrameBuffer.cpp index 6f790c58..30af8a62 100644 --- a/src/engine/module/renderer/opengl/src/gl/FrameBuffer.cpp +++ b/src/engine/module/renderer/opengl/src/gl/FrameBuffer.cpp @@ -1,6 +1,6 @@ #include "../../include/opengl/gl/FrameBuffer.hpp" -#include "log/Logging.hpp" +#include "mono/log/Logging.hpp" namespace mono::gl { diff --git a/src/engine/module/renderer/opengl/src/shader/Shader.cpp b/src/engine/module/renderer/opengl/src/shader/Shader.cpp index 04c6d544..b926da27 100644 --- a/src/engine/module/renderer/opengl/src/shader/Shader.cpp +++ b/src/engine/module/renderer/opengl/src/shader/Shader.cpp @@ -5,7 +5,7 @@ #include -#include "log/Logging.hpp" +#include "mono/log/Logging.hpp" #include "opengl/shader/ShaderType.hpp" namespace mono::gl diff --git a/src/engine/module/renderer/opengl/src/shader/ShaderProgram.cpp b/src/engine/module/renderer/opengl/src/shader/ShaderProgram.cpp index f1119c07..b526bfd0 100644 --- a/src/engine/module/renderer/opengl/src/shader/ShaderProgram.cpp +++ b/src/engine/module/renderer/opengl/src/shader/ShaderProgram.cpp @@ -2,7 +2,7 @@ #include -#include +#include #include "opengl/shader/Shader.hpp" diff --git a/src/engine/module/renderer/opengl/src/target/RenderWindow.cpp b/src/engine/module/renderer/opengl/src/target/RenderWindow.cpp index 0570f1b8..d37e3e38 100644 --- a/src/engine/module/renderer/opengl/src/target/RenderWindow.cpp +++ b/src/engine/module/renderer/opengl/src/target/RenderWindow.cpp @@ -5,8 +5,8 @@ #include #include -#include "config/StaticConfiguration.hpp" -#include "log/Logging.hpp" +#include "mono/config/Config.hpp" +#include "mono/log/Logging.hpp" #include "opengl/shader/ShaderManager.hpp" namespace mono::gl @@ -432,7 +432,12 @@ void RenderWindow::initGl() const spdlog::debug("Initializing GL"); // logging - if constexpr(mono::config::constant::debugMode) + if(mono::config::runtime.get("engine", "LogLevel") + .value() + .get() + .getValue() + .value_or(spdlog::level::info) + <= spdlog::level::debug) { log::enableOpenGlLogging(); } @@ -452,10 +457,6 @@ void RenderWindow::initImGui() const auto &io = ImGui::GetIO(); (void)io; - if constexpr(mono::config::constant::debugMode) - { - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - } io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.Fonts->AddFontDefault(); diff --git a/src/engine/module/section/CMakeLists.txt b/src/engine/module/section/CMakeLists.txt index fa93958d..b9f4c415 100644 --- a/src/engine/module/section/CMakeLists.txt +++ b/src/engine/module/section/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_section) add_library(mono::section ALIAS mono_module_section) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_section - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/traits/CMakeLists.txt b/src/engine/module/traits/CMakeLists.txt index a86a8550..91a35d40 100644 --- a/src/engine/module/traits/CMakeLists.txt +++ b/src/engine/module/traits/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_traits INTERFACE) add_library(mono::traits ALIAS mono_module_traits) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_traits - INTERFACE "DEBUG_MODE" + INTERFACE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/traits/include/traits/Arithmetic.hpp b/src/engine/module/traits/include/traits/Arithmetic.hpp new file mode 100644 index 00000000..a760f143 --- /dev/null +++ b/src/engine/module/traits/include/traits/Arithmetic.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace mono +{ + +template +concept ArithmeticTrait = + (std::integral or std::floating_point) and not std::same_as; + +} diff --git a/src/engine/module/ui/CMakeLists.txt b/src/engine/module/ui/CMakeLists.txt index d4fd1c2a..da74ad46 100644 --- a/src/engine/module/ui/CMakeLists.txt +++ b/src/engine/module/ui/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_module_ui) add_library(mono::ui ALIAS mono_module_ui) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_module_ui - PRIVATE "DEBUG_MODE" + PRIVATE "DEBUG_BUILD" ) endif() diff --git a/src/engine/module/ui/include/ui/BaseLayout.hpp b/src/engine/module/ui/include/ui/BaseLayout.hpp index 3a91848a..e50203ff 100644 --- a/src/engine/module/ui/include/ui/BaseLayout.hpp +++ b/src/engine/module/ui/include/ui/BaseLayout.hpp @@ -2,7 +2,7 @@ #include -#include "config/StaticConfiguration.hpp" +#include "mono/config/Config.hpp" // NOLINTNEXTLINE(readability-identifier-naming) namespace UI @@ -54,10 +54,11 @@ struct BaseLayout float buttonHS = m_baseHSpacing * m_scaleH; - ImGuiWindowFlags windowFlags = - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove - | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoDocking; + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar + | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize + | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoBringToFrontOnFocus + | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoBackground; }; BaseLayout::BaseLayout(const ImVec2& workpos, const ImVec2& worksize) @@ -65,12 +66,7 @@ BaseLayout::BaseLayout(const ImVec2& workpos, const ImVec2& worksize) , viewportH(worksize.y) , viewportX(workpos.x) , viewportY(workpos.y) -{ - if constexpr(not mono::config::constant::debugMode) - { - windowFlags |= ImGuiWindowFlags_NoBackground; - } -} +{ } void BaseLayout::update(const ImVec2& workpos, const ImVec2& worksize) { diff --git a/src/engine/module/util/CMakeLists.txt b/src/engine/module/util/CMakeLists.txt index cacbdf15..54dc4ba7 100644 --- a/src/engine/module/util/CMakeLists.txt +++ b/src/engine/module/util/CMakeLists.txt @@ -2,6 +2,7 @@ include_guard() add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/packed_variable") add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/index_of") +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/compiler") add_library(mono_module_util INTERFACE) add_library(mono::util ALIAS mono_module_util) @@ -9,4 +10,5 @@ add_library(mono::util ALIAS mono_module_util) target_link_libraries(mono_module_util INTERFACE mono::util::pv mono::util::index_of + mono::util::compiler ) diff --git a/src/engine/module/util/compiler/CMakeLists.txt b/src/engine/module/util/compiler/CMakeLists.txt new file mode 100644 index 00000000..7bd11620 --- /dev/null +++ b/src/engine/module/util/compiler/CMakeLists.txt @@ -0,0 +1,6 @@ +include_guard() + +add_library(mono_util_compiler INTERFACE) +add_library(mono::util::compiler ALIAS mono_util_compiler) +target_include_directories(mono_util_compiler INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_compile_features(mono_util_compiler INTERFACE cxx_std_20) diff --git a/src/engine/module/util/compiler/include/mono/util/Compiler.hpp b/src/engine/module/util/compiler/include/mono/util/Compiler.hpp new file mode 100644 index 00000000..5a3723f7 --- /dev/null +++ b/src/engine/module/util/compiler/include/mono/util/Compiler.hpp @@ -0,0 +1,24 @@ +#pragma once + +#ifndef __clang_major__ + #define __clang_major__ 0 +#endif + +#ifndef __GNUC__ + #define __GNUC__ 0 +#endif + +namespace mono::util +{ + +consteval bool isGnuCompiler() +{ + return __GNUC__ > 0; +} + +consteval bool isClangCompiler() +{ + return __clang_major__ > 0; +} + +} // namespace mono::util diff --git a/src/engine/module/util/packed_variable/CMakeLists.txt b/src/engine/module/util/packed_variable/CMakeLists.txt index 7e725ef8..6f7d3aef 100644 --- a/src/engine/module/util/packed_variable/CMakeLists.txt +++ b/src/engine/module/util/packed_variable/CMakeLists.txt @@ -3,9 +3,9 @@ include_guard() add_library(mono_util_pv INTERFACE) add_library(mono::util::pv ALIAS mono_util_pv) -if(DEBUG_MODE) +if(DEBUG_BUILD) target_compile_definitions(mono_util_pv - INTERFACE "DEBUG_MODE" + INTERFACE "DEBUG_BUILD" ) endif() diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 3ee07461..cfd31c7a 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -20,7 +20,7 @@ set_target_properties(monoshot PROPERTIES target_compile_definitions(monoshot PRIVATE - $<$:DEBUG_MODE> + $<$:DEBUG_BUILD> $, MONOSHOT_VERSION="${MONOSHOT_VERSION}", MONOSHOT_VERSION="unknown" @@ -53,8 +53,8 @@ target_link_libraries(monoshot mono::time mono::config mono::cstring + mono::dev_ui EnTT::EnTT glm::glm tinyfd::tinyfd - spdlog::spdlog ) diff --git a/src/game/include/App.hpp b/src/game/include/App.hpp index 4c18e8f7..f6a10fe9 100644 --- a/src/game/include/App.hpp +++ b/src/game/include/App.hpp @@ -1,11 +1,10 @@ #pragma once #include -#include -#include #include #include #include +#include #include #include #include @@ -18,7 +17,7 @@ class App final { public: - App(const std::string& window_title, uint32_t width, uint32_t height); + App(const std::string& window_title); App(const App&) = delete; App(App&&) = delete; ~App() noexcept; @@ -26,7 +25,6 @@ class App final App& operator=(const App&) = delete; App& operator=(App&&) = delete; - static void initLogger() noexcept; void initTextures() noexcept; void initFonts() noexcept; @@ -40,7 +38,6 @@ class App final void destroyFonts() noexcept; private: - mono::ConfigLoader& m_configLoader; std::shared_ptr m_window; std::shared_ptr m_timer; SectionManager& m_sectionManager; @@ -50,12 +47,12 @@ void App::update(UpdateableTrait auto&&... updateables) noexcept { if(m_sectionManager.size() == 0) { - spdlog::debug("SectionManager asks to close the window"); + mono::log::debug("SectionManager asks to close the window"); m_window->requestClose(); } if(m_window->shouldClose()) { - spdlog::debug("Window should close, clearing SectionManager, returning..."); + mono::log::debug("Window should close, clearing SectionManager, returning..."); m_sectionManager.clear(); return; } @@ -64,19 +61,11 @@ void App::update(UpdateableTrait auto&&... updateables) noexcept if(mono::input::isPressedOnce(GLFW_KEY_F11)) { auto size = m_window->getSize(); - spdlog::debug("on F11: window size = {}x{}", size.x, size.y); + mono::log::debug("on F11: window size = {}x{}", size.x, size.y); m_window->toggleFullscreen(); size = m_window->getSize(); - spdlog::debug("after F11: window size = {}x{}", size.x, size.y); - } - if constexpr(mono::config::constant::debugMode) - { - if(mono::input::isPressedOnce(GLFW_KEY_APOSTROPHE)) // debugging purposes - { - static int break_count; - spdlog::debug("======================= {} =========================", break_count++); - } + mono::log::debug("after F11: window size = {}x{}", size.x, size.y); } if(m_sectionManager.size() == 1) { @@ -107,47 +96,7 @@ void App::render(RenderableTrait auto&&... renderables) noexcept (renderables.render(), ...); } - // Debug panel - if constexpr(mono::config::constant::debugMode) - { - static bool show_debug_panel = true; - static bool enable_vsync = m_window->isVerticalSyncEnabled(); - static bool enable_fullscreen = m_window->isFullscreen(); - if(show_debug_panel) - { - static bool show_demo_window = false; - ImGui::Begin("Debug Panel"); - { - ImGui::Checkbox("Demo Window", &show_demo_window); - if(show_demo_window) - { - ImGui::ShowDemoWindow(&show_demo_window); - } - - if(ImGui::Checkbox("Toggle VSYNC", &enable_vsync)) - { - m_window->setVerticalSync(enable_vsync); - } - - if(ImGui::Checkbox("Toggle fullscreen", &enable_fullscreen)) - { - m_window->setFullscreen(enable_fullscreen); - } - - const auto size = m_window->getSize(); - ImGui::Text("Window size: (%d, %d)", size.x, size.y); - ImGui::Text( - "Performance: [%.2fms] [%.0ffps]", - 1000.0f / ImGui::GetIO().Framerate, - ImGui::GetIO().Framerate); - ImGui::Text( - "Mouse Position: Screen[%.2fx, %.2fy]", - ImGui::GetMousePos().x, - ImGui::GetMousePos().y); - } - ImGui::End(); - } - } + mono::dev_ui::render(); mono::renderer::render(); diff --git a/src/game/include/ui/LowerNavigationBox.hpp b/src/game/include/ui/LowerNavigationBox.hpp index 9d28405b..29706a60 100644 --- a/src/game/include/ui/LowerNavigationBox.hpp +++ b/src/game/include/ui/LowerNavigationBox.hpp @@ -22,14 +22,14 @@ inline void LowerNavigationBox(const UI::BaseLayout& layout) ImGui::SetCursorScreenPos({layout.menuX + layout.buttonWS, layout.menuY + layout.buttonHS}); if(ImGui::Button("Close", {layout.buttonW, layout.buttonH})) { - spdlog::debug("Clicking 'Close'"); + mono::log::debug("Clicking 'Close'"); auto& sm = SectionManager::get(); sm.popSection(); } // else if(ImGui::SameLine(); ImGui::Button("Other option", {layout.buttonW, // layout.buttonH})) // { - // spdlog::debug("Clicking 'Other option'"); + // mono::log::debug("Clicking 'Other option'"); // } } ImGui::End(); diff --git a/src/game/include/ui/LowerNavigationBoxLayout.hpp b/src/game/include/ui/LowerNavigationBoxLayout.hpp index 7877643e..2a2e991a 100644 --- a/src/game/include/ui/LowerNavigationBoxLayout.hpp +++ b/src/game/include/ui/LowerNavigationBoxLayout.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "SettingsLayout.hpp" @@ -19,9 +19,9 @@ LowerNavigationBoxLayout::LowerNavigationBoxLayout(const ImVec2& workpos, const m_baseMenuW = 1840.f; this->update(workpos, worksize); - spdlog::debug("box size = ({}, {})", menuW, menuH); - spdlog::debug("box pos = ({}, {})", menuX, menuY); - spdlog::debug("button size = [{}({}, {}){}]", buttonWS, buttonW, buttonH, buttonHS); + mono::log::debug("box size = ({}, {})", menuW, menuH); + mono::log::debug("box pos = ({}, {})", menuX, menuY); + mono::log::debug("button size = [{}({}, {}){}]", buttonWS, buttonW, buttonH, buttonHS); } void LowerNavigationBoxLayout::update(const ImVec2& workpos, const ImVec2& worksize) diff --git a/src/game/main.cpp b/src/game/main.cpp index 894dcbc6..2311e689 100644 --- a/src/game/main.cpp +++ b/src/game/main.cpp @@ -1,10 +1,14 @@ +#include +#include + #include "include/App.hpp" int main(int, char**) { - App::initLogger(); + mono::config::initialize(); + mono::log::initialize(); - App app("MONOSHOT", 1280, 720); + App app("MONOSHOT"); app.run(); app.terminate(EXIT_SUCCESS); diff --git a/src/game/src/App.cpp b/src/game/src/App.cpp index 1e8f0619..b1ac4e15 100644 --- a/src/game/src/App.cpp +++ b/src/game/src/App.cpp @@ -3,24 +3,39 @@ #include #include +#include +#include #include #include #include #include #include -#include -#include #include #include "../include/section/MainMenuSection.hpp" +#include "mono/config/types/BasicConfigItem.hpp" +#include "mono/config/types/OptionStringConfigItem.hpp" -App::App(const std::string& window_title, uint32_t width, uint32_t height) - : m_configLoader(mono::ConfigLoader::get()) - , m_sectionManager(SectionManager::get()) +App::App(const std::string& window_title) + : m_sectionManager(SectionManager::get()) { - spdlog::info("App version: {}", MONOSHOT_VERSION); + mono::log::info("App version: {}", MONOSHOT_VERSION); - m_window = std::make_shared(width, height, window_title); + + const auto resolution = []() -> glm::ivec2 { + const auto resolution_config = + mono::config::runtime.get>( + "engine.window", + "Resolution"); + if(resolution_config.has_value()) + { + const auto& res = resolution_config.value().get(); + return res.getValue().value_or(glm::ivec2{1920, 1080}); + } + return {1920, 1080}; + }(); + + m_window = std::make_shared(resolution.x, resolution.y, window_title); ResourceManager::window = m_window; { @@ -47,18 +62,41 @@ App::App(const std::string& window_title, uint32_t width, uint32_t height) mono::renderer::addPipeline(std::move(pipeline)); } - if constexpr(mono::config::constant::debugMode) // Debug Build + const auto window_mode = []() -> std::string { + const auto conf = mono::config::runtime.get( + "engine.window", + "Mode"); + if(conf.has_value()) + { + return conf.value().get().getValue().value_or("borderless"); + } + return "borderless"; + }(); + + if(window_mode.compare("borderless") == 0) { - // windowed, vsync - m_window->setFullscreen(false); - m_window->setVerticalSync(true); + m_window->setBorderlessFullscreen(); } - else // Release Build + else if(window_mode.compare("fullscreen") == 0) { - // fullscreen, no-vsync - m_window->setFullscreen(true); - m_window->setVerticalSync(false); + m_window->setFullscreen(); } + else + { + m_window->setFullscreen(false); + } + + const auto vsync_enabled = []() { + const auto conf = mono::config::runtime.get>( + "engine.window", + "UseVSync"); + if(conf.has_value()) + { + return conf.value().get().getValue().value_or(true); + } + return true; + }(); + m_window->setVerticalSync(vsync_enabled); m_timer = std::make_shared(); ResourceManager::timer = m_timer; @@ -79,57 +117,9 @@ App::~App() noexcept ResourceManager::window.reset(); } -void App::initLogger() noexcept -{ - // TODO(vis4rd): move logging initialization to engine - namespace fs = std::filesystem; - fs::create_directory("../logs"); - - constexpr mono::cstring info_pattern{"[%Y-%m-%d %T.%e][%^%l%$] %v"}; - constexpr mono::cstring debug_pattern{"[%Y-%m-%d %T.%e][%^%l%$][thread %t][%s:%#] %v"}; - spdlog::level log_level{spdlog::level::info}; - std::string log_pattern{info_pattern}; - - if constexpr(mono::config::constant::debugMode) - { - log_level = spdlog::level::debug; - log_pattern = debug_pattern; - } - - auto console_sink = std::make_shared(); - console_sink->set_pattern(log_pattern); - console_sink->set_level(log_level); - - std::string file_name = std::format("../logs/{}", std::chrono::system_clock::now()); - file_name.replace(file_name.find(' '), 1, "_"); - file_name.replace(file_name.find(':'), 1, "-"); - file_name.replace(file_name.find(':'), 1, "-"); - file_name = file_name.substr(0, file_name.rfind('.')); - if constexpr(mono::config::constant::debugMode) - { - file_name += "_debug"; - } - file_name += ".log"; - - { - // create a file - std::fstream file{file_name}; - } - - auto file_sink = std::make_shared(file_name, true); - file_sink->set_pattern(log_pattern); - file_sink->set_level(log_level); - - spdlog::logger multisink_logger("logger", {console_sink, file_sink}); - multisink_logger.set_level(log_level); - - spdlog::set_default_logger(std::make_shared(multisink_logger)); - spdlog::debug("Logging initialized"); -} - void App::initTextures() noexcept { - spdlog::info("Loading textures"); + mono::log::info("Loading textures"); using res = ResourceManager; res::largeTreeTexture = Resource::create("../res/textures/large_tree.png", 128, 128); @@ -166,7 +156,7 @@ void App::initTextures() noexcept void App::initFonts() noexcept { - spdlog::info("Loading fonts"); + mono::log::info("Loading fonts"); using res = ResourceManager; const auto& window_width = res::window->getSize().x; @@ -191,7 +181,7 @@ void App::initFonts() noexcept void App::run() noexcept { - spdlog::info("Starting main application loop"); + mono::log::info("Starting main application loop"); while(true) { this->update(m_sectionManager); @@ -202,12 +192,12 @@ void App::run() noexcept m_timer->update(); this->render(m_sectionManager); } - spdlog::info("Stopped main application loop"); + mono::log::info("Stopped main application loop"); } void App::terminate(int code) noexcept { - spdlog::info("Closing the application"); + mono::log::info("Closing the application"); } void App::destroyTextures() noexcept diff --git a/src/game/src/gameplay/Hero.cpp b/src/game/src/gameplay/Hero.cpp index afeebfb3..99b0a83f 100644 --- a/src/game/src/gameplay/Hero.cpp +++ b/src/game/src/gameplay/Hero.cpp @@ -1,7 +1,6 @@ #include "../../include/gameplay/Hero.hpp" #include -#include Hero::Hero( const std::int32_t& health, diff --git a/src/game/src/gameplay/items/Weapon.cpp b/src/game/src/gameplay/items/Weapon.cpp index 803ee019..998de2bd 100644 --- a/src/game/src/gameplay/items/Weapon.cpp +++ b/src/game/src/gameplay/items/Weapon.cpp @@ -2,8 +2,6 @@ #include -#include - Weapon::Weapon( const std::int32_t& damage, const std::uint32_t& magazine_capacity, diff --git a/src/game/src/map/Map.cpp b/src/game/src/map/Map.cpp index 90b56d1d..3bd4ca49 100644 --- a/src/game/src/map/Map.cpp +++ b/src/game/src/map/Map.cpp @@ -22,7 +22,7 @@ Map::Map(const std::size_t& width, const std::size_t& height) Map::~Map() { - spdlog::trace("Deleting Map instance"); + mono::log::trace("Deleting Map instance"); } std::size_t Map::getSize() const @@ -42,7 +42,7 @@ const std::size_t& Map::getHeight() const void Map::addObject(const glm::vec2& position, const float& rotation, ObjectID object_id) { - spdlog::debug( + mono::log::debug( "Placed MapObject: OID = {}, pos = ({}, {}), rot = {}", objectIdToString(object_id), position.x, @@ -80,7 +80,7 @@ void Map::removeObject(const glm::vec2& position) void Map::setTile(const Tile& tile) { - spdlog::debug( + mono::log::debug( "Map: Placing a tile with coords ({}, {}), rotation {}, block_id {}", tile.x, tile.y, @@ -149,7 +149,7 @@ void Map::addTilesToRegistry(entt::registry& registry) const pos.y = tile.y; rot.data = tile.rotation; size = {1.f, 1.f}; - spdlog::debug( + mono::log::debug( "Filling ECS registry with tile pos = ({}, {}), rot = {}", pos.x, pos.y, @@ -169,18 +169,22 @@ void Map::addTilesToRegistry(entt::registry& registry) const pos = object.getPosition(); rot = object.getRotation(); size = object.getSize(); - spdlog::debug("Filling ECS registry with tile pos = ({}, {}), rot = {}", pos.x, pos.y, rot); + mono::log::debug( + "Filling ECS registry with tile pos = ({}, {}), rot = {}", + pos.x, + pos.y, + rot); } } void Map::loadFromFile(const std::string& filename, entt::registry& enemy_registry) { - spdlog::debug("Loading map from file '{}'...", filename); + mono::log::debug("Loading map from file '{}'...", filename); std::ifstream file(filename); if(!file.is_open() || !file.good()) { - spdlog::debug("Couldn't open file: {}", filename); + mono::log::debug("Couldn't open file: {}", filename); return; } @@ -204,7 +208,7 @@ void Map::loadFromFile(const std::string& filename, entt::registry& enemy_regist std::stringstream line_buffer(line); line_buffer >> tile_x >> tile_y >> tile_rotation >> tile_block >> tile_solid; this->setTile(tile_x, tile_y, tile_rotation, static_cast(tile_block), tile_solid); - spdlog::debug( + mono::log::debug( "Loading Tile: pos = ({}, {}), rot = {}, solid = {}, ID = '{}'", tile_x, tile_y, @@ -237,7 +241,7 @@ void Map::loadFromFile(const std::string& filename, entt::registry& enemy_regist {object_pos_x, object_pos_y}, object_rotation, static_cast(object_id)); - spdlog::debug( + mono::log::debug( "Loading MapObject: pos = ({}, {}), size = ({}, {}), rot = {}, solid = {}, ID = '{}'", object_pos_x, object_pos_y, @@ -260,15 +264,15 @@ void Map::loadFromFile(const std::string& filename, entt::registry& enemy_regist {enemy_pos_x, enemy_pos_y}, {1.f, 1.f}, enemy_rotation); - spdlog::debug("Loading Enemy: pos = ({}, {})", enemy_pos_x, enemy_pos_y); + mono::log::debug("Loading Enemy: pos = ({}, {})", enemy_pos_x, enemy_pos_y); } - spdlog::debug("Map loaded from file successfully"); + mono::log::debug("Map loaded from file successfully"); } void Map::saveToFile(const std::string& filename, const entt::registry& enemy_registry) { - spdlog::debug("Saving map to file '{}'...", filename); + mono::log::debug("Saving map to file '{}'...", filename); // clang-format off std::sort(m_tiles.begin(), m_tiles.end(), [](const Tile& tile1, const Tile& tile2) @@ -284,7 +288,7 @@ void Map::saveToFile(const std::string& filename, const entt::registry& enemy_re { file_buffer << tile.x << ' ' << tile.y << ' ' << tile.rotation << ' ' << tile.blockId << ' ' << tile.solid << '\n'; - spdlog::debug( + mono::log::debug( "Saving Tile: pos = ({}, {}), rot = {}, solid = {}, ID = '{}'", tile.x, tile.y, @@ -303,7 +307,7 @@ void Map::saveToFile(const std::string& filename, const entt::registry& enemy_re file_buffer << pos.x << ' ' << pos.y << ' ' << size.x << ' ' << size.y << ' ' << object.getRotation() << ' ' << object.hasCollision << ' ' << object.id << '\n'; - spdlog::debug( + mono::log::debug( "Saving MapObject: pos = ({}, {}), size = ({}, {}), rot = {}, solid = {}, ID = '{}'", pos.x, pos.y, @@ -321,20 +325,20 @@ void Map::saveToFile(const std::string& filename, const entt::registry& enemy_re for(auto&& [enemy, pos, rot] : view.each()) { file_buffer << pos.x << ' ' << pos.y << ' ' << rot.data << '\n'; - spdlog::debug("Saving Enemy: pos = ({}, {}), rot = {}", pos.x, pos.y, rot.data); + mono::log::debug("Saving Enemy: pos = ({}, {}), rot = {}", pos.x, pos.y, rot.data); } // dump buffer to the file std::ofstream file(filename); if(!file.is_open() || !file.good()) { - spdlog::error("Could not open file '{}', can not save the map", filename); + mono::log::error("Could not open file '{}', can not save the map", filename); return; } file << file_buffer.rdbuf(); file.close(); - spdlog::debug("Map saved to file successfully"); + mono::log::debug("Map saved to file successfully"); } void Map::setTheme(const MapTheme& new_theme) @@ -417,21 +421,21 @@ void Map::calculateNewSize(const float& tile_x, const float& tile_y) if(abs_tile_center_x > center_x) { m_width = abs_tile_center_x * 2 + 1; - spdlog::trace( + mono::log::trace( "Map: tile.x > center_x ({} > {}): new_width = {}", abs_tile_center_x, center_x, m_width); - spdlog::debug("Map: Tile is out of bounds, new map width = {}", m_width); + mono::log::debug("Map: Tile is out of bounds, new map width = {}", m_width); } if(abs_tile_center_y > center_y) { m_height = abs_tile_center_y * 2 + 1; - spdlog::trace( + mono::log::trace( "Map: tile.y > center_y (|{}| > {}): new_height = {}", abs_tile_center_y, center_y, m_height); - spdlog::debug("Map: Tile is out of bounds, new map height = {}", m_height); + mono::log::debug("Map: Tile is out of bounds, new map height = {}", m_height); } } diff --git a/src/game/src/section/CreatorSection.cpp b/src/game/src/section/CreatorSection.cpp index c9c5df88..dfa79f58 100644 --- a/src/game/src/section/CreatorSection.cpp +++ b/src/game/src/section/CreatorSection.cpp @@ -55,7 +55,7 @@ void CreatorSection::update() noexcept const double velocity = base_velocity * delta_time * pos.z; if(mono::input::isPressedOnce(GLFW_KEY_ESCAPE)) { - spdlog::debug("Lets pop some sections"); + mono::log::debug("Lets pop some sections"); SectionManager::get().popSection(); } if(mono::input::isHeld(GLFW_KEY_A)) @@ -150,7 +150,7 @@ void CreatorSection::update() noexcept m_mouseWorldPos, {1.f, 1.f}, m_randomizedRotation); - spdlog::debug( + mono::log::debug( "Map: Placing an enemy with coords ({}, {})", m_mouseWorldPos.x, m_mouseWorldPos.y); @@ -318,7 +318,7 @@ void CreatorSection::render() noexcept (blockToString(block_id) + std::string("##unique_id")).c_str(), &(checks.at(block_id - BlockID::FIRST_BLOCK - 1)))) { - spdlog::debug("Selected Block '{}'", blockToString(block_id)); + mono::log::debug("Selected Block '{}'", blockToString(block_id)); m_selectedMapItem = block_id; preview = blockToString(block_id); } @@ -331,7 +331,7 @@ void CreatorSection::render() noexcept (objectIdToString(object_id) + std::string("##unique_id")).c_str(), &(checks.at(BlockID::BLOCK_COUNT + object_id - ObjectID::FIRST_OBJECT - 1)))) { - spdlog::debug("Selected MapObject '{}'", objectIdToString(object_id)); + mono::log::debug("Selected MapObject '{}'", objectIdToString(object_id)); m_selectedMapItem = object_id; preview = objectIdToString(object_id); } @@ -342,13 +342,13 @@ void CreatorSection::render() noexcept [static_cast(BlockID::BLOCK_COUNT) + static_cast(ObjectID::OBJECT_COUNT)]))) { - spdlog::debug("Selected End Area"); + mono::log::debug("Selected End Area"); m_selectedMapItem = 9999; preview = "End Area"; } if(ImGui::Selectable("Enemy##unique_id", &(checks.back()))) { - spdlog::debug("Selected Enemy"); + mono::log::debug("Selected Enemy"); m_selectedMapItem = 10000; preview = "Enemy"; } diff --git a/src/game/src/section/DebugSection.cpp b/src/game/src/section/DebugSection.cpp index 267d15a6..2b75ecdf 100644 --- a/src/game/src/section/DebugSection.cpp +++ b/src/game/src/section/DebugSection.cpp @@ -54,7 +54,7 @@ DebugSection::DebugSection() DebugSection::~DebugSection() { - spdlog::trace("Destroying DebugSection"); + mono::log::trace("Destroying DebugSection"); m_mapElementsRegistry.clear(); } @@ -159,7 +159,7 @@ void DebugSection::update() noexcept void DebugSection::render() noexcept { - spdlog::trace("Rendering DebugSection"); + mono::log::trace("Rendering DebugSection"); // m_renderer.beginBatch(); @@ -402,19 +402,19 @@ void DebugSection::showDebugUI(bool& draw_area, bool& draw_bounding_boxes) if(ImGui::Selectable("Tutorial Theme##unique_id", &check)) { preview = "Tutorial Theme"; - spdlog::debug("Switching MapTheme to '{}'", preview); + mono::log::debug("Switching MapTheme to '{}'", preview); m_map.setTheme(MapThemes::tutorialTheme); } if(ImGui::Selectable("Forest Theme##unique_id", &check)) { preview = "Forest Theme"; - spdlog::debug("Switching MapTheme to '{}'", preview); + mono::log::debug("Switching MapTheme to '{}'", preview); m_map.setTheme(MapThemes::forestTheme); } if(ImGui::Selectable("Winter Theme##unique_id", &check)) { preview = "Winter Theme"; - spdlog::debug("Switching MapTheme to '{}'", preview); + mono::log::debug("Switching MapTheme to '{}'", preview); m_map.setTheme(MapThemes::winterTheme); } ImGui::EndCombo(); diff --git a/src/game/src/section/GameplaySection.cpp b/src/game/src/section/GameplaySection.cpp index 64406bab..ea521ff1 100644 --- a/src/game/src/section/GameplaySection.cpp +++ b/src/game/src/section/GameplaySection.cpp @@ -22,7 +22,7 @@ GameplaySection::GameplaySection() GameplaySection::~GameplaySection() { - spdlog::trace("Destroying {}", m_name); + mono::log::trace("Destroying {}", m_name); m_mapElementsRegistry.clear(); } @@ -133,7 +133,7 @@ void GameplaySection::update() noexcept void GameplaySection::render() noexcept { - spdlog::trace("Rendering GameplaySection"); + mono::log::trace("Rendering GameplaySection"); // m_renderer.beginBatch(); m_map.drawTiles(); @@ -216,30 +216,6 @@ void GameplaySection::render() noexcept ImGui::End(); font_guard.popFont(); } - - if constexpr(not mono::config::constant::debugMode) - { - if(m_showDebugInfo) - { - ImGui::Begin("Release Mode Statistics"); - { - ImGui::Text( - "Performance: [%.2fms] [%.0ffps]", - 1000.0f / ImGui::GetIO().Framerate, - ImGui::GetIO().Framerate); - ImGui::Text( - "Mouse Position: Screen[%.2fx, %.2fy]", - ImGui::GetMousePos().x, - ImGui::GetMousePos().y); - // TODO(vis4rd): expose pipeline-level stats in renderer - // ImGui::Text("Quad count: %d", m_renderer.getStats().quadCount); - // ImGui::Text("Line count: %d", m_renderer.getStats().lineCount); - // ImGui::Text("Draw calls: %d", m_renderer.getStats().drawCount); - // ImGui::Text("Indices: %d", m_renderer.getStats().indexCount); - } - ImGui::End(); - } - } } // returns world coordinates at 0 height diff --git a/src/game/src/section/MainMenuSection.cpp b/src/game/src/section/MainMenuSection.cpp index 8e8bd0a9..be62b1b1 100644 --- a/src/game/src/section/MainMenuSection.cpp +++ b/src/game/src/section/MainMenuSection.cpp @@ -56,7 +56,7 @@ void MainMenuSection::render() noexcept {m_layout.menuX + m_layout.buttonWS, m_layout.menuY + m_layout.buttonHS}); if(ImGui::Button("New game", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'New game'"); + mono::log::debug("Clicking 'New game'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -64,7 +64,7 @@ void MainMenuSection::render() noexcept {m_layout.menuX + m_layout.buttonWS, ImGui::GetCursorScreenPos().y}); if(ImGui::Button("Map creator", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Map creator'"); + mono::log::debug("Clicking 'Map creator'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -72,7 +72,7 @@ void MainMenuSection::render() noexcept {m_layout.menuX + m_layout.buttonWS, ImGui::GetCursorScreenPos().y}); if(ImGui::Button("Settings", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Settings'"); + mono::log::debug("Clicking 'Settings'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -80,7 +80,7 @@ void MainMenuSection::render() noexcept {m_layout.menuX + m_layout.buttonWS, ImGui::GetCursorScreenPos().y}); if(ImGui::Button("Exit", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Exit'"); + mono::log::debug("Clicking 'Exit'"); SectionManager::get().clear(); } } diff --git a/src/game/src/section/NewGameSection.cpp b/src/game/src/section/NewGameSection.cpp index 8021b4fb..33df052f 100644 --- a/src/game/src/section/NewGameSection.cpp +++ b/src/game/src/section/NewGameSection.cpp @@ -66,7 +66,7 @@ void NewGameSection::render() noexcept {m_layout.menuX + m_layout.buttonWS, m_layout.menuY + m_layout.buttonHS}); if(ImGui::Button("Tutorial", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Tutorial'"); + mono::log::debug("Clicking 'Tutorial'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -75,7 +75,7 @@ void NewGameSection::render() noexcept m_layout.menuY + m_layout.buttonHS * 2 + m_layout.buttonH}); if(ImGui::Button("Forest", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Forest'"); + mono::log::debug("Clicking 'Forest'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -84,7 +84,7 @@ void NewGameSection::render() noexcept m_layout.menuY + m_layout.buttonHS * 3 + m_layout.buttonH * 2}); if(ImGui::Button("Winter", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Winter'"); + mono::log::debug("Clicking 'Winter'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } @@ -95,7 +95,7 @@ void NewGameSection::render() noexcept m_layout.menuY + m_layout.buttonHS * 4 + m_layout.buttonH * 3}); if(ImGui::Button("Debug Level", {m_layout.buttonW, m_layout.buttonH})) { - spdlog::debug("Clicking 'Debug Level'"); + mono::log::debug("Clicking 'Debug Level'"); auto& sm = SectionManager::get(); sm.emplaceSection(); } diff --git a/src/playground/main.cpp b/src/playground/main.cpp index 54628273..6d30e120 100644 --- a/src/playground/main.cpp +++ b/src/playground/main.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include