From 0e45d4835f65ff9127a16adcbe9a9f0a20370cfc Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Thu, 28 Nov 2024 18:18:13 +0100
Subject: implement resource manager

---
 src/crepe/ResourceManager.cpp    | 37 +++++++---------
 src/crepe/ResourceManager.h      | 51 +++++----------------
 src/crepe/ResourceManager.hpp    | 26 +++++++++++
 src/test/ResourceManagerTest.cpp | 95 ++++++++++++++--------------------------
 4 files changed, 87 insertions(+), 122 deletions(-)
 create mode 100644 src/crepe/ResourceManager.hpp

diff --git a/src/crepe/ResourceManager.cpp b/src/crepe/ResourceManager.cpp
index 111b9e0..8b1fbf5 100644
--- a/src/crepe/ResourceManager.cpp
+++ b/src/crepe/ResourceManager.cpp
@@ -1,5 +1,3 @@
-#include <stdexcept>
-
 #include "util/Log.h"
 
 #include "ResourceManager.h"
@@ -11,26 +9,23 @@ ResourceManager::~ResourceManager() { 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();
 }
 
-// template <typename T>
-// T & ResourceManager::cache(const Asset & asset) {
-// 	dbg_trace();
-// 	static_assert(is_base_of<Resource, T>::value, "cache must recieve a derivative class of Resource");
-// 
-// 	if (!this->resources.contains(asset))
-// 		this->resources[asset] = make_unique<T>(asset);
-// 
-// 	Resource * resource = this->resources.at(asset).get();
-// 	T * concrete_resource = dynamic_cast<T *>(resource);
-// 
-// 	if (concrete_resource == nullptr)
-// 		throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path()));
-// 
-// 	return *concrete_resource;
-// }
-// 
-// #include "facade/Sound.h"
-// template Sound & ResourceManager::cache(const Asset &);
+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/ResourceManager.h b/src/crepe/ResourceManager.h
index 26a86a8..fc50b65 100644
--- a/src/crepe/ResourceManager.h
+++ b/src/crepe/ResourceManager.h
@@ -5,12 +5,10 @@
 
 #include "api/Asset.h"
 
-#include "Component.h"
 #include "Resource.h"
 
 namespace crepe {
 
-
 /**
  * \brief The ResourceManager is responsible for storing and managing assets over
  * multiple scenes.
@@ -26,51 +24,24 @@ public:
 	virtual ~ResourceManager(); // dbg_trace
 
 private:
-	template <typename Resource>
-	Resource & get_internal(const Component & component, const Asset & asset);
-
-	template <typename Resource>
-	const Asset & get_source(const Component & component) const;
-
+	struct CacheEntry {
+		std::unique_ptr<Resource> resource = nullptr;
+		bool persistent = false;
+	};
 	//! A cache that holds all the assets, accessible by their file path, over multiple scenes.
-	std::unordered_map<const Asset, std::unique_ptr<Resource>> resources;
+	std::unordered_map<const Asset, CacheEntry> resources;
+	CacheEntry & get_entry(const Asset & asset);
 
 public:
-	/**
-	 * \brief Caches an asset by loading it from the given file path.
-	 *
-	 * \param file_path The path to the asset file to load.
-	 * \param reload If true, the asset will be reloaded from the file, even if
-	 * it is already cached.
-	 * \tparam T The type of asset to cache (e.g., texture, sound, etc.).
-	 * 
-	 * \return A reference to the resource
-	 * 
-	 * This template function caches the asset at the given file path. If the
-	 * asset is already cached, the existing instance will be returned.
-	 * Otherwise, the concrete resource will be instantiated and added to the
-	 * cache.
-	 */
-	template <typename Resource>
-	void cache(const Asset & asset, bool persistent = false);
-
-	template <typename Component>
-	void cache(const Component & component, bool persistent = false);
-
-	// void resman.cache<Resource>(Asset, Lifetime);
-	// void resman.cache(Component, Asset, Lifetime);
+	void set_persistent(const Asset & asset, bool persistent);
 
-	template <typename Resource, typename Component>
-	Resource & get(const Component & component);
+	template <typename Resource>
+	Resource & get(const Asset & asset);
 
-	//! Clear the resource cache
 	void clear();
+	void clear_all();
 };
 
-class Sound;
-class AudioSource;
-template <>
-Sound & ResourceManager::get(const AudioSource & component);
-
 } // namespace crepe
 
+#include "ResourceManager.hpp"
diff --git a/src/crepe/ResourceManager.hpp b/src/crepe/ResourceManager.hpp
new file mode 100644
index 0000000..8270bc5
--- /dev/null
+++ b/src/crepe/ResourceManager.hpp
@@ -0,0 +1,26 @@
+#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;
+}
+
+}
+
diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp
index f57c419..cc3b022 100644
--- a/src/test/ResourceManagerTest.cpp
+++ b/src/test/ResourceManagerTest.cpp
@@ -6,96 +6,69 @@
 #include <crepe/util/Log.h>
 #include <crepe/ResourceManager.h>
 #include <crepe/api/GameObject.h>
