diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..209381a --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: LLVM +IndentWidth: 2 +BreakBeforeBraces: Allman diff --git a/.gitignore b/.gitignore index 0c6ec0e..8b0a5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ !/tests # Include files +!/.clang-format !/.gitignore !/CMakeLists.txt !/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 86dffa8..64689e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,16 +30,25 @@ add_executable(engine_test src/engine/core/engine.cpp src/engine/core/engine_db_provider.cpp ) +add_executable(lifecycle_test + tests/engine/lifecycle_test.cpp + src/engine/core/engine.cpp + src/engine/core/engine_db_provider.cpp +) target_include_directories(event_bus_test PUBLIC "${CMAKE_SOURCE_DIR}/include") target_link_libraries(event_bus_test PRIVATE SQLite::SQLite3) -target_include_directories(db_provider_test PUBLIC "${CMAKE_SOURCE_DIR}/include") +target_include_directories(db_provider_test PUBLIC + "${CMAKE_SOURCE_DIR}/include") target_link_libraries(db_provider_test PRIVATE SQLite::SQLite3) target_include_directories(engine_test PUBLIC "${CMAKE_SOURCE_DIR}/include") target_link_libraries(engine_test PRIVATE SQLite::SQLite3) +target_include_directories(lifecycle_test PUBLIC "${CMAKE_SOURCE_DIR}/include") +target_link_libraries(lifecycle_test PRIVATE SQLite::SQLite3) + add_test( NAME "Event Bus" COMMAND event_bus_test @@ -52,3 +61,7 @@ add_test( NAME "Engine" COMMAND engine_test ) +add_test( + NAME "Lifecycle" + COMMAND lifecycle_test +) diff --git a/include/engine/core/engine.hpp b/include/engine/core/engine.hpp index e6b0284..0e194da 100644 --- a/include/engine/core/engine.hpp +++ b/include/engine/core/engine.hpp @@ -1,32 +1,50 @@ #ifndef __MCS_ENGINE_HPP__ #define __MCS_ENGINE_HPP__ +#include +#include #include #include -#include -#include #include #include -/// Magic Srolls engine core +/// Ядро движка Magic Srolls class MscEngine : public IMscEngineModuleAPI { private: - /// Loaded modules list + /// @brief Загруженные модули std::vector> _modules; + + /// @brief Общая событийная шина движка MscEventBus _bus; + /// @brief Флаг работы движка + bool _isRunning = false; + + /// @brief Время последнего тика + clock::time_point _lastUpdate; + public: - /// Actual version + /// @brief Номер ревизии static uint16_t REV; MscEngine(std::vector> modules); + ~MscEngine() = default; + + /// @brief Запустить движок + void start(); + + /// @brief Остановить движок + void stop(); MscEventBus &getBus() override; - std::unordered_map getLoadedModules() const override; - MscEngineModule &getModule(const std::string &name) override; + std::unordered_map + getLoadedModules() const override; + std::shared_ptr &getModule(const std::string &name) override; std::unique_ptr createDBProvider() override; - std::unique_ptr createDBProvider(const std::string &filepath) override; + std::unique_ptr + createDBProvider(const std::string &filepath) override; + void requestStop() override; }; #endif // __MCS_ENGINE_HPP__ diff --git a/include/engine/core/engine_module.hpp b/include/engine/core/engine_module.hpp index 19978a6..a198f27 100644 --- a/include/engine/core/engine_module.hpp +++ b/include/engine/core/engine_module.hpp @@ -1,8 +1,8 @@ #ifndef __MCS_ENGINE_MODULE_HPP__ #define __MCS_ENGINE_MODULE_HPP__ -#include "engine/utils/rev_range.hpp" #include "engine/core/engine_module_api.hpp" +#include "engine/utils/rev_range.hpp" #include #include #include @@ -23,8 +23,27 @@ public: virtual const std::unordered_map & getDependencies() const = 0; - /// Module initialization phase + /// @brief Module initialization + /// @param engine engine public API for modules virtual void onInitialize(IMscEngineModuleAPI &engine) = 0; + + /// @brief Engine start event handler + /// @param engine engine public API for modules + virtual void onStart(IMscEngineModuleAPI &engine) = 0; + + /// @brief Engine update cycle step + /// @param engine engine public API for modules + /// @param dt loop delta + virtual void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) = 0; + + /// @brief Engine stop event handler + /// @param engine engine public API for modules + virtual void onStop(IMscEngineModuleAPI &engine) = 0; + + /// @brief Module destroying + /// @param engine engine public API for modules + virtual void onDestory(IMscEngineModuleAPI &engine) = 0; }; #endif // __MCS_ENGINE_MODULE_HPP__ diff --git a/include/engine/core/engine_module_api.hpp b/include/engine/core/engine_module_api.hpp index 616bd9b..b7a2cc0 100644 --- a/include/engine/core/engine_module_api.hpp +++ b/include/engine/core/engine_module_api.hpp @@ -1,34 +1,43 @@ #ifndef __MSC_ENGINE_MODULE_API_HPP__ #define __MSC_ENGINE_MODULE_API_HPP__ +#include #include #include -#include +#include #include #include -#include class MscEngineModule; +/// @brief Публичный API движка для модулей class IMscEngineModuleAPI { public: + using clock = std::chrono::steady_clock; + virtual ~IMscEngineModuleAPI() = default; - /// @brief Get access to engine event bus + /// @brief Получить доступ к событийной шине virtual MscEventBus &getBus() = 0; - /// @brief Get access to loaded module list - virtual std::unordered_map getLoadedModules() const = 0; + /// @brief Получить список загруженных модулей + virtual std::unordered_map + getLoadedModules() const = 0; - /// @brief Access module by its name - virtual MscEngineModule &getModule(const std::string &name) = 0; + /// @brief Получить прямой доступ к модулю + virtual std::shared_ptr & + getModule(const std::string &name) = 0; - /// @brief Create database provider with in-memory database + /// @brief Создать провайдер БД с хранением в памяти virtual std::unique_ptr createDBProvider() = 0; - /// @brief Create database provider with file database - virtual std::unique_ptr createDBProvider(const std::string &filepath) = 0; + /// @brief Создать провайдер БД с указанным файлом + virtual std::unique_ptr + createDBProvider(const std::string &filepath) = 0; + + /// @brief Запросить остановку движка + virtual void requestStop() = 0; }; #endif // __MSC_ENGINE_MODULE_API_HPP__ diff --git a/include/engine/modules/test1/test1.hpp b/include/engine/modules/test1/test1.hpp deleted file mode 100644 index 444540f..0000000 --- a/include/engine/modules/test1/test1.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __MSC_TEST_1_MODULE_HPP__ -#define __MSC_TEST_1_MODULE_HPP__ - -#include "engine/core/engine_module.hpp" -#include "engine/core/engine_module_api.hpp" -#include - -class Test1Module : public MscEngineModule -{ -public: - ~Test1Module() = default; - - std::string getName() const override; - uint16_t getRev() const override; - const std::unordered_map & - getDependencies() const override; - - void onInitialize(IMscEngineModuleAPI &engine) override; -}; - -#endif // __MSC_TEST_1_MODULE_HPP__ diff --git a/src/engine/core/engine.cpp b/src/engine/core/engine.cpp index 95f8ff6..927ae43 100644 --- a/src/engine/core/engine.cpp +++ b/src/engine/core/engine.cpp @@ -1,12 +1,16 @@ +#include "engine/core/engine_module_api.hpp" +#include #include #include +#include #include #include #include +#include #include #include -uint16_t MscEngine::REV = 0; +std::uint16_t MscEngine::REV = 1; MscEngine::MscEngine(std::vector> modules) { @@ -49,17 +53,71 @@ MscEngine::MscEngine(std::vector> modules) std::to_string(current_rev)); } } + + module.get()->onInitialize(*this); } _modules = std::move(modules); } -MscEventBus &MscEngine::getBus() +void MscEngine::start() { - return _bus; + if (_isRunning) + return; + + _isRunning = true; + _lastUpdate = clock::now(); + + for (auto &m : _modules) + m->onStart(*this); + + constexpr auto tickDuration = std::chrono::milliseconds(50); + auto nextTick = clock::now(); + + using UpdateFn = std::function; + std::vector updateFns; + updateFns.reserve(_modules.size()); + + for (auto &m : _modules) + updateFns.emplace_back([m](IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) + { m->onUpdate(engine, dt); }); + + while (_isRunning) + { + nextTick += tickDuration; + const auto currentTick = clock::now(); + const auto dt = currentTick - _lastUpdate; + + // TODO: process + + for (auto upd : updateFns) + upd(*this, dt); + + _lastUpdate = currentTick; + + std::this_thread::sleep_until(nextTick); + if (clock::now() > nextTick + std::chrono::seconds(1)) + nextTick = clock::now(); + } + + for (auto &m : _modules) + m->onStop(*this); } -std::unordered_map MscEngine::getLoadedModules() const +void MscEngine::stop() +{ + if (!_isRunning) + return; + + _isRunning = false; +} + +MscEventBus &MscEngine::getBus() { return _bus; } + +std::unordered_map +MscEngine::getLoadedModules() const { std::unordered_map result; for (const auto &module : _modules) @@ -74,12 +132,12 @@ std::unordered_map MscEngine::getLoadedModules() con return result; } -MscEngineModule &MscEngine::getModule(const std::string &name) +std::shared_ptr &MscEngine::getModule(const std::string &name) { for (auto &module : _modules) { if (module.get()->getName() == name) - return *module.get(); + return module; } throw std::runtime_error("Module not found: " + name); @@ -90,7 +148,13 @@ std::unique_ptr MscEngine::createDBProvider() return std::make_unique(); } -std::unique_ptr MscEngine::createDBProvider(const std::string &filepath) +std::unique_ptr +MscEngine::createDBProvider(const std::string &filepath) { return std::make_unique(filepath); } + +void MscEngine::requestStop() +{ + this->stop(); // TODO: заменить на флаг +} diff --git a/src/engine/modules/test1/test1.hpp b/src/engine/modules/test1/test1.hpp deleted file mode 100644 index cac5322..0000000 --- a/src/engine/modules/test1/test1.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "engine/modules/test1/test1.hpp" -#include "engine/core/engine_module_api.hpp" -#include "engine/utils/rev_range.hpp" -#include -#include -#include - -std::string Test1Module::getName() const { return "test1"; } - -uint16_t Test1Module::getRev() const { return 1; } - -const std::unordered_map & -Test1Module::getDependencies() const -{ - static const std::unordered_map deps{}; - return deps; -} - -void Test1Module::onInitialize(IMscEngineModuleAPI &engine) -{ - (void)(engine); -} diff --git a/src/main.cpp b/src/main.cpp index d4a84cc..faabc99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,9 @@ -#include "engine/modules/test1/test1.hpp" #include #include -#include #include -#include int main(int argc, char **argv) { - auto engine = - std::make_unique(std::vector>{ - std::make_shared()}); - - printf("Hello, CMake, %p!\n", &engine); + printf("Hello, engine!\n"); return 0; } diff --git a/tests/engine/engine_test.cpp b/tests/engine/engine_test.cpp index 8aadfc0..4661d9b 100644 --- a/tests/engine/engine_test.cpp +++ b/tests/engine/engine_test.cpp @@ -1,149 +1,157 @@ #include -#include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include class FirstTestModuleR1 : public MscEngineModule { public: - std::string getName() const override - { - return "first_tmod"; - }; + std::string getName() const override { return "first_tmod"; }; - std::uint16_t getRev() const override - { - return 1; - } + std::uint16_t getRev() const override { return 1; } - const std::unordered_map &getDependencies() const override - { - static const std::unordered_map deps{}; - return deps; - } + const std::unordered_map & + getDependencies() const override + { + static const std::unordered_map deps{}; + return deps; + } - void onInitialize(IMscEngineModuleAPI &engine) override {} + void onInitialize(IMscEngineModuleAPI &engine) override {} + void onStart(IMscEngineModuleAPI &engine) override {} + void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) override + { + } + void onStop(IMscEngineModuleAPI &engine) override {} + void onDestory(IMscEngineModuleAPI &engine) override {} }; class FirstTestModuleR2 : public MscEngineModule { public: - std::string getName() const override - { - return "first_tmod"; - }; + std::string getName() const override { return "first_tmod"; }; - std::uint16_t getRev() const override - { - return 2; - } + std::uint16_t getRev() const override { return 2; } - const std::unordered_map &getDependencies() const override - { - static const std::unordered_map deps{}; - return deps; - } + const std::unordered_map & + getDependencies() const override + { + static const std::unordered_map deps{}; + return deps; + } - void onInitialize(IMscEngineModuleAPI &engine) override {} + void onInitialize(IMscEngineModuleAPI &engine) override {} + void onStart(IMscEngineModuleAPI &engine) override {} + void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) override + { + } + void onStop(IMscEngineModuleAPI &engine) override {} + void onDestory(IMscEngineModuleAPI &engine) override {} }; class SecondTestModuleE1 : public MscEngineModule { public: - std::string getName() const override - { - return "second_tmod"; - } + std::string getName() const override { return "second_tmod"; } - std::uint16_t getRev() const override - { - return 1; - } + std::uint16_t getRev() const override { return 1; } - const std::unordered_map &getDependencies() const override - { - static const std::unordered_map deps{ - {"first_tmod", MscRevisionRange{1, 1}}}; - return deps; - } + const std::unordered_map & + getDependencies() const override + { + static const std::unordered_map deps{ + {"first_tmod", MscRevisionRange{1, 1}}}; + return deps; + } - void onInitialize(IMscEngineModuleAPI &engine) override {} + void onInitialize(IMscEngineModuleAPI &engine) override {} + void onStart(IMscEngineModuleAPI &engine) override {} + void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) override + { + } + void onStop(IMscEngineModuleAPI &engine) override {} + void onDestory(IMscEngineModuleAPI &engine) override {} }; class SecondTestModuleE2 : public MscEngineModule { public: - std::string getName() const override - { - return "second_tmod"; - } + std::string getName() const override { return "second_tmod"; } - std::uint16_t getRev() const override - { - return 1; - } + std::uint16_t getRev() const override { return 1; } - const std::unordered_map &getDependencies() const override - { - static const std::unordered_map deps{ - {"first_tmod", MscRevisionRange{1, 2}}}; - return deps; - } + const std::unordered_map & + getDependencies() const override + { + static const std::unordered_map deps{ + {"first_tmod", MscRevisionRange{1, 2}}}; + return deps; + } - void onInitialize(IMscEngineModuleAPI &engine) override {} + void onInitialize(IMscEngineModuleAPI &engine) override {} + void onStart(IMscEngineModuleAPI &engine) override {} + void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) override + { + } + void onStop(IMscEngineModuleAPI &engine) override {} + void onDestory(IMscEngineModuleAPI &engine) override {} }; void testSatisfiedModules() { - MscEngine engine({std::make_shared(), - std::make_shared()}); + MscEngine engine({std::make_shared(), + std::make_shared()}); } void testDuplicatedModules() { - try - { - MscEngine engine({ - std::make_shared(), - std::make_shared(), - }); - assert(false); - } - catch (const std::runtime_error &error) - { - } + try + { + MscEngine engine({ + std::make_shared(), + std::make_shared(), + }); + assert(false); + } + catch (const std::runtime_error &error) + { + } } void testUnsatisfiedModules() { - try - { - MscEngine engine({std::make_shared(), - std::make_shared()}); - assert(false); - } - catch (const std::runtime_error &error) - { - } + try + { + MscEngine engine({std::make_shared(), + std::make_shared()}); + assert(false); + } + catch (const std::runtime_error &error) + { + } } void testSatisfiedMultiversionModule() { - MscEngine engine({std::make_shared(), - std::make_shared()}); + MscEngine engine({std::make_shared(), + std::make_shared()}); } int main() { - testSatisfiedModules(); - testDuplicatedModules(); - testUnsatisfiedModules(); - testSatisfiedMultiversionModule(); + testSatisfiedModules(); + testDuplicatedModules(); + testUnsatisfiedModules(); + testSatisfiedMultiversionModule(); - return 0; + return 0; } diff --git a/tests/engine/lifecycle_test.cpp b/tests/engine/lifecycle_test.cpp new file mode 100644 index 0000000..d12916c --- /dev/null +++ b/tests/engine/lifecycle_test.cpp @@ -0,0 +1,64 @@ +#include "engine/core/engine.hpp" +#include "engine/core/engine_module.hpp" +#include "engine/utils/rev_range.hpp" +#include +#include +#include +#include +#include + +bool onInitWasCalled = false; +bool onStartWasCalled = false; +bool onUpdateWasCalled = false; +bool onStopWasCalled = false; + +class TestModule : public MscEngineModule +{ +public: + std::string getName() const override { return "test_mod"; } + std::uint16_t getRev() const override { return 1; } + const std::unordered_map & + getDependencies() const override + { + static const std::unordered_map deps{}; + return deps; + } + + void onInitialize(IMscEngineModuleAPI &engine) override + { + onInitWasCalled = true; + } + + void onStart(IMscEngineModuleAPI &engine) override + { + onStartWasCalled = true; + } + + void onUpdate(IMscEngineModuleAPI &engine, + IMscEngineModuleAPI::clock::duration dt) override + { + onUpdateWasCalled = true; + engine.requestStop(); + } + + void onStop(IMscEngineModuleAPI &engine) override { onStopWasCalled = true; } + + void onDestory(IMscEngineModuleAPI &engine) override {} +}; + +void testStates() +{ + MscEngine engine({std::make_shared()}); + engine.start(); + + assert(onInitWasCalled); + assert(onStartWasCalled); + assert(onUpdateWasCalled); + assert(onStopWasCalled); +} + +int main() +{ + testStates(); + return 0; +}