mirror of
https://github.com/MadeOfJelly/MushMachine.git
synced 2025-04-20 01:52:59 +02:00
UpdateStrategy Refactor: 6. draft, now looks good, not fully tested
This commit is contained in:
parent
bab5552e6f
commit
c1ae30c89c
@ -6,10 +6,14 @@ add_library(engine
|
|||||||
src/mm/engine.hpp
|
src/mm/engine.hpp
|
||||||
src/mm/engine.cpp
|
src/mm/engine.cpp
|
||||||
|
|
||||||
|
src/mm/update_strategies/update_strategy.hpp
|
||||||
|
|
||||||
src/mm/services/service.hpp
|
src/mm/services/service.hpp
|
||||||
|
|
||||||
src/mm/services/default_service.cpp
|
src/mm/update_strategies/dependency_check_decorater.hpp
|
||||||
src/mm/services/default_service.hpp
|
|
||||||
|
src/mm/update_strategies/default_strategy.hpp
|
||||||
|
src/mm/update_strategies/default_strategy.cpp
|
||||||
|
|
||||||
src/mm/services/scene_service_interface.hpp
|
src/mm/services/scene_service_interface.hpp
|
||||||
src/mm/services/scene_service_interface.cpp
|
src/mm/services/scene_service_interface.cpp
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "./engine.hpp"
|
#include "./engine.hpp"
|
||||||
|
|
||||||
|
#include <mm/update_strategies/default_strategy.hpp>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
@ -16,71 +18,7 @@
|
|||||||
|
|
||||||
namespace MM {
|
namespace MM {
|
||||||
|
|
||||||
Engine::FunctionDataHandle Engine::addUpdate(std::function<void(Engine&)> function) {
|
void Engine::setup(void) {
|
||||||
if (!function) {
|
|
||||||
LOG_ERROR("could not add Update, empty function!");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDataHandle r = _update_functions.emplace_back(std::make_shared<FunctionDataType>(function));
|
|
||||||
_update_functions_modified = true;
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine::FunctionDataHandle Engine::addFixedUpdate(std::function<void(Engine&)> function) {
|
|
||||||
if (!function) {
|
|
||||||
LOG_ERROR("could not add fixedUpdate, empty function!");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionDataHandle r = _fixed_update_functions.emplace_back(std::make_shared<FunctionDataType>(function));
|
|
||||||
_fixed_update_functions_modified = true;
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::removeUpdate(FunctionDataHandle handle) {
|
|
||||||
if (handle.expired()) {
|
|
||||||
LOG_ERROR("could not remove Update, invalid handle!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = handle.lock();
|
|
||||||
auto it = std::find(_update_functions.begin(), _update_functions.end(), lock);
|
|
||||||
if (it != _update_functions.end()) {
|
|
||||||
_update_functions.erase(it);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("could not remove Update, unknown handle!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::removeFixedUpdate(FunctionDataHandle handle) {
|
|
||||||
if (handle.expired()) {
|
|
||||||
LOG_ERROR("could not remove fixedUpdate, invalid handle!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lock = handle.lock();
|
|
||||||
auto it = std::find(_fixed_update_functions.begin(), _fixed_update_functions.end(), lock);
|
|
||||||
if (it != _fixed_update_functions.end()) {
|
|
||||||
_fixed_update_functions.erase(it);
|
|
||||||
} else {
|
|
||||||
LOG_ERROR("could not remove fixedUpdate, unknown handle!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::addFixedDefer(std::function<void(Engine&)> function) {
|
|
||||||
_fixed_defered.emplace_back(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::traverseUpdateFunctions(std::vector<std::shared_ptr<FunctionDataType>>& list) {
|
|
||||||
for (auto& entry : list) {
|
|
||||||
entry->f(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine::Engine(float f_delta_time) : _fixed_delta_time(f_delta_time) {
|
|
||||||
if (!MM::Logger::initialized) {
|
if (!MM::Logger::initialized) {
|
||||||
MM::Logger::init();
|
MM::Logger::init();
|
||||||
}
|
}
|
||||||
@ -88,6 +26,13 @@ Engine::Engine(float f_delta_time) : _fixed_delta_time(f_delta_time) {
|
|||||||
MM::Logger::initSectionLogger("Engine");
|
MM::Logger::initSectionLogger("Engine");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Engine::Engine(void) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
_update_strategy = std::make_unique<MM::UpdateStrategies::SingleThreadedDefault>();
|
||||||
|
LOG_INFO("defaulting to SingleThreadedDefault UpdateStrategy");
|
||||||
|
}
|
||||||
|
|
||||||
Engine::~Engine(void) {
|
Engine::~Engine(void) {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
@ -107,74 +52,61 @@ void Engine::cleanup(void) {
|
|||||||
|
|
||||||
_service_enable_order.clear();
|
_service_enable_order.clear();
|
||||||
|
|
||||||
_update_functions.clear();
|
_update_strategy.reset();
|
||||||
_fixed_update_functions.clear();
|
|
||||||
|
|
||||||
spdlog::get("Engine")->flush();
|
spdlog::get("Engine")->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::update(void) {
|
void Engine::update(void) {
|
||||||
FrameMarkStart("update")
|
FrameMarkStart("update")
|
||||||
if (_update_functions_modified) {
|
//if (_update_functions_modified) {
|
||||||
ZoneScopedN("MM::Engine::update::sort_update_functions")
|
//ZoneScopedN("MM::Engine::update::sort_update_functions")
|
||||||
std::sort(_update_functions.begin(), _update_functions.end(), [](const auto& a, const auto& b) { return a->priority > b->priority; });
|
//std::sort(_update_functions.begin(), _update_functions.end(), [](const auto& a, const auto& b) { return a->priority > b->priority; });
|
||||||
_update_functions_modified = false;
|
//_update_functions_modified = false;
|
||||||
}
|
//}
|
||||||
|
|
||||||
{
|
//{
|
||||||
ZoneScopedN("MM::Engine::update::traverseUpdateFunctions")
|
//ZoneScopedN("MM::Engine::update::traverseUpdateFunctions")
|
||||||
traverseUpdateFunctions(_update_functions);
|
//traverseUpdateFunctions(_update_functions);
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
_update_strategy->doUpdate(*this);
|
||||||
|
|
||||||
FrameMarkEnd("update")
|
FrameMarkEnd("update")
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::fixedUpdate(void) {
|
//void Engine::fixedUpdate(void) {
|
||||||
FrameMarkStart("fixedUpdate")
|
//FrameMarkStart("fixedUpdate")
|
||||||
if (_fixed_update_functions_modified) {
|
////if (_fixed_update_functions_modified) {
|
||||||
ZoneScopedN("MM::Engine::fixedUpdate::sort_update_functions")
|
////ZoneScopedN("MM::Engine::fixedUpdate::sort_update_functions")
|
||||||
|
|
||||||
std::sort(_fixed_update_functions.begin(), _fixed_update_functions.end(), [](const auto& a, const auto& b) { return a->priority > b->priority; });
|
////std::sort(_fixed_update_functions.begin(), _fixed_update_functions.end(), [](const auto& a, const auto& b) { return a->priority > b->priority; });
|
||||||
_fixed_update_functions_modified = false;
|
////_fixed_update_functions_modified = false;
|
||||||
}
|
////}
|
||||||
|
|
||||||
{
|
////{
|
||||||
ZoneScopedN("MM::Engine::fixedUpdate::traverseUpdateFunctions")
|
////ZoneScopedN("MM::Engine::fixedUpdate::traverseUpdateFunctions")
|
||||||
traverseUpdateFunctions(_fixed_update_functions);
|
////traverseUpdateFunctions(_fixed_update_functions);
|
||||||
}
|
////}
|
||||||
|
|
||||||
if (!_fixed_defered.empty()) {
|
////if (!_fixed_defered.empty()) {
|
||||||
ZoneScopedN("MM::Engine::fixedUpdate::defered")
|
////ZoneScopedN("MM::Engine::fixedUpdate::defered")
|
||||||
for (auto& fn : _fixed_defered) {
|
////for (auto& fn : _fixed_defered) {
|
||||||
fn(*this);
|
////fn(*this);
|
||||||
}
|
////}
|
||||||
|
|
||||||
_fixed_defered.clear();
|
////_fixed_defered.clear();
|
||||||
}
|
////}
|
||||||
|
|
||||||
FrameMarkEnd("fixedUpdate")
|
//FrameMarkEnd("fixedUpdate")
|
||||||
}
|
//}
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
|
|
||||||
static void emscripten_update(void* arg) {
|
static void emscripten_update(void* arg) {
|
||||||
using clock = std::chrono::high_resolution_clock;
|
|
||||||
static long long int accumulator = 0;
|
|
||||||
static auto now = clock::now();
|
|
||||||
|
|
||||||
auto* e = (MM::Engine*)arg;
|
auto* e = (MM::Engine*)arg;
|
||||||
|
|
||||||
auto newNow = clock::now();
|
|
||||||
auto deltaTime = std::chrono::duration_cast<std::chrono::nanoseconds>(newNow - now);
|
|
||||||
now = newNow;
|
|
||||||
accumulator += deltaTime.count();
|
|
||||||
auto dt = e->getFixedDeltaTime() * 1'000'000'000.0f;
|
|
||||||
while (accumulator >= dt) {
|
|
||||||
accumulator -= dt;
|
|
||||||
e->fixedUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
e->update();
|
e->update();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -183,30 +115,9 @@ void Engine::run(void) {
|
|||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
emscripten_set_main_loop_arg(emscripten_update, this, 0, 1);
|
emscripten_set_main_loop_arg(emscripten_update, this, 0, 1);
|
||||||
#else
|
#else
|
||||||
using clock = std::chrono::high_resolution_clock;
|
|
||||||
|
|
||||||
_is_running = true;
|
_is_running = true;
|
||||||
long long int accumulator = 0;
|
|
||||||
auto now = clock::now();
|
|
||||||
while (_is_running) {
|
while (_is_running) {
|
||||||
auto newNow = clock::now();
|
|
||||||
auto deltaTime = std::chrono::duration_cast<std::chrono::nanoseconds>(newNow - now);
|
|
||||||
now = newNow;
|
|
||||||
accumulator += deltaTime.count();
|
|
||||||
auto dt = _fixed_delta_time * 1'000'000'000.0f;
|
|
||||||
|
|
||||||
|
|
||||||
size_t continuous_counter = 0;
|
|
||||||
while (accumulator >= dt) {
|
|
||||||
continuous_counter++;
|
|
||||||
accumulator -= static_cast<long long int>(dt);
|
|
||||||
fixedUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (continuous_counter > 2) {
|
|
||||||
LOG_WARN("had {} contiguous fixedUpdates!", std::to_string(continuous_counter));
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -224,7 +135,12 @@ bool Engine::enableService(service_family::family_type s_t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_service_enable_order.emplace_back(s_t); // TODO: make sure
|
_service_enable_order.emplace_back(s_t); // TODO: make sure
|
||||||
return ss_entry->first = ss_entry->second.get()->enable(*this);
|
|
||||||
|
ss_entry->first = ss_entry->second.get()->enable(*this);
|
||||||
|
|
||||||
|
_update_strategy->enableService(s_t); // after service::enable()
|
||||||
|
|
||||||
|
return ss_entry->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found
|
// not found
|
||||||
@ -236,7 +152,11 @@ void Engine::disableService(service_family::family_type s_t) {
|
|||||||
if (_services.count(s_t)) {
|
if (_services.count(s_t)) {
|
||||||
auto* s_entry = _services[s_t].get();
|
auto* s_entry = _services[s_t].get();
|
||||||
if (s_entry->first) {
|
if (s_entry->first) {
|
||||||
|
|
||||||
|
_update_strategy->disableService(s_t);
|
||||||
|
|
||||||
s_entry->first = false;
|
s_entry->first = false;
|
||||||
|
|
||||||
s_entry->second.get()->disable(*this);
|
s_entry->second.get()->disable(*this);
|
||||||
//_service_enable_order.emplace_back(service_family::type<T>);
|
//_service_enable_order.emplace_back(service_family::type<T>);
|
||||||
auto it = std::find(_service_enable_order.begin(), _service_enable_order.end(), s_t);
|
auto it = std::find(_service_enable_order.begin(), _service_enable_order.end(), s_t);
|
||||||
@ -258,6 +178,5 @@ bool Engine::provide(service_family::family_type I, service_family::family_type
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // MM
|
} // MM
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "./engine_fwd.hpp"
|
||||||
|
|
||||||
#include <entt/core/family.hpp>
|
#include <entt/core/family.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -12,6 +14,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <mm/services/service.hpp>
|
#include <mm/services/service.hpp>
|
||||||
|
#include <mm/update_strategies/update_strategy.hpp>
|
||||||
|
|
||||||
namespace MM {
|
namespace MM {
|
||||||
|
|
||||||
@ -22,63 +25,38 @@ class Engine {
|
|||||||
public:
|
public:
|
||||||
using service_family_type = service_family::family_type;
|
using service_family_type = service_family::family_type;
|
||||||
|
|
||||||
// the services "internal" interface
|
protected:
|
||||||
//private:
|
std::unique_ptr<UpdateStrategies::UpdateStrategy> _update_strategy;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FunctionPriorityDataStructure {
|
UpdateStrategies::UpdateStrategy& getUpdateStrategy(void) { return *_update_strategy; }
|
||||||
std::function<void(Engine&)> f;
|
|
||||||
int16_t priority = 0; // 0 is equal to scene update, the higher the prio the earlier
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
explicit FunctionPriorityDataStructure(std::function<void(Engine&)> fun) : f(fun) {}
|
|
||||||
FunctionPriorityDataStructure(const FunctionPriorityDataStructure& rhs)
|
|
||||||
: f(rhs.f), priority(rhs.priority), name(rhs.name) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using FunctionDataType = FunctionPriorityDataStructure;
|
|
||||||
//using FunctionDataHandle = FunctionDataType*; // important: its a pointer
|
|
||||||
using FunctionDataHandle = std::weak_ptr<FunctionDataType>; // important: its a pointer
|
|
||||||
|
|
||||||
// return nullptr on error
|
|
||||||
[[nodiscard]] FunctionDataHandle addUpdate(std::function<void(Engine&)> function);
|
|
||||||
[[nodiscard]] FunctionDataHandle addFixedUpdate(std::function<void(Engine&)> function);
|
|
||||||
|
|
||||||
void removeUpdate(FunctionDataHandle handle);
|
|
||||||
void removeFixedUpdate(FunctionDataHandle handle);
|
|
||||||
|
|
||||||
// dont use, if you are not using it to modify the engine.
|
|
||||||
// you usualy dont need to use this, if you think you need to use this, you probably dont.
|
|
||||||
void addFixedDefer(std::function<void(Engine&)> function);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::shared_ptr<FunctionDataType>> _update_functions;
|
|
||||||
bool _update_functions_modified;
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<FunctionDataType>> _fixed_update_functions;
|
|
||||||
bool _fixed_update_functions_modified;
|
|
||||||
|
|
||||||
std::vector<std::function<void(Engine&)>> _fixed_defered;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void traverseUpdateFunctions(std::vector<std::shared_ptr<FunctionDataType>>& list); // traverses an update list, gets called by update()/fixedUpdate()
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
volatile bool _is_running = false;
|
volatile bool _is_running = false;
|
||||||
const float _fixed_delta_time;
|
|
||||||
|
private:
|
||||||
|
void setup(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Engine(void);
|
||||||
|
|
||||||
|
explicit Engine(std::unique_ptr<UpdateStrategies::UpdateStrategy> us) {
|
||||||
|
setup();
|
||||||
|
_update_strategy = std::move(us);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Engine(float f_delta_time = 1.f/60.f);
|
|
||||||
~Engine(void);
|
~Engine(void);
|
||||||
|
|
||||||
// called from destructor or explicitly
|
// called from destructor or explicitly
|
||||||
void cleanup(void);
|
void cleanup(void);
|
||||||
|
|
||||||
[[nodiscard]] float getFixedDeltaTime(void) const { return _fixed_delta_time; };
|
//[[nodiscard]] float getFixedDeltaTime(void) const { return _fixed_delta_time; };
|
||||||
|
|
||||||
void update(void);
|
void update(void);
|
||||||
void fixedUpdate(void);
|
//void fixedUpdate(void);
|
||||||
|
|
||||||
void run(void); // calls update()/fixedUpdate() until stopped
|
void run(void); // calls update() until stopped
|
||||||
void stop(void);
|
void stop(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -94,9 +72,6 @@ class Engine {
|
|||||||
>>
|
>>
|
||||||
> _services;
|
> _services;
|
||||||
|
|
||||||
// maps I to T
|
|
||||||
//std::unordered_map<service_family::family_type, service_family::family_type> _implementation_provider;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
constexpr auto type(void) {
|
constexpr auto type(void) {
|
||||||
@ -117,6 +92,12 @@ class Engine {
|
|||||||
|
|
||||||
_service_add_order.emplace_back(service_family::type<T>);
|
_service_add_order.emplace_back(service_family::type<T>);
|
||||||
|
|
||||||
|
// add updates to update strategy
|
||||||
|
_update_strategy->registerService(
|
||||||
|
service_family::type<T>,
|
||||||
|
ss_entry.get()->second->registerUpdates()
|
||||||
|
);
|
||||||
|
|
||||||
return (T&)*ss_entry.get()->second.get();
|
return (T&)*ss_entry.get()->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// this is a forwarding header
|
// this is a forwarding header
|
||||||
|
|
||||||
#include <entt/fwd.hpp>
|
#include <entt/fwd.hpp>
|
||||||
|
|
||||||
namespace MM {
|
namespace MM {
|
||||||
|
namespace UpdateStrategies { class UpdateStrategy; }
|
||||||
class Engine;
|
class Engine;
|
||||||
using Entity = entt::entity;
|
using Entity = entt::entity;
|
||||||
using Scene = entt::basic_registry<Entity>;
|
using Scene = entt::basic_registry<Entity>;
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
#include "./default_service.hpp"
|
|
||||||
|
|
||||||
namespace MM::Services {
|
|
||||||
|
|
||||||
// TODO: error handling
|
|
||||||
bool DefaultService::enable(Engine& engine) {
|
|
||||||
{
|
|
||||||
_func_handles[0] = engine.addUpdate([this](Engine& e) { this->preSceneUpdate(e); });
|
|
||||||
auto tmp_lock = _func_handles[0].lock();
|
|
||||||
tmp_lock->priority = 1;
|
|
||||||
tmp_lock->name = "DefaultService::preSceneUpdate";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
_func_handles[1] = engine.addUpdate([this](Engine& e) { this->postSceneUpdate(e); });
|
|
||||||
auto tmp_lock = _func_handles[1].lock();
|
|
||||||
tmp_lock->priority = -1;
|
|
||||||
tmp_lock->name = "DefaultService::postSceneUpdate";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
_func_handles[2] = engine.addFixedUpdate([this](Engine& e) { this->preSceneFixedUpdate(e); });
|
|
||||||
auto tmp_lock = _func_handles[2].lock();
|
|
||||||
tmp_lock->priority = 1;
|
|
||||||
tmp_lock->name = "DefaultService::preSceneFixedUpdate";
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
_func_handles[3] = engine.addFixedUpdate([this](Engine& e) { this->postSceneFixedUpdate(e); });
|
|
||||||
auto tmp_lock = _func_handles[3].lock();
|
|
||||||
tmp_lock->priority = -1;
|
|
||||||
tmp_lock->name = "DefaultService::postSceneFixedUpdate";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultService::disable(Engine& engine) {
|
|
||||||
engine.removeUpdate(_func_handles[0]);
|
|
||||||
engine.removeUpdate(_func_handles[1]);
|
|
||||||
|
|
||||||
engine.removeFixedUpdate(_func_handles[2]);
|
|
||||||
engine.removeFixedUpdate(_func_handles[3]);
|
|
||||||
|
|
||||||
_func_handles[0].reset();
|
|
||||||
_func_handles[1].reset();
|
|
||||||
_func_handles[2].reset();
|
|
||||||
_func_handles[3].reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // MM::Services
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "./service.hpp"
|
|
||||||
#include <mm/engine.hpp>
|
|
||||||
|
|
||||||
namespace MM::Services {
|
|
||||||
|
|
||||||
class DefaultService : public Service {
|
|
||||||
private:
|
|
||||||
Engine::FunctionDataHandle _func_handles[4];
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual const char* name(void) override { return "DefaultService"; }
|
|
||||||
|
|
||||||
virtual bool enable(Engine& engine) override;
|
|
||||||
virtual void disable(Engine& engine) override;
|
|
||||||
|
|
||||||
virtual void preSceneUpdate(Engine&) {}
|
|
||||||
virtual void postSceneUpdate(Engine&) {}
|
|
||||||
virtual void preSceneFixedUpdate(Engine&) {}
|
|
||||||
virtual void postSceneFixedUpdate(Engine&) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} //MM::Services
|
|
||||||
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace MM {
|
namespace MM {
|
||||||
|
|
||||||
class Engine;
|
class Engine;
|
||||||
|
namespace UpdateStrategies {
|
||||||
|
struct UpdateCreationInfo;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Services {
|
namespace Services {
|
||||||
|
|
||||||
@ -10,12 +15,13 @@ namespace MM {
|
|||||||
public:
|
public:
|
||||||
virtual ~Service(void) {}
|
virtual ~Service(void) {}
|
||||||
|
|
||||||
virtual const char* name(void) { return "UnNamedService"; }
|
virtual const char* name(void) = 0;
|
||||||
//virtual const char* name(void) = 0; // use this to find unnamed services
|
|
||||||
|
|
||||||
// required
|
|
||||||
virtual bool enable(Engine& engine) = 0;
|
virtual bool enable(Engine& engine) = 0;
|
||||||
virtual void disable(Engine& engine) = 0;
|
virtual void disable(Engine& engine) = 0;
|
||||||
|
|
||||||
|
// optional, only if service actually needs to be part of the update loop
|
||||||
|
virtual std::vector<UpdateStrategies::UpdateCreationInfo> registerUpdates(void) { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Services
|
} // Services
|
||||||
|
225
framework/engine/src/mm/update_strategies/default_strategy.cpp
Normal file
225
framework/engine/src/mm/update_strategies/default_strategy.cpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#include "./default_strategy.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace MM::UpdateStrategies {
|
||||||
|
|
||||||
|
#define __L_ASSERT(cond) if (!(cond)) { return false; }
|
||||||
|
|
||||||
|
SingleThreadedDefault::Graph& SingleThreadedDefault::getGraph(update_phase_t type) {
|
||||||
|
using type_t = update_phase_t;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case type_t::PRE:
|
||||||
|
return _pre_graph;
|
||||||
|
case type_t::MAIN:
|
||||||
|
return _main_graph;
|
||||||
|
case type_t::POST:
|
||||||
|
return _post_graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _main_graph; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<update_key_t>& SingleThreadedDefault::getActiveSet(update_phase_t type) {
|
||||||
|
using type_t = update_phase_t;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case type_t::PRE:
|
||||||
|
return _pre_active;
|
||||||
|
case type_t::MAIN:
|
||||||
|
return _main_active;
|
||||||
|
case type_t::POST:
|
||||||
|
return _post_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _main_active; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedDefault::runType(MM::Engine& engine, update_phase_t type) {
|
||||||
|
auto aset = getActiveSet(type); // copy
|
||||||
|
const auto& graph = getGraph(type);
|
||||||
|
|
||||||
|
while (!aset.empty()) {
|
||||||
|
for (const auto key : aset) {
|
||||||
|
// check if dependencies are resolved (or dont exist)
|
||||||
|
bool resolved = true;
|
||||||
|
for (const auto deps : graph.at(key)) {
|
||||||
|
if (aset.count(deps)) {
|
||||||
|
resolved = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolved) {
|
||||||
|
// do task
|
||||||
|
_tasks[key].fn(engine);
|
||||||
|
|
||||||
|
// delete task from set
|
||||||
|
aset.erase(key);
|
||||||
|
|
||||||
|
break; // this might be optional but makes the delete easy
|
||||||
|
} else {
|
||||||
|
// continue the for / dont do anything with this task yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleThreadedDefault::~SingleThreadedDefault(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::registerService(const entt::id_type s_id, std::vector<UpdateCreationInfo>&& info_array) {
|
||||||
|
__L_ASSERT(_service_tasks.count(s_id) == 0);
|
||||||
|
|
||||||
|
// early out
|
||||||
|
if (info_array.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& service_tasks = _service_tasks[s_id];
|
||||||
|
|
||||||
|
for (const UpdateCreationInfo& reg_e : info_array) {
|
||||||
|
// test if already exists
|
||||||
|
__L_ASSERT(_tasks.count(reg_e.key) == 0);
|
||||||
|
// also the graphs
|
||||||
|
__L_ASSERT(_pre_graph.count(reg_e.key) == 0);
|
||||||
|
__L_ASSERT(_main_graph.count(reg_e.key) == 0);
|
||||||
|
__L_ASSERT(_post_graph.count(reg_e.key) == 0);
|
||||||
|
|
||||||
|
// also the enabled taks
|
||||||
|
__L_ASSERT(_pre_active.count(reg_e.key) == 0);
|
||||||
|
__L_ASSERT(_main_active.count(reg_e.key) == 0);
|
||||||
|
__L_ASSERT(_post_active.count(reg_e.key) == 0);
|
||||||
|
|
||||||
|
// potentially check for cicles (can be done by provided decorator)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const UpdateCreationInfo& reg_e : info_array) {
|
||||||
|
// add to tasks
|
||||||
|
{
|
||||||
|
auto& new_task = _tasks[reg_e.key];
|
||||||
|
new_task.name = reg_e.name;
|
||||||
|
new_task.fn = reg_e.fn;
|
||||||
|
new_task.phase = reg_e.phase;
|
||||||
|
new_task.auto_enable = reg_e.auto_enable;
|
||||||
|
|
||||||
|
new_task.enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add relation to service
|
||||||
|
service_tasks.emplace_back(reg_e.key);
|
||||||
|
|
||||||
|
// fill in dependencies
|
||||||
|
auto& graph = getGraph(reg_e.phase);
|
||||||
|
graph[reg_e.key].insert(reg_e.dependencies.begin(), reg_e.dependencies.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::enable(const update_key_t key) {
|
||||||
|
__L_ASSERT(_tasks.count(key) == 1);
|
||||||
|
__L_ASSERT(_tasks[key].enabled == false);
|
||||||
|
|
||||||
|
auto ret = getActiveSet(_tasks[key].phase).emplace(key);
|
||||||
|
_tasks[key].enabled = true;
|
||||||
|
|
||||||
|
return ret.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::disable(const update_key_t key) {
|
||||||
|
__L_ASSERT(_tasks.count(key) == 1);
|
||||||
|
__L_ASSERT(_tasks[key].enabled == true);
|
||||||
|
|
||||||
|
auto& aset = getActiveSet(_tasks[key].phase);
|
||||||
|
|
||||||
|
__L_ASSERT(aset.count(key) == 1);
|
||||||
|
|
||||||
|
aset.erase(key);
|
||||||
|
|
||||||
|
_tasks[key].enabled = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::enableService(const entt::id_type s_id) {
|
||||||
|
bool succ = true;
|
||||||
|
for (const auto id : _service_tasks[s_id]) {
|
||||||
|
auto& task = _tasks[id];
|
||||||
|
|
||||||
|
// there should be no task running, if the service is no enabled!!
|
||||||
|
assert(!task.enabled);
|
||||||
|
|
||||||
|
if (task.auto_enable) {
|
||||||
|
succ &= enable(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::disableService(const entt::id_type s_id) {
|
||||||
|
bool succ = true;
|
||||||
|
for (const auto id : _service_tasks[s_id]) {
|
||||||
|
auto& task = _tasks[id];
|
||||||
|
|
||||||
|
if (task.auto_enable) {
|
||||||
|
assert(task.enabled); // this should never happen
|
||||||
|
}
|
||||||
|
|
||||||
|
if (task.enabled) {
|
||||||
|
succ &= disable(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return succ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleThreadedDefault::depend(const update_key_t A, const update_key_t B) {
|
||||||
|
// TODO: error checking lol
|
||||||
|
|
||||||
|
if (_tasks.count(A) == 0) {
|
||||||
|
return false; // can not add a dependecy of a non existing task
|
||||||
|
// TODO: or do we?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_tasks.count(B) == 1) {
|
||||||
|
if (_tasks[A].phase != _tasks[B].phase) {
|
||||||
|
// cross graph tasks are not allowed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& graph = getGraph(_tasks[A].phase);
|
||||||
|
auto ret = graph[A].emplace(B);
|
||||||
|
return ret.second; // returns whether it was inserted (or already existed)
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedDefault::doUpdate(MM::Engine& engine) {
|
||||||
|
// pre
|
||||||
|
runType(engine, update_phase_t::PRE);
|
||||||
|
|
||||||
|
// main
|
||||||
|
runType(engine, update_phase_t::MAIN);
|
||||||
|
|
||||||
|
// post
|
||||||
|
runType(engine, update_phase_t::POST);
|
||||||
|
|
||||||
|
if (!_defered_queue.empty()) {
|
||||||
|
for (auto&& fn : _defered_queue) {
|
||||||
|
fn(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
_defered_queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedDefault::addDefered(std::function<void(Engine&)> function) {
|
||||||
|
_defered_queue.emplace_back(std::move(function));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef __L_ASSERT
|
||||||
|
|
||||||
|
} // MM::UpdateStrategies
|
||||||
|
|
@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./update_strategy.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace MM::UpdateStrategies {
|
||||||
|
|
||||||
|
class SingleThreadedDefault : public MM::UpdateStrategies::UpdateStrategy {
|
||||||
|
private:
|
||||||
|
struct Task {
|
||||||
|
std::string name;
|
||||||
|
std::function<void(Engine&)> fn;
|
||||||
|
update_phase_t phase;
|
||||||
|
bool auto_enable;
|
||||||
|
|
||||||
|
bool enabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<update_key_t, Task> _tasks;
|
||||||
|
|
||||||
|
// the tasks a service has registered
|
||||||
|
std::unordered_map<entt::id_type, std::vector<update_key_t>> _service_tasks;
|
||||||
|
|
||||||
|
// tasks dependencies
|
||||||
|
using Graph = std::unordered_map<update_key_t, std::set<update_key_t>>;
|
||||||
|
// TODO: do vector for perf?
|
||||||
|
|
||||||
|
Graph _pre_graph;
|
||||||
|
Graph _main_graph;
|
||||||
|
Graph _post_graph;
|
||||||
|
|
||||||
|
std::set<update_key_t> _pre_active;
|
||||||
|
std::set<update_key_t> _main_active;
|
||||||
|
std::set<update_key_t> _post_active;
|
||||||
|
|
||||||
|
std::vector<std::function<void(Engine&)>> _defered_queue;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Graph& getGraph(update_phase_t phase);
|
||||||
|
std::set<update_key_t>& getActiveSet(update_phase_t phase);
|
||||||
|
|
||||||
|
void runType(MM::Engine& engine, update_phase_t phase);
|
||||||
|
|
||||||
|
public:
|
||||||
|
SingleThreadedDefault(void) = default;
|
||||||
|
virtual ~SingleThreadedDefault(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool registerService(const entt::id_type s_id, std::vector<UpdateCreationInfo>&& info_array) override;
|
||||||
|
|
||||||
|
bool enableService(const entt::id_type s_id) override;
|
||||||
|
bool disableService(const entt::id_type s_id) override;
|
||||||
|
|
||||||
|
void doUpdate(MM::Engine& engine) override;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool enable(const update_key_t key) override;
|
||||||
|
bool disable(const update_key_t key) override;
|
||||||
|
|
||||||
|
bool depend(const update_key_t A, const update_key_t B) override;
|
||||||
|
|
||||||
|
void addDefered(std::function<void(Engine&)> function) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // MM::UpdateStrategies
|
||||||
|
|
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./update_strategy.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace MM::UpdateStrategies {
|
||||||
|
|
||||||
|
// checks for cyles
|
||||||
|
template<class T>
|
||||||
|
class DependencyCheckDecorator : public T {
|
||||||
|
static_assert(std::is_base_of_v<UpdateStrategy, T>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//DependencyCheckDecorator(void) = default;
|
||||||
|
|
||||||
|
//template<typename ...Args>
|
||||||
|
//DependencyCheckDecorator(Args... args) : T(args) {
|
||||||
|
//}
|
||||||
|
|
||||||
|
virtual ~DependencyCheckDecorator(void) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void doUpdate(MM::Engine& engine) override {
|
||||||
|
T::doUpdate(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool registerService(const entt::id_type s_id, std::vector<UpdateCreationInfo>&& info_array) override {
|
||||||
|
return T::registerService(s_id, std::move(info_array));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enableService(const entt::id_type s_id) override {
|
||||||
|
return T::enableService(s_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disableService(const entt::id_type s_id) override {
|
||||||
|
return T::disableService(s_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool enable(const update_key_t key) override {
|
||||||
|
return T::enable(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disable(const update_key_t key) override {
|
||||||
|
return T::disable(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool depend(const update_key_t A, const update_key_t B) override {
|
||||||
|
return T::depend(A, B);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // MM::UpdateStrategies
|
||||||
|
|
@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../engine_fwd.hpp"
|
||||||
|
|
||||||
|
#include <mm/services/service.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace MM::UpdateStrategies {
|
||||||
|
|
||||||
|
using update_key_t = entt::id_type;
|
||||||
|
|
||||||
|
enum class update_phase_t {
|
||||||
|
PRE, // for on-main-thread
|
||||||
|
MAIN,
|
||||||
|
POST // for on-main-thread
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UpdateCreationInfo {
|
||||||
|
update_key_t key; // key for dependencies
|
||||||
|
|
||||||
|
std::string name; // for debugging
|
||||||
|
|
||||||
|
std::function<void(Engine&)> fn; // the actual payload
|
||||||
|
|
||||||
|
update_phase_t phase = update_phase_t::MAIN;
|
||||||
|
|
||||||
|
bool auto_enable = true; // whether this update is enabled with the service
|
||||||
|
|
||||||
|
// this update also depends on (in the same phase)
|
||||||
|
std::vector<update_key_t> dependencies {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// pure virtual interface for managing the update logic of the engine
|
||||||
|
class UpdateStrategy {
|
||||||
|
public:
|
||||||
|
virtual ~UpdateStrategy(void) {}
|
||||||
|
|
||||||
|
protected: // the engine facing interface
|
||||||
|
friend ::MM::Engine;
|
||||||
|
|
||||||
|
// TODO: return something?
|
||||||
|
virtual bool registerService(const entt::id_type s_id, std::vector<UpdateCreationInfo>&& info_array) = 0;
|
||||||
|
|
||||||
|
// returns true on success
|
||||||
|
// failure conditions may include:
|
||||||
|
// - already en/dis-abled
|
||||||
|
// - is auto_enable
|
||||||
|
// - impossible dependencies
|
||||||
|
virtual bool enableService(const entt::id_type s_id) = 0;
|
||||||
|
virtual bool disableService(const entt::id_type s_id) = 0;
|
||||||
|
|
||||||
|
// runs one update
|
||||||
|
virtual void doUpdate(MM::Engine& engine) = 0;
|
||||||
|
|
||||||
|
public: // the user facing interface
|
||||||
|
// similar to *ableService, can only be used for non-auto_enable-updates
|
||||||
|
virtual bool enable(const update_key_t key) = 0;
|
||||||
|
virtual bool disable(const update_key_t key) = 0;
|
||||||
|
|
||||||
|
// add extra dependencies into the tree, the user has the most knowlage about
|
||||||
|
// the order the services should execute in.
|
||||||
|
// A -> B (make A depend on B)
|
||||||
|
virtual bool depend(const update_key_t A, const update_key_t B) = 0;
|
||||||
|
|
||||||
|
// WIP:
|
||||||
|
|
||||||
|
// dont use, if you are not using it to modify the engine.
|
||||||
|
// you usualy dont need to use this, if you think you need to use this, you probably dont.
|
||||||
|
//virtual void addFixedDefered(std::function<void(Engine&)> function) = 0;
|
||||||
|
virtual void addDefered(std::function<void(Engine&)> function) = 0; // called after everything
|
||||||
|
|
||||||
|
//virtual std::future addAsync(std::function<void(Engine&)> function) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // MM::UpdateStrategies
|
||||||
|
|
@ -1,8 +1,13 @@
|
|||||||
add_executable(engine_test
|
add_executable(engine_test
|
||||||
update_test.cpp
|
update_strategy_test.cpp
|
||||||
run_test.cpp
|
default_us_test.cpp
|
||||||
service_system_test.cpp
|
#dependency_check_us_test.cpp
|
||||||
default_service_test.cpp
|
|
||||||
|
# old:
|
||||||
|
#update_test.cpp
|
||||||
|
#run_test.cpp
|
||||||
|
#service_test.cpp
|
||||||
|
#default_service_test.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(engine_test PRIVATE ".")
|
target_include_directories(engine_test PRIVATE ".")
|
||||||
@ -10,6 +15,7 @@ target_include_directories(engine_test PRIVATE ".")
|
|||||||
target_link_libraries(engine_test
|
target_link_libraries(engine_test
|
||||||
engine
|
engine
|
||||||
gtest_main
|
gtest_main
|
||||||
|
gmock
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test(NAME engine_test COMMAND engine_test)
|
add_test(NAME engine_test COMMAND engine_test)
|
||||||
|
108
framework/engine/test/default_us_test.cpp
Normal file
108
framework/engine/test/default_us_test.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "mm/update_strategies/update_strategy.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mm/engine.hpp>
|
||||||
|
#include <mm/update_strategies/default_strategy.hpp>
|
||||||
|
|
||||||
|
#include <entt/core/hashed_string.hpp>
|
||||||
|
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
|
class MockService : public MM::Services::Service {
|
||||||
|
public:
|
||||||
|
const char* name(void) override { return "MockService"; }
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, enable, (MM::Engine& engine), (override));
|
||||||
|
MOCK_METHOD(void, disable, (MM::Engine& engine), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(std::vector<MM::UpdateStrategies::UpdateCreationInfo>, registerUpdates, (), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(default_update_strategy, simple_update) {
|
||||||
|
MM::Engine engine(std::make_unique<MM::UpdateStrategies::SingleThreadedDefault>());
|
||||||
|
|
||||||
|
engine.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(default_update_strategy, service) {
|
||||||
|
class TmpMockService : public MockService {
|
||||||
|
public:
|
||||||
|
TmpMockService(void) {
|
||||||
|
EXPECT_CALL(*this, registerUpdates())
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, enable(_))
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, enable(_))
|
||||||
|
.WillByDefault(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, disable(_))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
MM::Engine engine(std::make_unique<MM::UpdateStrategies::SingleThreadedDefault>());
|
||||||
|
|
||||||
|
engine.addService<TmpMockService>();
|
||||||
|
|
||||||
|
ASSERT_TRUE(engine.enableService<TmpMockService>());
|
||||||
|
engine.disableService<TmpMockService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(default_update_strategy, run_1) {
|
||||||
|
class TmpMockService : public MockService {
|
||||||
|
int& _counter;
|
||||||
|
public:
|
||||||
|
explicit TmpMockService(int& counter) : _counter(counter) {
|
||||||
|
EXPECT_CALL(*this, registerUpdates())
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, registerUpdates())
|
||||||
|
.WillByDefault([this]() -> std::vector<MM::UpdateStrategies::UpdateCreationInfo> {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
"TmpMockService"_hs,
|
||||||
|
"TmpMockService",
|
||||||
|
[this](MM::Engine&) { _counter++; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, enable(_))
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, enable(_))
|
||||||
|
.WillByDefault(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, disable(_))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
int counter = 1;
|
||||||
|
MM::Engine engine(std::make_unique<MM::UpdateStrategies::SingleThreadedDefault>());
|
||||||
|
ASSERT_EQ(counter, 1);
|
||||||
|
|
||||||
|
engine.addService<TmpMockService>(counter);
|
||||||
|
ASSERT_EQ(counter, 1);
|
||||||
|
|
||||||
|
ASSERT_TRUE(engine.enableService<TmpMockService>());
|
||||||
|
ASSERT_EQ(counter, 1);
|
||||||
|
|
||||||
|
engine.getUpdateStrategy().addDefered([](MM::Engine& e) { e.stop(); });
|
||||||
|
|
||||||
|
engine.run();
|
||||||
|
|
||||||
|
ASSERT_EQ(counter, 2);
|
||||||
|
|
||||||
|
engine.disableService<TmpMockService>();
|
||||||
|
|
||||||
|
ASSERT_EQ(counter, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
144
framework/engine/test/dependency_check_us_test.cpp
Normal file
144
framework/engine/test/dependency_check_us_test.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <mm/engine.hpp>
|
||||||
|
#include <mm/update_strategies/update_strategy.hpp>
|
||||||
|
#include <mm/update_strategies/dependency_check_decorater.hpp>
|
||||||
|
#include <mm/services/service.hpp>
|
||||||
|
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
|
#include <entt/core/hashed_string.hpp>
|
||||||
|
//using namespace ::entt::literals;
|
||||||
|
|
||||||
|
class MockUpdateStrategy : public MM::UpdateStrategies::UpdateStrategy {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(
|
||||||
|
bool,
|
||||||
|
registerService,
|
||||||
|
(const entt::id_type s_id, std::vector<MM::UpdateStrategies::UpdateCreationInfo>&& info),
|
||||||
|
(override)
|
||||||
|
);
|
||||||
|
|
||||||
|
// protected:
|
||||||
|
MOCK_METHOD(void, doUpdate, (MM::Engine& engine), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, enableService, (const entt::id_type s_id), (override));
|
||||||
|
MOCK_METHOD(bool, disableService, (const entt::id_type s_id), (override));
|
||||||
|
|
||||||
|
// public:
|
||||||
|
MOCK_METHOD(bool, enable, (const MM::UpdateStrategies::update_key_t key), (override));
|
||||||
|
MOCK_METHOD(bool, disable, (const MM::UpdateStrategies::update_key_t key), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, depend, (const MM::UpdateStrategies::update_key_t A, const MM::UpdateStrategies::update_key_t B), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void, addDefered, (std::function<void(MM::Engine&)> function), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockService : public MM::Services::Service {
|
||||||
|
public:
|
||||||
|
const char* name(void) override { return "MockService"; }
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, enable, (MM::Engine& engine), (override));
|
||||||
|
MOCK_METHOD(void, disable, (MM::Engine& engine), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(std::vector<MM::UpdateStrategies::UpdateCreationInfo>, registerUpdates, (), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(dependency_check_update_strategy, decoration_mock) {
|
||||||
|
auto dep = std::make_unique<MM::UpdateStrategies::DependencyCheckDecorator<MockUpdateStrategy>>();
|
||||||
|
auto* mock = static_cast<MockUpdateStrategy*>(dep.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock, registerService(_, _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, enableService(_))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, disableService(_))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
class TmpMockService : public MockService {
|
||||||
|
public:
|
||||||
|
TmpMockService(void) {
|
||||||
|
EXPECT_CALL(*this, registerUpdates())
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, enable(_))
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, enable(_))
|
||||||
|
.WillByDefault(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, disable(_))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
MM::Engine engine(std::move(dep));
|
||||||
|
|
||||||
|
engine.addService<TmpMockService>();
|
||||||
|
|
||||||
|
ASSERT_TRUE(engine.enableService<TmpMockService>());
|
||||||
|
engine.disableService<TmpMockService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(dependency_check_update_strategy, simple_loop) {
|
||||||
|
auto dep = std::make_unique<MM::UpdateStrategies::DependencyCheckDecorator<MockUpdateStrategy>>();
|
||||||
|
auto* mock = static_cast<MockUpdateStrategy*>(dep.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock, registerService(_, _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, enableService(_))
|
||||||
|
.Times(1);
|
||||||
|
//EXPECT_CALL(*mock, disableService(_))
|
||||||
|
//.Times(1);
|
||||||
|
EXPECT_CALL(*mock, depend(_, _))
|
||||||
|
.Times(1); // the layer should catch the error before forwarding it
|
||||||
|
|
||||||
|
class BonkersService : public MM::Services::Service {
|
||||||
|
public:
|
||||||
|
virtual ~BonkersService(void) {}
|
||||||
|
|
||||||
|
const char* name(void) override { return "BonkersService"; }
|
||||||
|
|
||||||
|
bool enable(MM::Engine& engine) override {
|
||||||
|
engine.getUpdateStrategy().depend("BonkerA"_hs, "BonkerB"_hs);
|
||||||
|
|
||||||
|
// maleformed: direct cycle
|
||||||
|
engine.getUpdateStrategy().depend("BonkerB"_hs, "BonkerA"_hs);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable(MM::Engine&) override {}
|
||||||
|
|
||||||
|
std::vector<MM::UpdateStrategies::UpdateCreationInfo> registerUpdates(void) override {
|
||||||
|
return {
|
||||||
|
MM::UpdateStrategies::UpdateCreationInfo{
|
||||||
|
"BonkerA"_hs,
|
||||||
|
"BonkerA",
|
||||||
|
[](MM::Engine&) {}
|
||||||
|
},
|
||||||
|
MM::UpdateStrategies::UpdateCreationInfo{
|
||||||
|
"BonkerB"_hs,
|
||||||
|
"BonkerB",
|
||||||
|
[](MM::Engine&) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
MM::Engine engine(std::move(dep));
|
||||||
|
|
||||||
|
engine.addService<BonkersService>();
|
||||||
|
|
||||||
|
ASSERT_THROW(engine.enableService<BonkersService>(), std::logic_error);
|
||||||
|
|
||||||
|
//engine.disableService<BonkersService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <mm/engine.hpp>
|
#include <mm/engine.hpp>
|
||||||
|
|
||||||
TEST(engine_run, test_run) {
|
TEST(engine_run, test_run) {
|
||||||
MM::Engine engine;
|
MM::Engine engine{std::make_unique};
|
||||||
|
|
||||||
bool run = false;
|
bool run = false;
|
||||||
|
|
||||||
|
140
framework/engine/test/update_strategy_test.cpp
Normal file
140
framework/engine/test/update_strategy_test.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "mm/services/service.hpp"
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mm/engine.hpp>
|
||||||
|
#include <mm/update_strategies/update_strategy.hpp>
|
||||||
|
|
||||||
|
#include <entt/core/hashed_string.hpp>
|
||||||
|
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::_;
|
||||||
|
|
||||||
|
class MockUpdateStrategy : public MM::UpdateStrategies::UpdateStrategy {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(
|
||||||
|
bool,
|
||||||
|
registerService,
|
||||||
|
(const entt::id_type s_id, std::vector<MM::UpdateStrategies::UpdateCreationInfo>&& info),
|
||||||
|
(override)
|
||||||
|
);
|
||||||
|
|
||||||
|
// protected:
|
||||||
|
MOCK_METHOD(void, doUpdate, (MM::Engine& engine), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, enableService, (const entt::id_type s_id), (override));
|
||||||
|
MOCK_METHOD(bool, disableService, (const entt::id_type s_id), (override));
|
||||||
|
|
||||||
|
// public:
|
||||||
|
MOCK_METHOD(bool, enable, (const MM::UpdateStrategies::update_key_t key), (override));
|
||||||
|
MOCK_METHOD(bool, disable, (const MM::UpdateStrategies::update_key_t key), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, depend, (const MM::UpdateStrategies::update_key_t A, const MM::UpdateStrategies::update_key_t B), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void, addDefered, (std::function<void(MM::Engine&)> function), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
class MockService : public MM::Services::Service {
|
||||||
|
public:
|
||||||
|
const char* name(void) override { return "MockService"; }
|
||||||
|
|
||||||
|
MOCK_METHOD(bool, enable, (MM::Engine& engine), (override));
|
||||||
|
MOCK_METHOD(void, disable, (MM::Engine& engine), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(std::vector<MM::UpdateStrategies::UpdateCreationInfo>, registerUpdates, (), (override));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(engine_mock, update_strategy_run) {
|
||||||
|
auto mock = std::make_unique<MockUpdateStrategy>();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock, doUpdate(_))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
MM::Engine engine(std::move(mock));
|
||||||
|
|
||||||
|
engine.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(engine_mock, service_update_strategy) {
|
||||||
|
auto mock = std::make_unique<MockUpdateStrategy>();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock, registerService(_, _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, enableService(_))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, disableService(_))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
class TmpMockService : public MockService {
|
||||||
|
public:
|
||||||
|
TmpMockService(void) {
|
||||||
|
EXPECT_CALL(*this, registerUpdates())
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, enable(_))
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, enable(_))
|
||||||
|
.WillByDefault(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, disable(_))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
MM::Engine engine(std::move(mock));
|
||||||
|
|
||||||
|
engine.addService<TmpMockService>();
|
||||||
|
|
||||||
|
ASSERT_TRUE(engine.enableService<TmpMockService>());
|
||||||
|
engine.disableService<TmpMockService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(engine_mock, service_update_strategy_run_1) {
|
||||||
|
auto mock = std::make_unique<MockUpdateStrategy>();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mock, registerService(_, _))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, enableService(_))
|
||||||
|
.Times(1);
|
||||||
|
EXPECT_CALL(*mock, disableService(_))
|
||||||
|
.Times(1);
|
||||||
|
|
||||||
|
class TmpMockService : public MockService {
|
||||||
|
public:
|
||||||
|
explicit TmpMockService(void) {
|
||||||
|
EXPECT_CALL(*this, registerUpdates())
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, registerUpdates())
|
||||||
|
.WillByDefault([]() -> std::vector<MM::UpdateStrategies::UpdateCreationInfo> {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
"TmpMockService"_hs,
|
||||||
|
"TmpMockService",
|
||||||
|
[](MM::Engine&) {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, enable(_))
|
||||||
|
.Times(1);
|
||||||
|
ON_CALL(*this, enable(_))
|
||||||
|
.WillByDefault(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(*this, disable(_))
|
||||||
|
.Times(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
MM::Engine engine(std::move(mock));
|
||||||
|
|
||||||
|
engine.addService<TmpMockService>();
|
||||||
|
|
||||||
|
ASSERT_TRUE(engine.enableService<TmpMockService>());
|
||||||
|
engine.disableService<TmpMockService>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,84 +1,123 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <mm/engine.hpp>
|
#include <mm/engine.hpp>
|
||||||
|
|
||||||
|
//class MyUpdateStrategy : public MM::UpdateStrategy::UpdateStrategy {
|
||||||
|
//public:
|
||||||
|
//virtual ~MyUpdateStrategy(void) {}
|
||||||
|
|
||||||
|
//// return nullptr on error
|
||||||
|
//[[nodiscard]] MM::UpdateStrategy::FunctionDataHandle addUpdate(std::function<void(MM::Engine&)> fn) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[[nodiscard]] MM::UpdateStrategy::FunctionDataHandle addFixedUpdate(std::function<void(MM::Engine&)> fn) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// knows which one
|
||||||
|
//void removeUpdate(MM::UpdateStrategy::FunctionDataHandle handle) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// dont use, if you are not using it to modify the engine.
|
||||||
|
//// you usualy dont need to use this, if you think you need to use this, you probably dont.
|
||||||
|
//void addFixedDefered(std::function<void(MM::Engine&)> function) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
////virtual std::future addAsync(std::function<void(Engine&)> function) = 0;
|
||||||
|
|
||||||
|
//void doUpdate(MM::Engine& engine) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void doFixedUpdate(MM::Engine& engine) override {
|
||||||
|
//}
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
||||||
|
class MyEngine : public MM::Engine {
|
||||||
|
public:
|
||||||
|
MyEngine(void) : MM::Engine(std::make_unique<MyUpdateStrategy>()) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TEST(engine_fixed_update, empty_add_rm) {
|
TEST(engine_fixed_update, empty_add_rm) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
auto test_fun = [](auto&) {};
|
auto test_fun = [](auto&) {};
|
||||||
|
|
||||||
auto handle = engine.addFixedUpdate(test_fun);
|
auto handle = engine.getUpdateStrategy().addFixedUpdate(test_fun);
|
||||||
ASSERT_NE(handle.lock(), nullptr);
|
ASSERT_NE(handle.lock(), nullptr);
|
||||||
|
|
||||||
handle.lock()->priority = 1;
|
//handle.lock()->priority = 1;
|
||||||
|
|
||||||
engine.removeFixedUpdate(handle);
|
engine.getUpdateStrategy().removeUpdate(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(engine_update, empty_add_rm) {
|
TEST(engine_update, empty_add_rm) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
auto test_fun = [](auto&) {};
|
auto test_fun = [](auto&) {};
|
||||||
|
|
||||||
auto handle = engine.addUpdate(test_fun);
|
auto handle = engine.getUpdateStrategy().addUpdate(test_fun);
|
||||||
ASSERT_NE(handle.lock(), nullptr);
|
ASSERT_NE(handle.lock(), nullptr);
|
||||||
|
|
||||||
handle.lock()->priority = 1;
|
//handle.lock()->priority = 1;
|
||||||
|
|
||||||
engine.removeUpdate(handle);
|
engine.getUpdateStrategy().removeUpdate(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(engine_fixed_update, empty_run) {
|
TEST(engine_fixed_update, empty_run) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
auto test_fun = [](auto&) {};
|
auto test_fun = [](auto&) {};
|
||||||
|
|
||||||
auto handle = engine.addFixedUpdate(test_fun);
|
auto handle = engine.getUpdateStrategy().addFixedUpdate(test_fun);
|
||||||
ASSERT_NE(handle.lock(), nullptr);
|
ASSERT_NE(handle.lock(), nullptr);
|
||||||
|
|
||||||
handle.lock()->priority = 1;
|
//handle.lock()->priority = 1;
|
||||||
|
|
||||||
engine.fixedUpdate(); // single update
|
engine.fixedUpdate(); // single update
|
||||||
|
|
||||||
engine.removeFixedUpdate(handle);
|
engine.getUpdateStrategy().removeUpdate(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(engine_update, empty_run) {
|
TEST(engine_update, empty_run) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
auto test_fun = [](auto&) {};
|
auto test_fun = [](auto&) {};
|
||||||
|
|
||||||
auto handle = engine.addUpdate(test_fun);
|
auto handle = engine.getUpdateStrategy().addUpdate(test_fun);
|
||||||
ASSERT_NE(handle.lock(), nullptr);
|
ASSERT_NE(handle.lock(), nullptr);
|
||||||
|
|
||||||
handle.lock()->priority = 1;
|
//handle.lock()->priority = 1;
|
||||||
|
|
||||||
engine.update();
|
engine.update();
|
||||||
|
|
||||||
engine.removeUpdate(handle);
|
engine.getUpdateStrategy().removeUpdate(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(engine_fixed_update, test_run) {
|
TEST(engine_fixed_update, test_run) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
bool run = false;
|
bool run = false;
|
||||||
|
|
||||||
auto test_fun = [&run](auto&) { run = true; };
|
auto test_fun = [&run](auto&) { run = true; };
|
||||||
|
|
||||||
auto handle = engine.addFixedUpdate(test_fun);
|
auto handle = engine.getUpdateStrategy().addFixedUpdate(test_fun);
|
||||||
ASSERT_NE(handle.lock(), nullptr);
|
ASSERT_NE(handle.lock(), nullptr);
|
||||||
|
|
||||||
handle.lock()->priority = 1;
|
//handle.lock()->priority = 1;
|
||||||
|
|
||||||
ASSERT_FALSE(run);
|
ASSERT_FALSE(run);
|
||||||
engine.fixedUpdate(); // single update
|
engine.fixedUpdate(); // single update
|
||||||
ASSERT_TRUE(run);
|
ASSERT_TRUE(run);
|
||||||
|
|
||||||
engine.removeFixedUpdate(handle);
|
engine.getUpdateStrategy().removeUpdate(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
TEST(engine_update, test_run) {
|
TEST(engine_update, test_run) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
bool run = false;
|
bool run = false;
|
||||||
|
|
||||||
@ -97,7 +136,7 @@ TEST(engine_update, test_run) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(engine_fixed_update, test_order_run) {
|
TEST(engine_fixed_update, test_order_run) {
|
||||||
MM::Engine engine;
|
MyEngine engine;
|
||||||
|
|
||||||
bool run1 = false;
|
bool run1 = false;
|
||||||
bool run2 = false;
|
bool run2 = false;
|
||||||
@ -228,3 +267,4 @@ TEST(engine_update, test_order_rev_run) {
|
|||||||
engine.removeUpdate(handle2);
|
engine.removeUpdate(handle2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user