aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/manager
diff options
context:
space:
mode:
authorJAROWMR <jarorutjes07@gmail.com>2024-12-11 20:12:12 +0100
committerJAROWMR <jarorutjes07@gmail.com>2024-12-11 20:12:12 +0100
commitc90423d5c9d38ae426c168e52994a4fd63e6f266 (patch)
treefa18eae598cf0fccbb0f2dffd3e1d76fedaed53a /src/crepe/manager
parent9b4f6f24f29e8873a14989ba8c9fccfbc460af7f (diff)
parent194ee3f192c3343c3ccc28dfa97fed180503ffd4 (diff)
merge master
Diffstat (limited to 'src/crepe/manager')
-rw-r--r--src/crepe/manager/CMakeLists.txt5
-rw-r--r--src/crepe/manager/ComponentManager.cpp11
-rw-r--r--src/crepe/manager/ComponentManager.h73
-rw-r--r--src/crepe/manager/ComponentManager.hpp81
-rw-r--r--src/crepe/manager/EventManager.cpp6
-rw-r--r--src/crepe/manager/EventManager.h23
-rw-r--r--src/crepe/manager/LoopTimerManager.cpp91
-rw-r--r--src/crepe/manager/LoopTimerManager.h175
-rw-r--r--src/crepe/manager/Mediator.h20
-rw-r--r--src/crepe/manager/ResourceManager.cpp30
-rw-r--r--src/crepe/manager/ResourceManager.h78
-rw-r--r--src/crepe/manager/ResourceManager.hpp27
-rw-r--r--src/crepe/manager/SaveManager.cpp33
-rw-r--r--src/crepe/manager/SaveManager.h32
-rw-r--r--src/crepe/manager/SceneManager.cpp3
15 files changed, 592 insertions, 96 deletions
diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt
index 517b8a2..f73e165 100644
--- a/src/crepe/manager/CMakeLists.txt
+++ b/src/crepe/manager/CMakeLists.txt
@@ -4,6 +4,8 @@ target_sources(crepe PUBLIC
Manager.cpp
SaveManager.cpp
SceneManager.cpp
+ LoopTimerManager.cpp
+ ResourceManager.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -16,5 +18,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SaveManager.h
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/EventManager.cpp b/src/crepe/manager/EventManager.cpp
index 20f0dd3..6aa49ee 100644
--- a/src/crepe/manager/EventManager.cpp
+++ b/src/crepe/manager/EventManager.cpp
@@ -3,11 +3,9 @@
using namespace crepe;
using namespace std;
-EventManager & EventManager::get_instance() {
- static EventManager instance;
- return instance;
+EventManager::EventManager(Mediator & mediator) : Manager(mediator) {
+ this->mediator.event_manager = *this;
}
-
void EventManager::dispatch_events() {
for (auto & event : this->events_queue) {
this->handle_event(event.type, event.channel, *event.event.get());
diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h
index ba5e98b..639e37f 100644
--- a/src/crepe/manager/EventManager.h
+++ b/src/crepe/manager/EventManager.h
@@ -8,6 +8,8 @@
#include "../api/Event.h"
#include "../api/EventHandler.h"
+#include "Manager.h"
+
namespace crepe {
//! Event listener unique ID
@@ -22,27 +24,19 @@ typedef size_t subscription_t;
typedef size_t event_channel_t;
/**
- * \class EventManager
* \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
*
* The `EventManager` acts as a centralized event system. It allows for registering callbacks
* for specific event types, triggering events synchronously, queueing events for later
* processing, and managing subscriptions via unique identifiers.
*/
-class EventManager {
+class EventManager : public Manager {
public:
static constexpr const event_channel_t CHANNEL_ALL = -1;
-
/**
- * \brief Get the singleton instance of the EventManager.
- *
- * This method returns the unique instance of the EventManager, creating it if it
- * doesn't already exist. Ensures only one instance is active in the program.
- *
- * \return Reference to the singleton instance of the EventManager.
+ * \param mediator A reference to a Mediator object used for transfering managers.
*/
- static EventManager & get_instance();
-
+ EventManager(Mediator & mediator);
/**
* \brief Subscribe to a specific event type.
*
@@ -108,13 +102,6 @@ public:
private:
/**
- * \brief Default constructor for the EventManager.
- *
- * Constructor is private to enforce the singleton pattern.
- */
- EventManager() = default;
-
- /**
* \struct QueueEntry
* \brief Represents an entry in the event queue.
*/
diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp
new file mode 100644
index 0000000..9819632
--- /dev/null
+++ b/src/crepe/manager/LoopTimerManager.cpp
@@ -0,0 +1,91 @@
+#include <chrono>
+#include <thread>
+
+#include "../util/Log.h"
+
+#include "LoopTimerManager.h"
+
+using namespace crepe;
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
+LoopTimerManager::LoopTimerManager(Mediator & mediator) : Manager(mediator) {
+ this->mediator.loop_timer = *this;
+ dbg_trace();
+}
+
+void LoopTimerManager::start() {
+ this->last_frame_time = std::chrono::steady_clock::now();
+
+ this->elapsed_time = elapsed_time_t{0};
+ this->elapsed_fixed_time = elapsed_time_t{0};
+ this->delta_time = duration_t{0};
+}
+
+void LoopTimerManager::update() {
+ time_point_t current_frame_time = std::chrono::steady_clock::now();
+ // Convert to duration in seconds for delta time
+ this->delta_time = current_frame_time - last_frame_time;
+
+ if (this->delta_time > this->maximum_delta_time) {
+ this->delta_time = this->maximum_delta_time;
+ }
+ if (this->delta_time > 0s) {
+ this->actual_fps = 1.0 / duration_cast<seconds>(this->delta_time).count();
+ } else {
+ this->actual_fps = 0;
+ }
+ this->elapsed_time += duration_cast<elapsed_time_t>(this->delta_time);
+ this->last_frame_time = current_frame_time;
+}
+
+duration_t LoopTimerManager::get_delta_time() const {
+ return this->delta_time * this->time_scale;
+}
+
+elapsed_time_t LoopTimerManager::get_elapsed_time() const { return this->elapsed_time; }
+
+void LoopTimerManager::advance_fixed_elapsed_time() {
+ this->elapsed_fixed_time
+ += std::chrono::duration_cast<elapsed_time_t>(this->fixed_delta_time);
+}
+
+void LoopTimerManager::set_target_framerate(unsigned fps) {
+ this->target_fps = fps;
+ //check if fps is lower or equals 0
+ if (fps <= 0) return;
+ // target time per frame in seconds
+ this->frame_target_time = duration_t(1s) / this->target_fps;
+}
+
+unsigned LoopTimerManager::get_fps() const { return this->actual_fps; }
+
+void LoopTimerManager::set_time_scale(double value) { this->time_scale = value; }
+
+float LoopTimerManager::get_time_scale() const { return this->time_scale; }
+
+void LoopTimerManager::enforce_frame_rate() {
+ time_point_t current_frame_time = std::chrono::steady_clock::now();
+ duration_t frame_duration = current_frame_time - this->last_frame_time;
+ // Check if frame duration is less than the target frame time
+ if (frame_duration < this->frame_target_time) {
+ duration_t delay_time = this->frame_target_time - frame_duration;
+ if (delay_time > 0s) {
+ std::this_thread::sleep_for(delay_time);
+ }
+ }
+}
+
+duration_t LoopTimerManager::get_lag() const {
+ return this->elapsed_time - this->elapsed_fixed_time;
+}
+
+duration_t LoopTimerManager::get_scaled_fixed_delta_time() const {
+ return this->fixed_delta_time * this->time_scale;
+}
+
+void LoopTimerManager::set_fixed_delta_time(float seconds) {
+ this->fixed_delta_time = duration_t(seconds);
+}
+
+duration_t LoopTimerManager::get_fixed_delta_time() const { return this->fixed_delta_time; }
diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h
new file mode 100644
index 0000000..91403e4
--- /dev/null
+++ b/src/crepe/manager/LoopTimerManager.h
@@ -0,0 +1,175 @@
+#pragma once
+
+#include <chrono>
+
+#include "Manager.h"
+
+namespace crepe {
+
+typedef std::chrono::duration<double> duration_t;
+typedef std::chrono::duration<unsigned long long, std::micro> elapsed_time_t;
+
+/**
+ * \brief Manages timing and frame rate for the game loop.
+ *
+ * The LoopTimerManager class is responsible for calculating and managing timing functions
+ * such as delta time, frames per second (FPS), fixed time steps, and time scaling. It ensures
+ * consistent frame updates and supports game loop operations, such as handling fixed updates
+ * for physics and other time-sensitive operations.
+ */
+class LoopTimerManager : public Manager {
+public:
+ /**
+ * \param mediator A reference to a Mediator object used for transfering managers.
+ */
+ LoopTimerManager(Mediator & mediator);
+ /**
+ * \brief Get the current delta time for the current frame.
+ *
+ * This value represents the estimated frame duration of the current frame.
+ * This value can be used in the frame_update to convert pixel based values to time based values.
+ *
+ * \return Delta time in seconds since the last frame.
+ */
+ duration_t get_delta_time() const;
+
+ /**
+ * \brief Get the current elapsed time (total time passed )
+ *
+ * \note The current game time may vary from real-world elapsed time. It is the cumulative
+ * sum of each frame's delta time.
+ *
+ * \return Elapsed game time in seconds.
+ */
+ elapsed_time_t get_elapsed_time() const;
+
+ /**
+ * \brief Set the target frames per second (FPS).
+ *
+ * \param fps The desired frames rendered per second.
+ */
+ void set_target_framerate(unsigned fps);
+
+ /**
+ * \brief Get the current frames per second (FPS).
+ *
+ * \return Current FPS.
+ */
+ unsigned get_fps() const;
+
+ /**
+ * \brief Get the current time scale.
+ *
+ * \return The current time scale, where (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up).
+ * up the game.
+ */
+ float get_time_scale() const;
+
+ /**
+ * \brief Set the time scale.
+ *
+ * time_scale is a value that changes the delta time that can be retrieved using get_delta_time function.
+ *
+ * \param time_scale The desired time scale (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up).
+ */
+ void set_time_scale(double time_scale);
+
+ /**
+ * \brief Get the fixed delta time in seconds without scaling by the time scale.
+ *
+ * This value is used in the LoopManager to determine how many times
+ * the fixed_update should be called within a given interval.
+ *
+ * \return The unscaled fixed delta time in seconds.
+ */
+ duration_t get_fixed_delta_time() const;
+
+ /**
+ * \brief Set the fixed_delta_time in seconds.
+ *
+ * \param seconds fixed_delta_time in seconds.
+ *
+ * The fixed_delta_time value is used to determine how many times per second the fixed_update and process_input functions are called.
+ *
+ */
+ void set_fixed_delta_time(float seconds);
+
+ /**
+ * \brief Retrieves the scaled fixed delta time in seconds.
+ *
+ * The scaled fixed delta time is the timing value used within the `fixed_update` function.
+ * It is adjusted by the time_scale to account for any changes in the simulation's
+ * speed.
+ *
+ * \return The fixed delta time, scaled by the current time scale, in seconds.
+ */
+ duration_t get_scaled_fixed_delta_time() const;
+
+private:
+ //! Friend relation to use start,enforce_frame_rate,get_lag,update,advance_fixed_update.
+ friend class LoopManager;
+ /**
+ * \brief Start the loop timer.
+ *
+ * Initializes the timer to begin tracking frame times.
+ */
+ void start();
+ /**
+ * \brief Enforce the frame rate limit.
+ *
+ * Ensures that the game loop does not exceed the target FPS by delaying frame updates as
+ * necessary.
+ */
+ void enforce_frame_rate();
+ /**
+ * \brief Get the accumulated lag in the game loop.
+ *
+ * Lag represents the difference between the target frame time and the actual frame time,
+ * useful for managing fixed update intervals.
+ *
+ * \return Accumulated lag in seconds.
+ */
+ duration_t get_lag() const;
+
+ /**
+ * \brief Update the timer to the current frame.
+ *
+ * Calculates and updates the delta time for the current frame and adds it to the cumulative
+ * game time.
+ */
+ void update();
+
+ /**
+ * \brief Progress the elapsed fixed time by the fixed delta time interval.
+ *
+ * This method advances the game's fixed update loop by adding the fixed_delta_time
+ * to elapsed_fixed_time, ensuring the fixed update catches up with the elapsed time.
+ */
+ void advance_fixed_elapsed_time();
+
+private:
+ //! Target frames per second.
+ unsigned target_fps = 60;
+ //! Actual frames per second.
+ unsigned actual_fps = 0;
+ //! Time scale for speeding up or slowing down the game (0 = pause, < 1 = slow down, 1 = normal speed, > 1 = speed up).
+ float time_scale = 1;
+ //! Maximum delta time in seconds to avoid large jumps.
+ duration_t maximum_delta_time{0.25};
+ //! Delta time for the current frame in seconds.
+ duration_t delta_time{0.0};
+ //! Target time per frame in seconds
+ duration_t frame_target_time{1.0 / target_fps};
+ //! Fixed delta time for fixed updates in seconds.
+ duration_t fixed_delta_time{1.0 / 50.0};
+ //! Total elapsed game time in microseconds.
+ elapsed_time_t elapsed_time{0};
+ //! Total elapsed time for fixed updates in microseconds.
+ elapsed_time_t elapsed_fixed_time{0};
+
+ typedef std::chrono::steady_clock::time_point time_point_t;
+ //! Time of the last frame.
+ time_point_t last_frame_time;
+};
+
+} // namespace crepe
diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h
index 8094d80..a336410 100644
--- a/src/crepe/manager/Mediator.h
+++ b/src/crepe/manager/Mediator.h
@@ -2,16 +2,15 @@
#include "../util/OptionalRef.h"
-// 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 EventManager;
+class LoopTimerManager;
+class SaveManager;
+class ResourceManager;
+class SDLContext;
/**
* Struct to pass references to classes that would otherwise need to be singletons down to
@@ -26,12 +25,13 @@ class SceneManager;
* \warning This class should never be directly accessible from the API
*/
struct Mediator {
+ OptionalRef<SDLContext> sdl_context;
OptionalRef<ComponentManager> component_manager;
OptionalRef<SceneManager> scene_manager;
- OptionalRef<SaveManager> save_manager = SaveManager::get_instance();
- OptionalRef<EventManager> event_manager = EventManager::get_instance();
- OptionalRef<SDLContext> sdl_context = SDLContext::get_instance();
- OptionalRef<LoopTimer> timer = LoopTimer::get_instance();
+ OptionalRef<EventManager> event_manager;
+ OptionalRef<LoopTimerManager> loop_timer;
+ OptionalRef<SaveManager> save_manager;
+ OptionalRef<ResourceManager> resource_manager;
};
} // namespace crepe
diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp
new file mode 100644
index 0000000..a141a46
--- /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) {
+ dbg_trace();
+ mediator.resource_manager = *this;
+}
+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..cf5c949
--- /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, this->mediator);
+
+ 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
diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp
index 50a9fbb..d4ca90b 100644
--- a/src/crepe/manager/SceneManager.cpp
+++ b/src/crepe/manager/SceneManager.cpp
@@ -32,4 +32,7 @@ void SceneManager::load_next_scene() {
// Load the new scene
scene->load_scene();
+
+ //clear the next scene
+ next_scene.clear();
}