diff options
author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-12-11 13:58:19 +0100 |
---|---|---|
committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-12-11 13:58:19 +0100 |
commit | d228d4b3856606ad4395723b2703759a0ebe9832 (patch) | |
tree | bfd3019ca2c9bec258fffca9a9204ddc88768264 | |
parent | 770496ee9d0e45480c0e0f8951adb8eee247bfe1 (diff) | |
parent | c45b60941b82dec2097d958b396a117b1634eada (diff) |
Merge branch 'master' into loek/replay
-rw-r--r-- | src/crepe/api/LoopManager.h | 3 | ||||
-rw-r--r-- | src/crepe/api/Script.cpp | 8 | ||||
-rw-r--r-- | src/crepe/api/Script.h | 3 | ||||
-rw-r--r-- | src/crepe/api/Script.hpp | 3 | ||||
-rw-r--r-- | src/crepe/manager/Mediator.h | 4 | ||||
-rw-r--r-- | src/crepe/manager/SaveManager.cpp | 35 | ||||
-rw-r--r-- | src/crepe/manager/SaveManager.h | 33 | ||||
-rw-r--r-- | src/crepe/util/OptionalRef.h | 10 | ||||
-rw-r--r-- | src/crepe/util/OptionalRef.hpp | 7 | ||||
-rw-r--r-- | src/example/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/example/savemgr.cpp | 44 | ||||
-rw-r--r-- | src/test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/test/SaveManagerTest.cpp | 40 | ||||
-rw-r--r-- | src/test/ScriptSaveManagerTest.cpp | 35 |
14 files changed, 134 insertions, 94 deletions
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 1ac6a39..1183a4d 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,6 +4,7 @@ #include "../manager/ComponentManager.h" #include "../manager/ResourceManager.h" #include "../manager/SceneManager.h" +#include "../manager/SaveManager.h" #include "../system/System.h" #include "LoopTimer.h" @@ -68,6 +69,8 @@ private: SceneManager scene_manager{mediator}; //! Resource manager instance ResourceManager resource_manager{mediator}; + //! Save manager instance + SaveManager save_manager{mediator}; //! SDL context \todo no more singletons! SDLContext & sdl_context = SDLContext::get_instance(); diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 4091fd4..753a9e3 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -8,8 +8,7 @@ using namespace crepe; using namespace std; Script::~Script() { - Mediator & mediator = this->mediator; - EventManager & mgr = mediator.event_manager; + EventManager & mgr = this->mediator->event_manager; for (auto id : this->listeners) { mgr.unsubscribe(id); } @@ -21,7 +20,8 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) { } void Script::set_next_scene(const string & name) { - Mediator & mediator = this->mediator; - SceneManager & mgr = mediator.scene_manager; + SceneManager & mgr = this->mediator->scene_manager; mgr.set_next_scene(name); } + +SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 49bdcf5..668e5d1 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -132,6 +132,9 @@ protected: */ void set_next_scene(const std::string & name); + //! Retrieve SaveManager reference + SaveManager & get_save_manager() const; + //! \} private: diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 16e0dc5..225a51c 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -31,8 +31,7 @@ void Script::logf(Args &&... args) { template <typename EventType> void Script::subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel) { - Mediator & mediator = this->mediator; - EventManager & mgr = mediator.event_manager; + EventManager & mgr = this->mediator->event_manager; subscription_t listener = mgr.subscribe<EventType>( [this, callback](const EventType & data) -> bool { bool & active = this->active; diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index ba0b41f..35ac181 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -5,13 +5,13 @@ // TODO: remove these singletons: #include "../facade/SDLContext.h" #include "EventManager.h" -#include "SaveManager.h" #include "api/LoopTimer.h" namespace crepe { class ComponentManager; class SceneManager; +class SaveManager; class ResourceManager; /** @@ -29,7 +29,7 @@ class ResourceManager; struct Mediator { OptionalRef<ComponentManager> component_manager; OptionalRef<SceneManager> scene_manager; - OptionalRef<SaveManager> save_manager = SaveManager::get_instance(); + OptionalRef<SaveManager> save_manager; OptionalRef<EventManager> event_manager = EventManager::get_instance(); OptionalRef<ResourceManager> resource_manager; OptionalRef<SDLContext> sdl_context = SDLContext::get_instance(); diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index d4ed1c1..39b92d4 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,13 +1,27 @@ #include "../ValueBroker.h" #include "../api/Config.h" #include "../facade/DB.h" -#include "../util/Log.h" #include "SaveManager.h" using namespace std; using namespace crepe; +SaveManager::SaveManager(Mediator & mediator) : Manager(mediator) { + mediator.save_manager = *this; +} + +DB & SaveManager::get_db() { + if (this->db == nullptr) { + Config & cfg = Config::get_instance(); + this->db = { + new DB(cfg.savemgr.location), + [](void * db){ delete static_cast<DB *>(db); } + }; + } + return *static_cast<DB *>(this->db.get()); +} + template <> string SaveManager::serialize(const string & value) const noexcept { return value; @@ -90,22 +104,6 @@ int32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize<int64_t>(value); } -SaveManager::SaveManager() { dbg_trace(); } - -SaveManager & SaveManager::get_instance() { - dbg_trace(); - static SaveManager instance; - return instance; -} - -DB & SaveManager::get_db() { - Config & cfg = Config::get_instance(); - // TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the - // default equivalent is on Windows using some third party library - static DB db(cfg.savemgr.location); - return db; -} - bool SaveManager::has(const string & key) { DB & db = this->get_db(); return db.has(key); @@ -155,7 +153,8 @@ ValueBroker<T> SaveManager::get(const string & key) { return { [this, key](const T & target) { this->set<T>(key, target); }, [this, key, value]() mutable -> const T & { - value = this->deserialize<T>(this->get_db().get(key)); + DB & db = this->get_db(); + value = this->deserialize<T>(db.get(key)); return value; }, }; diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 3d8c852..27e625c 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -1,9 +1,12 @@ #pragma once #include <memory> +#include <functional> #include "../ValueBroker.h" +#include "Manager.h" + namespace crepe { class DB; @@ -18,7 +21,7 @@ class DB; * * The underlying database is a key-value store. */ -class SaveManager { +class SaveManager : public Manager { public: /** * \brief Get a read/write reference to a value and initialize it if it does not yet exist @@ -63,8 +66,8 @@ public: */ bool has(const std::string & key); -private: - SaveManager(); +public: + SaveManager(Mediator & mediator); virtual ~SaveManager() = default; private: @@ -89,26 +92,12 @@ private: template <typename T> T deserialize(const std::string & value) const noexcept; -public: - // singleton - static SaveManager & get_instance(); - SaveManager(const SaveManager &) = delete; - SaveManager(SaveManager &&) = delete; - SaveManager & operator=(const SaveManager &) = delete; - SaveManager & operator=(SaveManager &&) = delete; - +protected: + //! Create or return DB + virtual DB & get_db(); private: - /** - * \brief Create an instance of DB and return its reference - * - * \returns DB instance - * - * This function exists because DB is a facade class, which can't directly be used in the API - * without workarounds - * - * TODO: better solution - */ - static DB & get_db(); + //! Database + std::unique_ptr<void, std::function<void(void*)>> db = nullptr; }; } // namespace crepe diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h index 3201667..1b2cb3f 100644 --- a/src/crepe/util/OptionalRef.h +++ b/src/crepe/util/OptionalRef.h @@ -25,7 +25,7 @@ public: */ OptionalRef<T> & operator=(T & ref); /** - * \brief Retrieve this reference + * \brief Retrieve this reference (cast) * * \returns Internal reference if it is set * @@ -33,6 +33,14 @@ public: */ operator T &() const; /** + * \brief Retrieve this reference (member access) + * + * \returns Internal reference if it is set + * + * \throws std::runtime_error if this function is called while the reference it not set + */ + T * operator->() const; + /** * \brief Check if this reference is not empty * * \returns `true` if reference is set, or `false` if it is not diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp index 4608c9e..5e36b3a 100644 --- a/src/crepe/util/OptionalRef.hpp +++ b/src/crepe/util/OptionalRef.hpp @@ -19,6 +19,13 @@ OptionalRef<T>::operator T &() const { } template <typename T> +T * OptionalRef<T>::operator->() const { + if (this->ref == nullptr) + throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); + return this->ref; +} + +template <typename T> OptionalRef<T> & OptionalRef<T>::operator=(T & ref) { this->ref = &ref; return *this; diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 7260f35..791001e 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -16,7 +16,6 @@ function(add_example target_name) add_dependencies(examples ${target_name}) endfunction() -add_example(savemgr) add_example(rendering_particle) add_example(game) add_example(button) diff --git a/src/example/savemgr.cpp b/src/example/savemgr.cpp deleted file mode 100644 index 65c4a34..0000000 --- a/src/example/savemgr.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** \file - * - * Standalone example for usage of the save manager - */ - -#include <cassert> -#include <crepe/api/Config.h> -#include <crepe/api/SaveManager.h> -#include <crepe/util/Log.h> -#include <crepe/util/Proxy.h> - -using namespace crepe; - -// unrelated setup code -int _ = []() { - // make sure all log messages get printed - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::TRACE; - - return 0; // satisfy compiler -}(); - -int main() { - const char * key = "mygame.test"; - - SaveManager & mgr = SaveManager::get_instance(); - - dbg_logf("has key = {}", mgr.has(key)); - ValueBroker<int> prop = mgr.get<int>(key, 0); - Proxy<int> val = mgr.get<int>(key, 0); - - dbg_logf("val = {}", mgr.get<int>(key).get()); - prop.set(1); - dbg_logf("val = {}", mgr.get<int>(key).get()); - val = 2; - dbg_logf("val = {}", mgr.get<int>(key).get()); - mgr.set<int>(key, 3); - dbg_logf("val = {}", mgr.get<int>(key).get()); - - dbg_logf("has key = {}", mgr.has(key)); - assert(true == mgr.has(key)); - - return 0; -} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index a0bb56d..28a4f8b 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -19,6 +19,8 @@ target_sources(test_main PUBLIC ScriptEventTest.cpp ScriptSceneTest.cpp Profiling.cpp + SaveManagerTest.cpp + ScriptSaveManagerTest.cpp ScriptECSTest.cpp ReplayManagerTest.cpp ) diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp new file mode 100644 index 0000000..e9b0c29 --- /dev/null +++ b/src/test/SaveManagerTest.cpp @@ -0,0 +1,40 @@ +#include <gtest/gtest.h> + +#include <crepe/ValueBroker.h> +#include <crepe/facade/DB.h> +#include <crepe/manager/SaveManager.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class SaveManagerTest : public Test { + Mediator m; + class TestSaveManager : public SaveManager { + using SaveManager::SaveManager; + + // in-memory database for testing + DB db{}; + virtual DB & get_db() override { return this->db; } + }; + +public: + TestSaveManager mgr{m}; +}; + +TEST_F(SaveManagerTest, ReadWrite) { + ASSERT_FALSE(mgr.has("foo")); + mgr.set<string>("foo", "bar"); + ASSERT_TRUE(mgr.has("foo")); + + ValueBroker value = mgr.get<string>("foo"); + EXPECT_EQ(value.get(), "bar"); +} + +TEST_F(SaveManagerTest, DefaultValue) { + ValueBroker value = mgr.get<int>("foo", 3); + + ASSERT_EQ(value.get(), 3); + value.set(5); + ASSERT_EQ(value.get(), 5); +} diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp new file mode 100644 index 0000000..64403c4 --- /dev/null +++ b/src/test/ScriptSaveManagerTest.cpp @@ -0,0 +1,35 @@ +#include <gtest/gtest.h> + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include <crepe/facade/DB.h> +#include <crepe/manager/SaveManager.h> + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptSaveManagerTest : public ScriptTest { +public: + class TestSaveManager : public SaveManager { + using SaveManager::SaveManager; + + // in-memory database for testing + DB db{}; + virtual DB & get_db() override { return this->db; } + }; + + TestSaveManager save_mgr{mediator}; +}; + +TEST_F(ScriptSaveManagerTest, GetSaveManager) { + MyScript & script = this->script; + + SaveManager & mgr = script.get_save_manager(); + + EXPECT_EQ(&mgr, &save_mgr); +} |