diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cff9ca10..72b6ac4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,9 @@ jobs: if: matrix.name == 'Linux' run: | sudo apt install -y libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev + - name: Update G++ + if: matrix.name == 'Linux' + run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 - name: Build run: | mkdir build && cd build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cd14b3f..b7bc5e5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,6 +13,8 @@ jobs: - name: Install Xorg lib run: | sudo apt install -y libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev + - name: Update G++ + run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 - name: Build run: | mkdir build && cd build diff --git a/lib/wal/CMakeLists.txt b/lib/wal/CMakeLists.txt index 335f26ce..fcebfa75 100644 --- a/lib/wal/CMakeLists.txt +++ b/lib/wal/CMakeLists.txt @@ -9,12 +9,8 @@ add_library(wal sources/System/System.hpp sources/Wal.cpp sources/Wal.hpp - sources/Scene/SceneManager.cpp - sources/Scene/SceneManager.hpp sources/Scene/Scene.cpp sources/Scene/Scene.hpp - sources/Events/EventManager.cpp - sources/Events/EventManager.hpp sources/Exception/WalError.cpp sources/Exception/WalError.hpp sources/Entity/Entity.cpp @@ -27,6 +23,7 @@ add_library(wal sources/System/Movable/MovableSystem.cpp sources/System/Movable/MovableSystem.hpp sources/System/System.cpp + sources/Models/Callback.hpp ) target_include_directories(wal PUBLIC sources) @@ -35,6 +32,7 @@ add_executable(wal_tests EXCLUDE_FROM_ALL tests/EntityTests.cpp tests/MainTest.cpp tests/EngineTests.cpp + tests/CallbackTest.cpp ) target_link_libraries(wal_tests PRIVATE wal) diff --git a/lib/wal/sources/Component/Component.cpp b/lib/wal/sources/Component/Component.cpp index f891352b..fb4e1279 100644 --- a/lib/wal/sources/Component/Component.cpp +++ b/lib/wal/sources/Component/Component.cpp @@ -20,11 +20,6 @@ namespace WAL this->_disabled = disabled; } - const std::vector &Component::getDependencies() const - { - return this->_dependencies; - } - void Component::onStart() { //TODO handle events here diff --git a/lib/wal/sources/Component/Component.hpp b/lib/wal/sources/Component/Component.hpp index 410a026c..b2441383 100644 --- a/lib/wal/sources/Component/Component.hpp +++ b/lib/wal/sources/Component/Component.hpp @@ -22,8 +22,6 @@ namespace WAL protected: //! @brief The entity that own this component Entity &_entity; - //! @brief The list of dependencies of this component. - std::vector _dependencies; //! @brief A component can't be instantiated, it should be derived. explicit Component(Entity &entity); @@ -44,9 +42,6 @@ namespace WAL //! @brief Disable this component. void setDisable(bool disabled); - //! @brief Get the dependencies of this component. - const std::vector &getDependencies() const; - //! @brief The entity or this component has just been enabled. virtual void onStart(); diff --git a/lib/wal/sources/Entity/Entity.cpp b/lib/wal/sources/Entity/Entity.cpp index e5ca8b29..9a59447b 100644 --- a/lib/wal/sources/Entity/Entity.cpp +++ b/lib/wal/sources/Entity/Entity.cpp @@ -59,4 +59,12 @@ namespace WAL }); return existing != this->_components.end(); } + + bool Entity::hasComponent(const std::type_index &type) const + { + auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) { + return std::type_index(typeid(*cmp)) == type; + }); + return existing != this->_components.end(); + } } \ No newline at end of file diff --git a/lib/wal/sources/Entity/Entity.hpp b/lib/wal/sources/Entity/Entity.hpp index 7a902ecd..5f4b21ca 100644 --- a/lib/wal/sources/Entity/Entity.hpp +++ b/lib/wal/sources/Entity/Entity.hpp @@ -67,6 +67,10 @@ namespace WAL //! @param type The type of the component bool hasComponent(const std::type_info &type) const; + //! @brief Check if this entity has a component. + //! @param type The type of the component + bool hasComponent(const std::type_index &type) const; + //! @brief Add a component to this entity. The component is constructed in place. //! @throw DuplicateError is thrown if a component with the same type already exist. //! @return This entity is returned diff --git a/lib/wal/sources/Events/EventManager.cpp b/lib/wal/sources/Events/EventManager.cpp deleted file mode 100644 index 69b939b7..00000000 --- a/lib/wal/sources/Events/EventManager.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by Zoe Roux on 2021-05-14. -// - -#include "EventManager.hpp" diff --git a/lib/wal/sources/Events/EventManager.hpp b/lib/wal/sources/Events/EventManager.hpp deleted file mode 100644 index a1f608e1..00000000 --- a/lib/wal/sources/Events/EventManager.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Zoe Roux on 2021-05-14. -// - - -#pragma once - -namespace WAL -{ - //! @brief A class to handle events. - class EventManager - { - - }; -} diff --git a/lib/wal/sources/Models/Callback.hpp b/lib/wal/sources/Models/Callback.hpp new file mode 100644 index 00000000..9cce38cd --- /dev/null +++ b/lib/wal/sources/Models/Callback.hpp @@ -0,0 +1,54 @@ +// +// Created by Zoe Roux on 5/21/21. +// + + +#pragma once + +#include + +namespace WAL +{ + //! @brief A callback where you can subscribe to and emit it. + template + class Callback + { + private: + int _nextID = 0; + //! @brief The list of functions to call. + std::unordered_map> _functions = {}; + + public: + //! @brief Add a method to be called when this callback is invoked. + //! @param callback The list of arguments of the callback method + //! @return A unique ID for this callback. That can be used to remove the callback later. + int addCallback(std::function callback) + { + int id = this->_nextID++; + this->_functions[id] = std::move(callback); + return id; + } + + //! @brief Remove a function from this callback. + //! @param id The ID of the function. + void removeCallback(int id) + { + this->_functions.erase(id); + } + + void operator()(Types ...args) const + { + for (const auto &[_, callback] : this->_functions) + callback(args...); + } + + //! @brief A default constructor + Callback() = default; + //! @brief A default copy constructor + Callback(const Callback &) = default; + //! @brief A default destructor + ~Callback() = default; + //! @brief A default assignment operator + Callback &operator=(const Callback &) = default; + }; +} \ No newline at end of file diff --git a/lib/wal/sources/Models/Vector3.hpp b/lib/wal/sources/Models/Vector3.hpp index 05391c25..9aa58d10 100644 --- a/lib/wal/sources/Models/Vector3.hpp +++ b/lib/wal/sources/Models/Vector3.hpp @@ -23,12 +23,12 @@ namespace WAL T z; //! @brief Create a new nil vector3. - Vector3() + Vector3() : x(0), y(0), z(0) {} //! @brief Create a new vector3 representing a specific coordinate. - Vector3(T x, T y, T z) + Vector3(T x, T y, T z) : x(x), y(y), z(z) {} diff --git a/lib/wal/sources/Scene/Scene.hpp b/lib/wal/sources/Scene/Scene.hpp index bf86de44..00329172 100644 --- a/lib/wal/sources/Scene/Scene.hpp +++ b/lib/wal/sources/Scene/Scene.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include #include "Entity/Entity.hpp" namespace WAL @@ -15,9 +16,27 @@ namespace WAL { private: //! @brief The list of registered entities - std::vector _entities; + std::vector _entities = {}; public: //! @brief Get the list of entities. std::vector &getEntities(); + + //! @brief Add a new entity to the scene, you can use this method with the same arguments as the entity's constructor. + //! @return The current scene is returned to allow you to chain call. + template + Scene &addEntity(Params ...params) + { + this->_entities.emplace_back(params...); + return *this; + } + + //! @brief A default constructor + Scene() = default; + //! @brief A scene is copy constructable + Scene(const Scene &) = default; + //! @brief A default destructor + ~Scene() = default; + //! @brief A scene is assignable + Scene &operator=(const Scene &) = default; }; } diff --git a/lib/wal/sources/Scene/SceneManager.cpp b/lib/wal/sources/Scene/SceneManager.cpp deleted file mode 100644 index 21c38221..00000000 --- a/lib/wal/sources/Scene/SceneManager.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by Zoe Roux on 2021-05-14. -// - -#include "SceneManager.hpp" - -namespace WAL -{ - SceneManager &WAL::SceneManager::addScene(WAL::Scene &&scene) - { - this->_scenes.push_front(scene); - return *this; - } - - SceneManager &SceneManager::addBackScene(Scene &&scene) - { - this->_scenes.insert(++this->_scenes.begin(), scene); - return *this; - } - - Scene &SceneManager::getCurrent() - { - if (this->_scenes.empty()) - throw NotFoundError("No scene exists."); - return this->_scenes.front(); - } - - SceneManager &SceneManager::closeCurrent() - { - this->_scenes.pop_front(); - return *this; - } -} \ No newline at end of file diff --git a/lib/wal/sources/System/Movable/MovableSystem.cpp b/lib/wal/sources/System/Movable/MovableSystem.cpp index 600f7c8e..7bd56fb0 100644 --- a/lib/wal/sources/System/Movable/MovableSystem.cpp +++ b/lib/wal/sources/System/Movable/MovableSystem.cpp @@ -9,10 +9,12 @@ namespace WAL { - const std::type_info &MovableSystem::getComponent() const - { - return typeid(MovableComponent); - } + MovableSystem::MovableSystem() + : System({ + typeid(MovableComponent), + typeid(PositionComponent) + }) + {} void MovableSystem::onFixedUpdate(Entity &entity) { diff --git a/lib/wal/sources/System/Movable/MovableSystem.hpp b/lib/wal/sources/System/Movable/MovableSystem.hpp index 400c7830..f7ec3c3c 100644 --- a/lib/wal/sources/System/Movable/MovableSystem.hpp +++ b/lib/wal/sources/System/Movable/MovableSystem.hpp @@ -13,13 +13,11 @@ namespace WAL class MovableSystem : public System { public: - //! @inherit - const std::type_info &getComponent() const override; //! @inherit void onFixedUpdate(Entity &entity) override; //! @brief A default constructor - MovableSystem() = default; + MovableSystem(); //! @brief A movable system is copy constructable MovableSystem(const MovableSystem &) = default; //! @brief A default destructor diff --git a/lib/wal/sources/System/System.cpp b/lib/wal/sources/System/System.cpp index 82f7fe11..56814735 100644 --- a/lib/wal/sources/System/System.cpp +++ b/lib/wal/sources/System/System.cpp @@ -4,8 +4,13 @@ #include "System.hpp" +#include + namespace WAL { + System::System(std::vector dependencies) + : _dependencies(std::move(dependencies)) + {} void System::onUpdate(Entity &entity, std::chrono::nanoseconds dtime) {} @@ -15,4 +20,9 @@ namespace WAL void System::onSelfUpdate() {} + + const std::vector &System::getDependencies() const + { + return this->_dependencies; + } } \ No newline at end of file diff --git a/lib/wal/sources/System/System.hpp b/lib/wal/sources/System/System.hpp index 3813e216..5ab9b8a5 100644 --- a/lib/wal/sources/System/System.hpp +++ b/lib/wal/sources/System/System.hpp @@ -12,6 +12,9 @@ namespace WAL //! @brief A base system of WAL class System { + private: + //! @brief The list of dependencies of this system + std::vector _dependencies = {}; public: //! @brief A virtual, default, destructor virtual ~System() = default; @@ -19,8 +22,8 @@ namespace WAL System(System &&) = default; //! @brief Get the name of the component corresponding to this system. - virtual const std::type_info &getComponent() const = 0; - + const std::vector &getDependencies() const; + //! @brief Update the corresponding component of the given entity //! @param entity The entity to update. //! @param dtime The delta time. @@ -35,7 +38,7 @@ namespace WAL virtual void onSelfUpdate(); protected: //! @brief A system can't be instantiated, it should be derived. - System() = default; + explicit System(std::vector dependencies); //! @brief A system can't be instantiated, it should be derived. System(const System &) = default; //! @brief A system can't be instantiated, it should be derived. diff --git a/lib/wal/sources/Wal.cpp b/lib/wal/sources/Wal.cpp index 569743aa..45d2caff 100644 --- a/lib/wal/sources/Wal.cpp +++ b/lib/wal/sources/Wal.cpp @@ -3,6 +3,7 @@ // #include +#include #include "Wal.hpp" using namespace std::chrono_literals; @@ -11,11 +12,6 @@ namespace WAL { std::chrono::nanoseconds Wal::timestep = 8ms; - SceneManager &Wal::getSceneManager() - { - return this->_sceneManager; - } - void Wal::run() { auto lastTick = std::chrono::steady_clock::now(); @@ -37,14 +33,12 @@ namespace WAL void Wal::_update(std::chrono::nanoseconds dtime) { - auto &entities = this->_sceneManager.getCurrent().getEntities(); + auto &entities = this->_scene.getEntities(); for (auto &system : this->_systems) { for (auto &entity : entities) { - const auto &cmp = system->getComponent(); - if (!entity.hasComponent(cmp)) + if (!Wal::_hasDependencies(entity, *system)) continue; - // TODO handle dependencies. system->onUpdate(entity, dtime); } system->onSelfUpdate(); @@ -53,16 +47,23 @@ namespace WAL void Wal::_fixedUpdate() { - auto &entities = this->_sceneManager.getCurrent().getEntities(); + auto &entities = this->_scene.getEntities(); for (auto &system : this->_systems) { for (auto &entity : entities) { - auto &cmp = system->getComponent(); - if (!entity.hasComponent(cmp)) + if (!Wal::_hasDependencies(entity, *system)) continue; - // TODO handle dependencies. system->onFixedUpdate(entity); } } } + + bool Wal::_hasDependencies(const Entity &entity, const System &system) + { + // TODO use an hashmap to cache results. + const auto &dependency = system.getDependencies(); + return std::ranges::all_of(dependency.begin(), dependency.end(), [&entity](const auto &dependency) { + return entity.hasComponent(dependency); + }); + } } \ No newline at end of file diff --git a/lib/wal/sources/Wal.hpp b/lib/wal/sources/Wal.hpp index ea72eb05..f1b61f6f 100644 --- a/lib/wal/sources/Wal.hpp +++ b/lib/wal/sources/Wal.hpp @@ -9,8 +9,7 @@ #include #include #include -#include "Events/EventManager.hpp" -#include "Scene/SceneManager.hpp" +#include "Scene/Scene.hpp" #include "Entity/Entity.hpp" #include "System/System.hpp" @@ -21,9 +20,7 @@ namespace WAL { private: //! @brief The scene manager that allow multiple scene to work together. - SceneManager _sceneManager; - //! @brief The event manager - EventManager _eventManager; + Scene _scene; //! @brief The list of registered systems std::vector> _systems = {}; //! @brief True if the engine should close after the end of the current tick. @@ -34,6 +31,12 @@ namespace WAL //! @brief Call the onFixedUpdate of every system with every component void _fixedUpdate(); + + //! @brief Check if an entity met a system's dependencies. + //! @param entity The entity to check + //! @param system The system that will list dependencies + //! @return True if all dependencies are met, false otherwise. + static bool _hasDependencies(const Entity &entity, const System &system); public: //! @brief The time between each fixed update. static std::chrono::nanoseconds timestep; @@ -97,9 +100,6 @@ namespace WAL return *this; } - //! @brief Get the scene manager. - SceneManager &getSceneManager(); - //! @brief Start the game loop void run(); diff --git a/lib/wal/tests/CallbackTest.cpp b/lib/wal/tests/CallbackTest.cpp new file mode 100644 index 00000000..c0d43f15 --- /dev/null +++ b/lib/wal/tests/CallbackTest.cpp @@ -0,0 +1,41 @@ +// +// Created by Zoe Roux on 5/21/21. +// + +#include +#include +#include +#include "Models/Callback.hpp" + +using namespace WAL; + +TEST_CASE("Callback basic test", "[Callback]") +{ + Callback callback; + + REQUIRE_NOTHROW(callback("1")); + SECTION("Check add") { + int id = callback.addCallback([](const std::string& i) { + if (i == "Super") + throw std::runtime_error(i); + }); + REQUIRE_THROWS_AS(callback("Super"), std::runtime_error); + + SECTION("Check remove") { + REQUIRE(id == 0); + callback.removeCallback(id); + REQUIRE_NOTHROW(callback("Super")); + } + } +} + +TEST_CASE("Callback multiple arguments", "[Callback]") +{ + Callback callback; + + callback.addCallback([](const std::string& str, int a, unsigned *value, Entity &entity) { + throw std::runtime_error(""); + }); + Entity entity("name"); + REQUIRE_THROWS_AS(callback("1", 0, nullptr, entity), std::runtime_error); +}