diff options
Diffstat (limited to 'src/crepe/manager')
-rw-r--r-- | src/crepe/manager/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/crepe/manager/ComponentManager.cpp | 11 | ||||
-rw-r--r-- | src/crepe/manager/ComponentManager.h | 73 | ||||
-rw-r--r-- | src/crepe/manager/ComponentManager.hpp | 81 | ||||
-rw-r--r-- | src/crepe/manager/Mediator.h | 5 | ||||
-rw-r--r-- | src/crepe/manager/ResourceManager.cpp | 30 | ||||
-rw-r--r-- | src/crepe/manager/ResourceManager.h | 78 | ||||
-rw-r--r-- | src/crepe/manager/ResourceManager.hpp | 27 | ||||
-rw-r--r-- | src/crepe/manager/SaveManager.cpp | 33 | ||||
-rw-r--r-- | src/crepe/manager/SaveManager.h | 32 |
10 files changed, 308 insertions, 65 deletions
diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index 29d6df0..f73e165 100644 --- a/src/crepe/manager/CMakeLists.txt +++ b/src/crepe/manager/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources(crepe PUBLIC SaveManager.cpp SceneManager.cpp LoopTimerManager.cpp + ResourceManager.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -18,5 +19,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SceneManager.h SceneManager.hpp LoopTimerManager.h + ResourceManager.h + ResourceManager.hpp ) diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 80cf8b4..df30d27 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,4 +1,5 @@ #include "../api/GameObject.h" +#include "../api/Metadata.h" #include "../types.h" #include "../util/Log.h" @@ -61,3 +62,13 @@ GameObject ComponentManager::new_object(const string & name, const string & tag, void ComponentManager::set_persistent(game_object_id_t id, bool persistent) { this->persistent[id] = persistent; } + +set<game_object_id_t> ComponentManager::get_objects_by_name(const string & name) const { + return this->get_objects_by_predicate<Metadata>( + [name](const Metadata & data) { return data.name == name; }); +} + +set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) const { + return this->get_objects_by_predicate<Metadata>( + [tag](const Metadata & data) { return data.tag == tag; }); +} diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 44429d9..19a8e81 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -1,6 +1,7 @@ #pragma once #include <memory> +#include <set> #include <typeindex> #include <unordered_map> #include <vector> @@ -134,8 +135,77 @@ public: */ template <typename T> RefVector<T> get_components_by_type() const; + /** + * \brief Get all components of a specific type on a GameObject with name \c name + * + * \tparam T The type of the component + * \param name Metadata::name for the same game_object_id as the returned components + * \return Components matching criteria + */ + template <typename T> + RefVector<T> get_components_by_name(const std::string & name) const; + /** + * \brief Get all components of a specific type on a GameObject with tag \c tag + * + * \tparam T The type of the component + * \param name Metadata::tag for the same game_object_id as the returned components + * \return Components matching criteria + */ + template <typename T> + RefVector<T> get_components_by_tag(const std::string & tag) const; + +private: + /** + * \brief Get object IDs by predicate function + * + * This function calls the predicate function \c pred for all components matching type \c T, + * and adds their parent game_object_id to a \c std::set if the predicate returns true. + * + * \tparam T The type of the component to check the predicate against + * \param pred Predicate function + * + * \note The predicate function may be called for multiple components with the same \c + * game_object_id. In this case, the ID is added if *any* call returns \c true. + * + * \returns game_object_id for all components where the predicate returned true + */ + template <typename T> + std::set<game_object_id_t> + get_objects_by_predicate(const std::function<bool(const T &)> & pred) const; + + /** + * \brief Get components of type \c T for multiple game object IDs + * + * \tparam T The type of the components to return + * \param ids The object IDs + * + * \return All components matching type \c T and one of the IDs in \c ids + */ + template <typename T> + RefVector<T> get_components_by_ids(const std::set<game_object_id_t> & ids) const; + + /** + * \brief Get object IDs for objects with name \c name + * + * \param name Object name to match + * \returns Object IDs where Metadata::name is equal to \c name + */ + std::set<game_object_id_t> get_objects_by_name(const std::string & name) const; + /** + * \brief Get object IDs for objects with tag \c tag + * + * \param tag Object tag to match + * \returns Object IDs where Metadata::tag is equal to \c tag + */ + std::set<game_object_id_t> get_objects_by_tag(const std::string & tag) const; private: + //! By Component \c std::type_index (readability helper type) + template <typename T> + using by_type = std::unordered_map<std::type_index, T>; + //! By \c game_object_id index (readability helper type) + template <typename T> + using by_id_index = std::vector<T>; /** * \brief The components * @@ -146,8 +216,7 @@ private: * The first vector is for the ids of the GameObjects and the second vector is for the * components (because a GameObject might have multiple components). */ - std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>> - components; + by_type<by_id_index<std::vector<std::unique_ptr<Component>>>> components; //! Persistent flag for each GameObject std::unordered_map<game_object_id_t, bool> persistent; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index ffb38ec..9e70865 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -95,32 +95,25 @@ template <typename T> RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const { using namespace std; - // Determine the type of T (this is used as the key of the unordered_map<>) - type_index type = typeid(T); - - // Create an empty vector<> - RefVector<T> component_vector; - - if (this->components.find(type) == this->components.end()) return component_vector; - - // Get the correct vector<> - const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type); - - // Make sure that the id (that we are looking for) is within the boundaries of the vector<> - if (id >= component_array.size()) return component_vector; - - // Loop trough the whole vector<> - for (const unique_ptr<Component> & component_ptr : component_array[id]) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast<T *>(component_ptr.get()); - - if (casted_component == nullptr) continue; + static_assert(is_base_of<Component, T>::value, + "get_components_by_id must recieve a derivative class of Component"); - // Add the dereferenced raw pointer to the vector<> - component_vector.push_back(*casted_component); + type_index type = typeid(T); + if (!this->components.contains(type)) return {}; + + const by_id_index<vector<unique_ptr<Component>>> & components_by_id + = this->components.at(type); + if (id >= components_by_id.size()) return {}; + + RefVector<T> out = {}; + const vector<unique_ptr<Component>> & components = components_by_id.at(id); + for (auto & component_ptr : components) { + if (component_ptr == nullptr) continue; + Component & component = *component_ptr.get(); + out.push_back(static_cast<T &>(component)); } - return component_vector; + return out; } template <typename T> @@ -158,4 +151,46 @@ RefVector<T> ComponentManager::get_components_by_type() const { return component_vector; } +template <typename T> +std::set<game_object_id_t> +ComponentManager::get_objects_by_predicate(const std::function<bool(const T &)> & pred) const { + using namespace std; + + set<game_object_id_t> objects = {}; + RefVector<T> components = this->get_components_by_type<T>(); + + for (const T & component : components) { + game_object_id_t id = dynamic_cast<const Component &>(component).game_object_id; + if (objects.contains(id)) continue; + if (!pred(component)) continue; + objects.insert(id); + } + + return objects; +} + +template <typename T> +RefVector<T> +ComponentManager::get_components_by_ids(const std::set<game_object_id_t> & ids) const { + using namespace std; + + RefVector<T> out = {}; + for (game_object_id_t id : ids) { + RefVector<T> components = get_components_by_id<T>(id); + out.insert(out.end(), components.begin(), components.end()); + } + + return out; +} + +template <typename T> +RefVector<T> ComponentManager::get_components_by_name(const std::string & name) const { + return this->get_components_by_ids<T>(this->get_objects_by_name(name)); +} + +template <typename T> +RefVector<T> ComponentManager::get_components_by_tag(const std::string & tag) const { + return this->get_components_by_ids<T>(this->get_objects_by_tag(tag)); +} + } // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index ad51cc6..dfb37dc 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -11,6 +11,8 @@ namespace crepe { class ComponentManager; class SceneManager; +class SaveManager; +class ResourceManager; class LoopTimerManager; class EventManager; @@ -31,7 +33,8 @@ struct Mediator { OptionalRef<SceneManager> scene_manager; OptionalRef<EventManager> event_manager; OptionalRef<LoopTimerManager> loop_timer; - OptionalRef<SaveManager> save_manager = SaveManager::get_instance(); + OptionalRef<SaveManager> save_manager; + OptionalRef<ResourceManager> resource_manager; OptionalRef<SDLContext> sdl_context = SDLContext::get_instance(); }; diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp new file mode 100644 index 0000000..7c01808 --- /dev/null +++ b/src/crepe/manager/ResourceManager.cpp @@ -0,0 +1,30 @@ +#include "util/Log.h" + +#include "ResourceManager.h" + +using namespace crepe; +using namespace std; + +ResourceManager::ResourceManager(Mediator & mediator) : Manager(mediator) { + mediator.resource_manager = *this; + dbg_trace(); +} +ResourceManager::~ResourceManager() { dbg_trace(); } + +void ResourceManager::clear() { + std::erase_if(this->resources, [](const pair<const Asset, CacheEntry> & pair) { + const CacheEntry & entry = pair.second; + return entry.persistent == false; + }); +} + +void ResourceManager::clear_all() { this->resources.clear(); } + +void ResourceManager::set_persistent(const Asset & asset, bool persistent) { + this->get_entry(asset).persistent = persistent; +} + +ResourceManager::CacheEntry & ResourceManager::get_entry(const Asset & asset) { + if (!this->resources.contains(asset)) this->resources[asset] = {}; + return this->resources.at(asset); +} diff --git a/src/crepe/manager/ResourceManager.h b/src/crepe/manager/ResourceManager.h new file mode 100644 index 0000000..84b275d --- /dev/null +++ b/src/crepe/manager/ResourceManager.h @@ -0,0 +1,78 @@ +#pragma once + +#include <memory> +#include <unordered_map> + +#include "../Resource.h" +#include "../api/Asset.h" + +#include "Manager.h" + +namespace crepe { + +/** + * \brief Owner of concrete Resource instances + * + * ResourceManager caches concrete Resource instances per Asset. Concrete resources are + * destroyed at the end of scenes by default, unless the game programmer marks them as + * persistent. + */ +class ResourceManager : public Manager { +public: + ResourceManager(Mediator & mediator); + virtual ~ResourceManager(); // dbg_trace + +private: + //! Cache entry + struct CacheEntry { + //! Concrete resource instance + std::unique_ptr<Resource> resource = nullptr; + //! Prevent ResourceManager::clear from removing this entry + bool persistent = false; + }; + //! Internal cache + std::unordered_map<const Asset, CacheEntry> resources; + /** + * \brief Ensure a cache entry exists for this asset and return a mutable reference to it + * + * \param asset Asset the concrete resource is instantiated from + * + * \returns Mutable reference to cache entry + */ + CacheEntry & get_entry(const Asset & asset); + +public: + /** + * \brief Mark a resource as persistent (i.e. used across multiple scenes) + * + * \param asset Asset the concrete resource is instantiated from + * \param persistent Whether this resource is persistent (true=keep, false=destroy) + */ + void set_persistent(const Asset & asset, bool persistent); + + /** + * \brief Retrieve reference to concrete Resource by Asset + * + * \param asset Asset the concrete resource is instantiated from + * \tparam Resource Concrete derivative of Resource + * + * This class instantiates the concrete resource if it is not yet stored in the internal + * cache, or returns a reference to the cached resource if it already exists. + * + * \returns Reference to concrete resource + * + * \throws std::runtime_error if the \c Resource parameter does not match with the actual + * type of the resource stored in the cache for this Asset + */ + template <typename Resource> + Resource & get(const Asset & asset); + + //! Clear non-persistent resources from cache + void clear(); + //! Clear all resources from cache regardless of persistence + void clear_all(); +}; + +} // namespace crepe + +#include "ResourceManager.hpp" diff --git a/src/crepe/manager/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp new file mode 100644 index 0000000..5167d71 --- /dev/null +++ b/src/crepe/manager/ResourceManager.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include <format> + +#include "ResourceManager.h" + +namespace crepe { + +template <typename T> +T & ResourceManager::get(const Asset & asset) { + using namespace std; + static_assert(is_base_of<Resource, T>::value, + "cache must recieve a derivative class of Resource"); + + CacheEntry & entry = this->get_entry(asset); + if (entry.resource == nullptr) entry.resource = make_unique<T>(asset); + + T * concrete_resource = dynamic_cast<T *>(entry.resource.get()); + if (concrete_resource == nullptr) + throw runtime_error(format("ResourceManager: mismatch between requested type and " + "actual type of resource ({})", + asset.get_path())); + + return *concrete_resource; +} + +} // namespace crepe diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index d4ed1c1..691ea2f 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,13 +1,25 @@ #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 +102,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 +151,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..61a978d 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -1,9 +1,12 @@ #pragma once +#include <functional> #include <memory> #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,13 @@ 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 |