commit 737ec46a063c68b189a32731410ced8b153b3e38 Author: Samsonium Date: Mon Sep 28 14:46:25 2026 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b582844 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/* + +# Include directories +!/docs +!/include +!/src + +# Include files +!/.gitignore +!/CMakeLists.txt + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9a530e6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.24) +project(magic_scrolls) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +file(GLOB_RECURSE sources + "${CMAKE_SOURCE_DIR}/src/*.cpp") + +add_executable(magic_scrolls ${sources}) + +target_include_directories(magic_scrolls PUBLIC "${CMAKE_SOURCE_DIR}/include") + diff --git a/docs/build.md b/docs/build.md new file mode 100644 index 0000000..2a06277 --- /dev/null +++ b/docs/build.md @@ -0,0 +1,12 @@ +# Сборка проекта + +Для сборки нужно иметь CMake минимум `3.24`, make и, соответственно, компилятор C (подойдёт как Clang, так и GCC). + +```bash +cmake -B build +cd build +make +``` + +В результате сборки получится исполняемый файл `magic_scrolls`. + diff --git a/docs/engine/README.md b/docs/engine/README.md new file mode 100644 index 0000000..6bcd107 --- /dev/null +++ b/docs/engine/README.md @@ -0,0 +1,5 @@ +# Класс `Engine` + +Класс `Engine` является основной сущностью, которая управляет всем процессом игры. +Для его инициализации нужен список подключаемых модулей, которые он провалидирует на совместимость, а затем запустит. + diff --git a/docs/engine/module.md b/docs/engine/module.md new file mode 100644 index 0000000..cbf5b6d --- /dev/null +++ b/docs/engine/module.md @@ -0,0 +1,16 @@ +# Модули движка + +Движок Magic Scrolls строится на основе модульной архитектуры. По-умолчанию, без модулей, движок предоставляет: + +- Событийную шину +- Реестр модулей +- Публичный API для модулей + +Вся остальная логика, включая REST API, realtime коммуникации и игровые фичи реализуются через модули, которые могут общаться между собой посредством событийной шины. + +Игровой модуль реализуется через свой основной класс, который наследуется от `MscEngineModule`. Сам модуль хранит о себе основную информацию в виде: + +- Имени модуля, для записи в реестр и обращении; +- Ревизии модуля, для проверки зависимостей; +- Список зависимостей, описываемых как карта "название требуемого модуля" <-> "диапазон допустимых ревизий" (`MscRevisionRange`) + diff --git a/include/engine/core/engine.hpp b/include/engine/core/engine.hpp new file mode 100644 index 0000000..5f7b102 --- /dev/null +++ b/include/engine/core/engine.hpp @@ -0,0 +1,26 @@ +#ifndef __MCS_ENGINE_HPP__ +#define __MCS_ENGINE_HPP__ + +#include "engine/core/engine_module.hpp" +#include "engine/core/engine_module_api.hpp" +#include +#include +#include + +/// Magic Srolls engine core +class MscEngine : public IMscEngineModuleAPI { +private: + /// Loaded modules list + std::vector> _modules; + +public: + /// Actual version + static uint16_t REV; + + MscEngine(std::vector> modules); + + UNIMPL getBus() const override; + UNIMPL getLoadedModules() const override; +}; + +#endif // __MCS_ENGINE_HPP__ diff --git a/include/engine/core/engine_module.hpp b/include/engine/core/engine_module.hpp new file mode 100644 index 0000000..e421a58 --- /dev/null +++ b/include/engine/core/engine_module.hpp @@ -0,0 +1,28 @@ +#ifndef __MCS_ENGINE_MODULE_HPP__ +#define __MCS_ENGINE_MODULE_HPP__ + +#include "engine/utils/rev_range.hpp" +#include +#include +#include + +/// Engine module base class +class MscEngineModule { +public: + virtual ~MscEngineModule() = default; + + /// Get module name + virtual std::string getName() const = 0; + + /// Get module revision + virtual uint16_t getRev() const = 0; + + /// Module dependency list + virtual const std::unordered_map & + getDependencies() const = 0; + + /// Module initialization phase + virtual void onInitialize(); +}; + +#endif // __MCS_ENGINE_MODULE_HPP__ diff --git a/include/engine/core/engine_module_api.hpp b/include/engine/core/engine_module_api.hpp new file mode 100644 index 0000000..c5e9b39 --- /dev/null +++ b/include/engine/core/engine_module_api.hpp @@ -0,0 +1,17 @@ +#ifndef __MSC_ENGINE_MODULE_API_HPP__ +#define __MSC_ENGINE_MODULE_API_HPP__ + +typedef void UNIMPL; + +class IMscEngineModuleAPI { +public: + virtual ~IMscEngineModuleAPI() = default; + + /// Get access to engine event bus + virtual UNIMPL getBus() const = 0; + + /// Get access to loaded module list + virtual UNIMPL getLoadedModules() const = 0; +}; + +#endif // __MSC_ENGINE_MODULE_API_HPP__ diff --git a/include/engine/modules/test1/test1.hpp b/include/engine/modules/test1/test1.hpp new file mode 100644 index 0000000..77411b8 --- /dev/null +++ b/include/engine/modules/test1/test1.hpp @@ -0,0 +1,19 @@ +#ifndef __MSC_TEST_1_MODULE_HPP__ +#define __MSC_TEST_1_MODULE_HPP__ + +#include "engine/core/engine_module.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() override; +}; + +#endif // __MSC_TEST_1_MODULE_HPP__ diff --git a/include/engine/utils/rev_range.hpp b/include/engine/utils/rev_range.hpp new file mode 100644 index 0000000..8167f56 --- /dev/null +++ b/include/engine/utils/rev_range.hpp @@ -0,0 +1,15 @@ +#ifndef __MSC_REV_RANGE_HPP__ +#define __MSC_REV_RANGE_HPP__ + +#include +struct MscRevisionRange { + uint16_t minRevision; + uint16_t maxRevision; + + /// Check provided revision to be in range + bool isSatisfied(uint16_t rev) const { + return rev >= this->minRevision && rev <= this->maxRevision; + } +}; + +#endif // __MSC_REV_RANGE_HPP__ diff --git a/src/engine/core/engine.cpp b/src/engine/core/engine.cpp new file mode 100644 index 0000000..53ddee4 --- /dev/null +++ b/src/engine/core/engine.cpp @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include +#include + +uint16_t MscEngine::REV = 0; + +MscEngine::MscEngine(std::vector> modules) { + std::unordered_map registry; + for (const auto &module : modules) { + if (!module) + continue; + + auto [it, inserted] = + registry.try_emplace(module->getName(), module->getRev()); + if (!inserted) { + throw std::runtime_error("Duplicate module detected: " + + module->getName()); + } + } + + for (const auto &module : modules) { + if (!module) + continue; + + for (const auto &[dep_name, range] : module->getDependencies()) { + auto it = registry.find(dep_name); + if (it == registry.end()) { + throw std::runtime_error("Module " + module->getName() + + " requires missing module: " + dep_name); + } + + int current_rev = it->second; + if (!range.isSatisfied(current_rev)) { + throw std::runtime_error( + "Module " + module->getName() + " requires " + dep_name + + " in range [" + std::to_string(range.minRevision) + " <-> " + + std::to_string(range.maxRevision) + "], but got rev" + + std::to_string(current_rev)); + } + } + } + + _modules = std::move(modules); +} + +UNIMPL MscEngine::getBus() const { throw std::runtime_error("Unimplemented"); } + +UNIMPL MscEngine::getLoadedModules() const { + throw std::runtime_error("Unimplemented"); +} diff --git a/src/engine/modules/test1/test1.hpp b/src/engine/modules/test1/test1.hpp new file mode 100644 index 0000000..e071cf3 --- /dev/null +++ b/src/engine/modules/test1/test1.hpp @@ -0,0 +1,17 @@ +#include "engine/modules/test1/test1.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() {} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3156943 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,15 @@ +#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); + return 0; +}