-#include <crepe/Component.h>
-#include <crepe/ComponentManager.h>
 
 using namespace std;
 using namespace crepe;
 using namespace testing;
 
-class TestComponent : public Component {
-public:
-	TestComponent(game_object_id_t id, const Asset & asset)
-		: Component(id),
-		  source(asset) {}
-	const Asset source;
-};
-
-class TestResource : public Resource {
-public:
-	static unsigned instances;
-
-public:
-	const unsigned instance;
-	TestResource(const Asset & src)
-		: Resource(src),
-		  instance(this->instances++) { }
-};
-unsigned TestResource::instances = 0;
-
-template <>
-TestResource & ResourceManager::get(const TestComponent & component) {
-	return this->get_internal<TestResource>(component, component.source);
-}
-
 class ResourceManagerTest : public Test {
 public:
 	ResourceManager resource_manager{};
 
-	static constexpr const char * ASSET_LOCATION = "asset/texture/img.png";
+	Asset asset_a{"asset/texture/img.png"};
+	Asset asset_b{"asset/texture/ERROR.png"};
+
+	class TestResource : public Resource {
+	public:
+		static unsigned instances;
 
-	TestComponent a{0, ASSET_LOCATION};
-	TestComponent b{1, ASSET_LOCATION};
+	public:
+		const unsigned instance;
+		TestResource(const Asset & src)
+			: Resource(src),
+				instance(this->instances++) { }
+		~TestResource() { this->instances--; }
+		bool operator == (const TestResource & other) const {
+			return this->instance == other.instance;
+		}
+	};
 
 private:
 	void SetUp() override {
 		TestResource::instances = 0;
 	}
 };
+unsigned ResourceManagerTest::TestResource::instances = 0;
 
-TEST_F(ResourceManagerTest, Uncached) {
-	TestResource & res_1 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_2 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_3 = resource_manager.get<TestResource>(b); // 2
-	TestResource & res_4 = resource_manager.get<TestResource>(b); // 2
+TEST_F(ResourceManagerTest, Default) {
+	TestResource & res_1 = resource_manager.get<TestResource>(asset_a);
+	TestResource & res_2 = resource_manager.get<TestResource>(asset_a);
+	TestResource & res_3 = resource_manager.get<TestResource>(asset_b);
+	TestResource & res_4 = resource_manager.get<TestResource>(asset_b);
 
 	ASSERT_EQ(res_1, res_2);
 	ASSERT_EQ(res_3, res_4);
 	EXPECT_NE(res_1, res_3);
 
 	EXPECT_EQ(TestResource::instances, 2);
-}
-
-// TODO: per GameObject / Component
-TEST_F(ResourceManagerTest, PerComponent) {
-	resource_manager.cache(a);
-	resource_manager.cache(b);
 
-	TestResource & res_1 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_2 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_3 = resource_manager.get<TestResource>(b); // 2
-	TestResource & res_4 = resource_manager.get<TestResource>(b); // 2
+	resource_manager.clear();
+}
 
-	ASSERT_EQ(res_1, res_2);
-	ASSERT_EQ(res_3, res_4);
-	EXPECT_NE(res_1, res_3);
+TEST_F(ResourceManagerTest, Persistent) {
+	resource_manager.set_persistent(asset_a, true);
+	EXPECT_EQ(TestResource::instances, 0);
 
+	resource_manager.get<TestResource>(asset_a);
+	resource_manager.get<TestResource>(asset_a);
+	resource_manager.get<TestResource>(asset_b);
+	resource_manager.get<TestResource>(asset_b);
 	EXPECT_EQ(TestResource::instances, 2);
-}
 
-TEST_F(ResourceManagerTest, PerAsset) {
-	resource_manager.cache(ASSET_LOCATION);
+	resource_manager.clear();
 	EXPECT_EQ(TestResource::instances, 1);
 
-	TestResource & res_1 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_2 = resource_manager.get<TestResource>(a); // 1
-	TestResource & res_3 = resource_manager.get<TestResource>(b); // 1
-	TestResource & res_4 = resource_manager.get<TestResource>(b); // 1
-
-	EXPECT_EQ(res_1, res_2);
-	EXPECT_EQ(res_2, res_3);
-	EXPECT_EQ(res_3, res_4);
-
-	EXPECT_EQ(TestResource::instances, 1);
+	resource_manager.clear_all();
+	EXPECT_EQ(TestResource::instances, 0);
 }
 
-- 
cgit v1.2.3