From 07adbf48e0781cd8c95983c1871a84b6160ee5bf Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 13:57:13 +0100 Subject: implement asset + more WIP audio system --- src/test/AssetTest.cpp | 33 +++++++++++++++++++++++++++++++++ src/test/AudioTest.cpp | 4 ++-- src/test/CMakeLists.txt | 1 + src/test/ParticleTest.cpp | 2 +- 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 src/test/AssetTest.cpp (limited to 'src/test') diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp new file mode 100644 index 0000000..c3ff158 --- /dev/null +++ b/src/test/AssetTest.cpp @@ -0,0 +1,33 @@ +#include "api/Config.h" +#include + +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class AssetTest : public Test { +public: + Config & cfg = Config::get_instance(); + void SetUp() override { + this->cfg.asset.root_pattern = ".crepe-root"; + } +}; + +TEST_F(AssetTest, Existant) { + ASSERT_NO_THROW(Asset{"asset/texture/img.png"}); +} + +TEST_F(AssetTest, Nonexistant) { + ASSERT_ANY_THROW(Asset{"asset/nonexistant"}); +} + +TEST_F(AssetTest, Rootless) { + cfg.asset.root_pattern.clear(); + + string arbitrary = "\\/this is / /../passed through as-is"; + Asset asset{arbitrary}; + ASSERT_EQ(arbitrary, asset.get_path()); +} + diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 6e2706c..e181de9 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,9 +1,9 @@ -#include "system/AudioSystem.h" #include #include #include #include +#include using namespace std; using namespace crepe; @@ -17,7 +17,7 @@ public: void SetUp() override { auto & mgr = this->component_manager; GameObject entity = mgr.new_object("name"); - entity.add_component("../mwe/audio/sfx1.wav"); + entity.add_component("mwe/audio/sfx1.wav"); } }; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 1a986bd..5ea90f7 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -4,5 +4,6 @@ target_sources(test_main PUBLIC ScriptTest.cpp ParticleTest.cpp AudioTest.cpp + AssetTest.cpp ) diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 1a89e3a..cd2ec2a 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -29,7 +29,7 @@ public: Color color(0, 0, 0, 0); Sprite test_sprite = game_object.add_component( - make_shared("../asset/texture/img.png"), color, + make_shared("asset/texture/img.png"), color, FlipSettings{true, true}); game_object.add_component(ParticleEmitter::Data{ -- cgit v1.2.3 From 431b0bd7c6c502b42bb5be5488371d8c475e7024 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 14:06:18 +0100 Subject: move some shit around --- src/crepe/Asset.cpp | 50 ------------------------------ src/crepe/Asset.h | 43 -------------------------- src/crepe/CMakeLists.txt | 2 -- src/crepe/api/Asset.cpp | 50 ++++++++++++++++++++++++++++++ src/crepe/api/Asset.h | 43 ++++++++++++++++++++++++++ src/crepe/api/AssetManager.cpp | 17 ---------- src/crepe/api/AssetManager.h | 65 --------------------------------------- src/crepe/api/AssetManager.hpp | 24 --------------- src/crepe/api/AudioSource.h | 5 ++- src/crepe/api/CMakeLists.txt | 8 +++-- src/crepe/api/ResourceManager.cpp | 17 ++++++++++ src/crepe/api/ResourceManager.h | 65 +++++++++++++++++++++++++++++++++++++++ src/crepe/api/ResourceManager.hpp | 24 +++++++++++++++ src/crepe/facade/Sound.cpp | 2 +- src/test/AssetTest.cpp | 2 +- 15 files changed, 208 insertions(+), 209 deletions(-) delete mode 100644 src/crepe/Asset.cpp delete mode 100644 src/crepe/Asset.h create mode 100644 src/crepe/api/Asset.cpp create mode 100644 src/crepe/api/Asset.h delete mode 100644 src/crepe/api/AssetManager.cpp delete mode 100644 src/crepe/api/AssetManager.h delete mode 100644 src/crepe/api/AssetManager.hpp create mode 100644 src/crepe/api/ResourceManager.cpp create mode 100644 src/crepe/api/ResourceManager.h create mode 100644 src/crepe/api/ResourceManager.hpp (limited to 'src/test') diff --git a/src/crepe/Asset.cpp b/src/crepe/Asset.cpp deleted file mode 100644 index 8692c6c..0000000 --- a/src/crepe/Asset.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include - -#include "Asset.h" -#include "api/Config.h" - -using namespace crepe; -using namespace std; - -Asset::Asset(const string & src) : src(find_asset(src)) { } -Asset::Asset(const char * src) : src(find_asset(src)) { } - -const string & Asset::get_path() const noexcept { return this->src; } - -string Asset::find_asset(const string & src) const { - auto & cfg = Config::get_instance(); - auto & root_pattern = cfg.asset.root_pattern; - - // if root_pattern is empty, find_asset must return all paths as-is - if (root_pattern.empty()) return src; - - // absolute paths do not need to be resolved, only canonicalized - filesystem::path path = src; - if (path.is_absolute()) - return filesystem::canonical(path); - - // find directory matching root_pattern - filesystem::path root = this->whereami(); - while (1) { - if (filesystem::exists(root / root_pattern)) - break; - if (!root.has_parent_path()) - throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); - root = root.parent_path(); - } - - // join path to root (base directory) and canonicalize - return filesystem::canonical(root / path); -} - -string Asset::whereami() const noexcept { - string path; - size_t path_length = wai_getExecutablePath(NULL, 0, NULL); - path.resize(path_length + 1); // wai writes null byte - wai_getExecutablePath(path.data(), path_length, NULL); - path.resize(path_length); - return path; -} - diff --git a/src/crepe/Asset.h b/src/crepe/Asset.h deleted file mode 100644 index f6e6782..0000000 --- a/src/crepe/Asset.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -namespace crepe { - -/** - * \brief Asset location helper - * - * This class is used to locate game asset files, and should *always* be used - * instead of reading file paths directly. - */ -class Asset { -public: - /** - * \param src Unique identifier to asset - */ - Asset(const std::string & src); - /** - * \param src Unique identifier to asset - */ - Asset(const char * src); - -public: - /** - * \brief Get the path to this asset - * \return path to this asset - */ - const std::string & get_path() const noexcept; - -private: - //! path to asset - const std::string src; - -private: - std::string find_asset(const std::string & src) const; - /** - * \returns The path to the current executable - */ - std::string whereami() const noexcept; -}; - -} // namespace crepe diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 52a781e..05f86d7 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -1,5 +1,4 @@ target_sources(crepe PUBLIC - Asset.cpp Particle.cpp ComponentManager.cpp Component.cpp @@ -8,7 +7,6 @@ target_sources(crepe PUBLIC ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES - Asset.h ComponentManager.h ComponentManager.hpp Component.h diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..8692c6c --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + +#include "Asset.h" +#include "api/Config.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) { } +Asset::Asset(const char * src) : src(find_asset(src)) { } + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { + auto & cfg = Config::get_instance(); + auto & root_pattern = cfg.asset.root_pattern; + + // if root_pattern is empty, find_asset must return all paths as-is + if (root_pattern.empty()) return src; + + // absolute paths do not need to be resolved, only canonicalized + filesystem::path path = src; + if (path.is_absolute()) + return filesystem::canonical(path); + + // find directory matching root_pattern + filesystem::path root = this->whereami(); + while (1) { + if (filesystem::exists(root / root_pattern)) + break; + if (!root.has_parent_path()) + throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); + root = root.parent_path(); + } + + // join path to root (base directory) and canonicalize + return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { + string path; + size_t path_length = wai_getExecutablePath(NULL, 0, NULL); + path.resize(path_length + 1); // wai writes null byte + wai_getExecutablePath(path.data(), path_length, NULL); + path.resize(path_length); + return path; +} + diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..f6e6782 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: + /** + * \param src Unique identifier to asset + */ + Asset(const std::string & src); + /** + * \param src Unique identifier to asset + */ + Asset(const char * src); + +public: + /** + * \brief Get the path to this asset + * \return path to this asset + */ + const std::string & get_path() const noexcept; + +private: + //! path to asset + const std::string src; + +private: + std::string find_asset(const std::string & src) const; + /** + * \returns The path to the current executable + */ + std::string whereami() const noexcept; +}; + +} // namespace crepe diff --git a/src/crepe/api/AssetManager.cpp b/src/crepe/api/AssetManager.cpp deleted file mode 100644 index 3925758..0000000 --- a/src/crepe/api/AssetManager.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "util/Log.h" - -#include "AssetManager.h" - -using namespace crepe; - -AssetManager & AssetManager::get_instance() { - static AssetManager instance; - return instance; -} - -AssetManager::~AssetManager() { - dbg_trace(); - this->asset_cache.clear(); -} - -AssetManager::AssetManager() { dbg_trace(); } diff --git a/src/crepe/api/AssetManager.h b/src/crepe/api/AssetManager.h deleted file mode 100644 index 86a9902..0000000 --- a/src/crepe/api/AssetManager.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace crepe { - -/** - * \brief The AssetManager is responsible for storing and managing assets over - * multiple scenes. - * - * The AssetManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the AssetManager is - * destroyed, at which point the cached assets are cleared. - */ -class AssetManager { - -private: - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map asset_cache; - -private: - AssetManager(); - virtual ~AssetManager(); - -public: - AssetManager(const AssetManager &) = delete; - AssetManager(AssetManager &&) = delete; - AssetManager & operator=(const AssetManager &) = delete; - AssetManager & operator=(AssetManager &&) = delete; - - /** - * \brief Retrieves the singleton instance of the AssetManager. - * - * \return A reference to the single instance of the AssetManager. - */ - static AssetManager & get_instance(); - -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 shared pointer to the cached asset. - * - * This template function caches the asset at the given file path. If the - * asset is already cached and `reload` is false, the existing cached version - * will be returned. Otherwise, the asset will be reloaded and added to the - * cache. - */ - template - std::shared_ptr cache(const std::string & file_path, - bool reload = false); -}; - -} // namespace crepe - -#include "AssetManager.hpp" diff --git a/src/crepe/api/AssetManager.hpp b/src/crepe/api/AssetManager.hpp deleted file mode 100644 index 977b4e1..0000000 --- a/src/crepe/api/AssetManager.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "AssetManager.h" - -namespace crepe { - -template -std::shared_ptr AssetManager::cache(const std::string & file_path, - bool reload) { - auto it = asset_cache.find(file_path); - - if (!reload && it != asset_cache.end()) { - return std::any_cast>(it->second); - } - - std::shared_ptr new_asset - = std::make_shared(file_path.c_str()); - - asset_cache[file_path] = new_asset; - - return new_asset; -} - -} // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 0748267..8a78927 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -1,11 +1,10 @@ #pragma once -#include - -#include "../Asset.h" #include "../Component.h" #include "../types.h" +#include "Asset.h" + namespace crepe { //! Audio source component diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 93a1fac..70f1527 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -8,7 +8,7 @@ target_sources(crepe PUBLIC Transform.cpp Color.cpp Texture.cpp - AssetManager.cpp + ResourceManager.cpp Sprite.cpp SaveManager.cpp Config.cpp @@ -20,6 +20,7 @@ target_sources(crepe PUBLIC Animator.cpp LoopManager.cpp LoopTimer.cpp + Asset.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -35,8 +36,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.h Color.h Texture.h - AssetManager.h - AssetManager.hpp + ResourceManager.h + ResourceManager.hpp SaveManager.h Scene.h Metadata.h @@ -46,4 +47,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Animator.h LoopManager.h LoopTimer.h + Asset.h ) diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp new file mode 100644 index 0000000..470e511 --- /dev/null +++ b/src/crepe/api/ResourceManager.cpp @@ -0,0 +1,17 @@ +#include "util/Log.h" + +#include "ResourceManager.h" + +using namespace crepe; + +ResourceManager & ResourceManager::get_instance() { + static ResourceManager instance; + return instance; +} + +ResourceManager::~ResourceManager() { + dbg_trace(); + this->asset_cache.clear(); +} + +ResourceManager::ResourceManager() { dbg_trace(); } diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h new file mode 100644 index 0000000..7a45493 --- /dev/null +++ b/src/crepe/api/ResourceManager.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include + +namespace crepe { + +/** + * \brief The ResourceManager is responsible for storing and managing assets over + * multiple scenes. + * + * The ResourceManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the ResourceManager is + * destroyed, at which point the cached assets are cleared. + */ +class ResourceManager { + +private: + //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + std::unordered_map asset_cache; + +private: + ResourceManager(); + virtual ~ResourceManager(); + +public: + ResourceManager(const ResourceManager &) = delete; + ResourceManager(ResourceManager &&) = delete; + ResourceManager & operator=(const ResourceManager &) = delete; + ResourceManager & operator=(ResourceManager &&) = delete; + + /** + * \brief Retrieves the singleton instance of the ResourceManager. + * + * \return A reference to the single instance of the ResourceManager. + */ + static ResourceManager & get_instance(); + +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 shared pointer to the cached asset. + * + * This template function caches the asset at the given file path. If the + * asset is already cached and `reload` is false, the existing cached version + * will be returned. Otherwise, the asset will be reloaded and added to the + * cache. + */ + template + std::shared_ptr cache(const std::string & file_path, + bool reload = false); +}; + +} // namespace crepe + +#include "ResourceManager.hpp" diff --git a/src/crepe/api/ResourceManager.hpp b/src/crepe/api/ResourceManager.hpp new file mode 100644 index 0000000..9cd4bcb --- /dev/null +++ b/src/crepe/api/ResourceManager.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "ResourceManager.h" + +namespace crepe { + +template +std::shared_ptr ResourceManager::cache(const std::string & file_path, + bool reload) { + auto it = asset_cache.find(file_path); + + if (!reload && it != asset_cache.end()) { + return std::any_cast>(it->second); + } + + std::shared_ptr new_asset + = std::make_shared(file_path.c_str()); + + asset_cache[file_path] = new_asset; + + return new_asset; +} + +} // namespace crepe diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index b7bfeab..726f11f 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,6 +1,6 @@ #include -#include "../Asset.h" +#include "../api/Asset.h" #include "../util/Log.h" #include "Sound.h" diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp index c3ff158..324a3f1 100644 --- a/src/test/AssetTest.cpp +++ b/src/test/AssetTest.cpp @@ -1,7 +1,7 @@ #include "api/Config.h" #include -#include +#include using namespace std; using namespace crepe; -- cgit v1.2.3 From ab0b4923c4f49e7a28f6d17e994d3e013ca344bb Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 18:04:03 +0100 Subject: more WIP audio system + utilities --- src/crepe/CMakeLists.txt | 2 ++ src/crepe/Resource.cpp | 6 ++++ src/crepe/Resource.h | 12 ++----- src/crepe/api/ResourceManager.cpp | 13 ++++++++ src/crepe/api/ResourceManager.h | 13 ++++---- src/crepe/facade/Sound.cpp | 22 ++++++------- src/crepe/facade/Sound.h | 7 ++-- src/crepe/util/CMakeLists.txt | 2 ++ src/crepe/util/OptionalRef.h | 41 ++++++++++++++++++++++++ src/crepe/util/OptionalRef.hpp | 67 +++++++++++++++++++++++++++++++++++++++ src/test/AssetTest.cpp | 2 +- src/test/CMakeLists.txt | 2 ++ src/test/OptionalRefTest.cpp | 16 ++++++++++ src/test/ResourceManagerTest.cpp | 21 ++++++++++++ 14 files changed, 194 insertions(+), 32 deletions(-) create mode 100644 src/crepe/Resource.cpp create mode 100644 src/crepe/util/OptionalRef.h create mode 100644 src/crepe/util/OptionalRef.hpp create mode 100644 src/test/OptionalRefTest.cpp create mode 100644 src/test/ResourceManagerTest.cpp (limited to 'src/test') diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index 05f86d7..d72d4a0 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources(crepe PUBLIC Component.cpp Collider.cpp Exception.cpp + Resource.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -15,6 +16,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES ValueBroker.hpp Exception.h Exception.hpp + Resource.h ) add_subdirectory(api) diff --git a/src/crepe/Resource.cpp b/src/crepe/Resource.cpp new file mode 100644 index 0000000..e254695 --- /dev/null +++ b/src/crepe/Resource.cpp @@ -0,0 +1,6 @@ +#include "Resource.h" + +using namespace crepe; + +Resource::Resource(const Asset & asset) { } + diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h index dcf3dbd..95b4d06 100644 --- a/src/crepe/Resource.h +++ b/src/crepe/Resource.h @@ -12,16 +12,10 @@ class Asset; * resource (e.g. textures, sounds). */ class Resource { +public: + Resource(const Asset & src); + private: - /** - * \brief Prototype pattern clone function. - * - * \param src Source file of new resource (abstraction for file saved on - * disk) - * - * \returns New instance of concrete resource - */ - virtual std::unique_ptr clone(const Asset & src) const = 0; /** * The resource manager uses \c clone to create new instances of the concrete * resource class. This may be used to inherit references to classes that diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp index 17fbd9b..6eb4afd 100644 --- a/src/crepe/api/ResourceManager.cpp +++ b/src/crepe/api/ResourceManager.cpp @@ -2,6 +2,9 @@ #include "ResourceManager.h" +// default resource cache functions +#include "../facade/Sound.h" + using namespace crepe; ResourceManager & ResourceManager::get_instance() { @@ -11,3 +14,13 @@ ResourceManager & ResourceManager::get_instance() { ResourceManager::~ResourceManager() { dbg_trace(); } ResourceManager::ResourceManager() { dbg_trace(); } + +void ResourceManager::clear() { + this->resources.clear(); +} + +template <> +Sound & ResourceManager::cache(const Asset & asset) { + return this->cache(asset); +} + diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h index a69a9fa..468af16 100644 --- a/src/crepe/api/ResourceManager.h +++ b/src/crepe/api/ResourceManager.h @@ -51,22 +51,23 @@ public: * it is already cached. * \tparam T The type of asset to cache (e.g., texture, sound, etc.). * - * \return A shared pointer to the cached asset. + * \return A reference to the resource * * This template function caches the asset at the given file path. If the - * asset is already cached and `reload` is false, the existing cached version - * will be returned. Otherwise, the asset will be reloaded and added to the + * asset is already cached, the existing instance will be returned. + * Otherwise, the concrete resource will be instantiated and added to the * cache. */ template T & cache(const Asset & asset); - /** - * \brief Clear the resource cache - */ + //! Clear the resource cache void clear(); }; +template <> +Sound & ResourceManager::cache(const Asset & asset); + } // namespace crepe #include "ResourceManager.hpp" diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 726f11f..4eefcda 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,5 +1,3 @@ -#include - #include "../api/Asset.h" #include "../util/Log.h" @@ -9,16 +7,13 @@ using namespace crepe; using namespace std; -Sound::Sound(SoundContext & ctx) : context(ctx) { dbg_trace(); } - -unique_ptr Sound::clone(const Asset & src) const { - auto instance = make_unique(*this); - instance->sample.load(src.get_path().c_str()); - return instance; +Sound::Sound(const Asset & src) : Resource(src) { + this->sample.load(src.get_path().c_str()); + dbg_trace(); } void Sound::play() { - SoundContext & ctx = this->context; + SoundContext & ctx = this->context.get(); if (ctx.engine.getPause(this->handle)) { // resume if paused ctx.engine.setPause(this->handle, false); @@ -30,13 +25,13 @@ void Sound::play() { } void Sound::pause() { - SoundContext & ctx = this->context; + SoundContext & ctx = this->context.get(); if (ctx.engine.getPause(this->handle)) return; ctx.engine.setPause(this->handle, true); } void Sound::rewind() { - SoundContext & ctx = this->context; + SoundContext & ctx = this->context.get(); if (!ctx.engine.isValidVoiceHandle(this->handle)) return; ctx.engine.seek(this->handle, 0); } @@ -44,7 +39,7 @@ void Sound::rewind() { void Sound::set_volume(float volume) { this->volume = volume; - SoundContext & ctx = this->context; + SoundContext & ctx = this->context.get(); if (!ctx.engine.isValidVoiceHandle(this->handle)) return; ctx.engine.setVolume(this->handle, this->volume); } @@ -52,7 +47,8 @@ void Sound::set_volume(float volume) { void Sound::set_looping(bool looping) { this->looping = looping; - SoundContext & ctx = this->context; + SoundContext & ctx = this->context.get(); if (!ctx.engine.isValidVoiceHandle(this->handle)) return; ctx.engine.setLooping(this->handle, this->looping); } + diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 8342b46..6f8462a 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -4,6 +4,7 @@ #include #include +#include "../util/OptionalRef.h" #include "../Resource.h" namespace crepe { @@ -18,6 +19,7 @@ class SoundContext; */ class Sound : public Resource { public: + Sound(const Asset & src); /** * \brief Pause this sample * @@ -72,13 +74,12 @@ public: bool get_looping() const { return this->looping; } public: - Sound(SoundContext & ctx); - std::unique_ptr clone(const Asset & src) const override; + void set_context(SoundContext & ctx); private: SoLoud::Wav sample; SoLoud::handle handle; - SoundContext & context; + OptionalRef context; float volume = 1.0f; bool looping = false; diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 4be738a..94ed906 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -9,5 +9,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Log.hpp Proxy.h Proxy.hpp + OptionalRef.h + OptionalRef.hpp ) diff --git a/src/crepe/util/OptionalRef.h b/src/crepe/util/OptionalRef.h new file mode 100644 index 0000000..1ad3a6d --- /dev/null +++ b/src/crepe/util/OptionalRef.h @@ -0,0 +1,41 @@ +#pragma once + +namespace crepe { + +/** + * \brief Optional reference utility + * + * This class doesn't need to know the full definition of \c T to be used. + * + * \tparam T Value type + */ +template +class OptionalRef { +public: + OptionalRef() = default; + OptionalRef(T &); + OptionalRef & operator=(T &); + explicit operator bool() const noexcept; + + void set(T &) noexcept; + T & get() const; + void clear() noexcept; + + OptionalRef(const OptionalRef &); + OptionalRef(OptionalRef &&); + OptionalRef & operator=(const OptionalRef &); + OptionalRef & operator=(OptionalRef &&); + +private: + /** + * \brief Reference to the value of type \c T + * + * \note This raw pointer is *not* managed, and only used as a reference! + */ + T * ref = nullptr; +}; + +} + +#include "OptionalRef.hpp" + diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp new file mode 100644 index 0000000..e603a25 --- /dev/null +++ b/src/crepe/util/OptionalRef.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include "OptionalRef.h" + +namespace crepe { + +template +OptionalRef::OptionalRef(T & ref) { + this->set(ref); +} + +template +OptionalRef::OptionalRef(const OptionalRef & other) { + this->ref = other.ref; +} + +template +OptionalRef::OptionalRef(OptionalRef && other) { + this->ref = other.ref; + other.clear(); +} + +template +OptionalRef & OptionalRef::operator=(const OptionalRef & other) { + this->ref = other.ref; + return *this; +} + +template +OptionalRef & OptionalRef::operator=(OptionalRef && other) { + this->ref = other.ref; + other.clear(); + return *this; +} + +template +T & OptionalRef::get() const { + if (this->ref == nullptr) + throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); + return *this->ref; +} + +template +void OptionalRef::set(T & ref) noexcept { + this->ref = &ref; +} + +template +void OptionalRef::clear() noexcept { + this->ref = nullptr; +} + +template +OptionalRef & OptionalRef::operator=(T & ref) { + this->set(ref); + return *this; +} + +template +OptionalRef::operator bool() const noexcept { + return this->ref == nullptr; +} + +} + diff --git a/src/test/AssetTest.cpp b/src/test/AssetTest.cpp index 324a3f1..563a253 100644 --- a/src/test/AssetTest.cpp +++ b/src/test/AssetTest.cpp @@ -1,7 +1,7 @@ -#include "api/Config.h" #include #include +#include using namespace std; using namespace crepe; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 5ea90f7..437c296 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -5,5 +5,7 @@ target_sources(test_main PUBLIC ParticleTest.cpp AudioTest.cpp AssetTest.cpp + ResourceManagerTest.cpp + OptionalRefTest.cpp ) diff --git a/src/test/OptionalRefTest.cpp b/src/test/OptionalRefTest.cpp new file mode 100644 index 0000000..65bd816 --- /dev/null +++ b/src/test/OptionalRefTest.cpp @@ -0,0 +1,16 @@ +#include + +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +TEST(OptionalRefTest, Explicit) { + string value = "foo"; + OptionalRef ref; + + EXPECT_FALSE(bool(ref)); + ASSERT_THROW(ref.get(), runtime_error); +} + diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp new file mode 100644 index 0000000..42b6b5d --- /dev/null +++ b/src/test/ResourceManagerTest.cpp @@ -0,0 +1,21 @@ +#include + +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class ResourceManagerTest : public Test { +public: + ResourceManager & manager = ResourceManager::get_instance(); + + void SetUp() override { + this->manager.clear(); + } +}; + +TEST_F(ResourceManagerTest, Main) { + Sound & sound = this->manager.cache("mwe/audio/sfx1.wav"); +} + -- cgit v1.2.3 From add8724446fdeae1aaec9b07544cf7a5475a9bfe Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 19:57:45 +0100 Subject: ResourceManager working + tested --- src/crepe/Resource.h | 3 +-- src/crepe/api/Asset.cpp | 4 ++++ src/crepe/api/Asset.h | 7 +++++++ src/crepe/api/CMakeLists.txt | 1 - src/crepe/api/ResourceManager.cpp | 27 +++++++++++++++++++++------ src/crepe/api/ResourceManager.h | 4 ---- src/crepe/api/ResourceManager.hpp | 27 --------------------------- src/crepe/facade/Sound.cpp | 5 +++++ src/crepe/facade/Sound.h | 2 +- src/crepe/util/OptionalRef.hpp | 2 +- src/test/OptionalRefTest.cpp | 24 +++++++++++++++++++++++- src/test/ResourceManagerTest.cpp | 36 +++++++++++++++++++++++++++++++++--- src/test/main.cpp | 15 +++++++++++++-- 13 files changed, 109 insertions(+), 48 deletions(-) delete mode 100644 src/crepe/api/ResourceManager.hpp (limited to 'src/test') diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h index 95b4d06..a0c8859 100644 --- a/src/crepe/Resource.h +++ b/src/crepe/Resource.h @@ -1,7 +1,5 @@ #pragma once -#include - namespace crepe { class ResourceManager; @@ -14,6 +12,7 @@ class Asset; class Resource { public: Resource(const Asset & src); + virtual ~Resource() = default; private: /** diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp index 1887814..5271cf7 100644 --- a/src/crepe/api/Asset.cpp +++ b/src/crepe/api/Asset.cpp @@ -48,6 +48,10 @@ string Asset::whereami() const noexcept { return path; } +bool Asset::operator==(const Asset & other) const noexcept { + return this->src == other.src; +} + size_t std::hash::operator()(const Asset & asset) const noexcept { return std::hash{}(asset.get_path()); }; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h index 0f6b0b3..05dccba 100644 --- a/src/crepe/api/Asset.h +++ b/src/crepe/api/Asset.h @@ -29,6 +29,13 @@ public: */ const std::string & get_path() const noexcept; + /** + * \brief Comparison operator + * \param other Possibly different instance of \c Asset to test equality against + * \return True if \c this and \c other are equal + */ + bool operator == (const Asset & other) const noexcept; + private: //! path to asset const std::string src; diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 70f1527..b452f37 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -37,7 +37,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Color.h Texture.h ResourceManager.h - ResourceManager.hpp SaveManager.h Scene.h Metadata.h diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp index 6eb4afd..7877ed9 100644 --- a/src/crepe/api/ResourceManager.cpp +++ b/src/crepe/api/ResourceManager.cpp @@ -1,11 +1,11 @@ +#include + #include "util/Log.h" #include "ResourceManager.h" -// default resource cache functions -#include "../facade/Sound.h" - using namespace crepe; +using namespace std; ResourceManager & ResourceManager::get_instance() { static ResourceManager instance; @@ -19,8 +19,23 @@ void ResourceManager::clear() { this->resources.clear(); } -template <> -Sound & ResourceManager::cache(const Asset & asset) { - return this->cache(asset); +template +T & ResourceManager::cache(const Asset & asset) { + dbg_trace(); + static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); + + if (!this->resources.contains(asset)) + this->resources[asset] = make_unique(asset); + + Resource * resource = this->resources.at(asset).get(); + T * concrete_resource = dynamic_cast(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 &); + diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h index 468af16..efdd5c5 100644 --- a/src/crepe/api/ResourceManager.h +++ b/src/crepe/api/ResourceManager.h @@ -65,9 +65,5 @@ public: void clear(); }; -template <> -Sound & ResourceManager::cache(const Asset & asset); - } // namespace crepe -#include "ResourceManager.hpp" diff --git a/src/crepe/api/ResourceManager.hpp b/src/crepe/api/ResourceManager.hpp deleted file mode 100644 index 62cac20..0000000 --- a/src/crepe/api/ResourceManager.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -#include "ResourceManager.h" - -namespace crepe { - -template -T & ResourceManager::cache(const Asset & asset) { - using namespace std; - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); - - if (!this->resources.contains(asset)) - this->resources[asset] = make_unique(asset); - - Resource * resource = this->resources.at(asset).get(); - T * concrete_resource = dynamic_cast(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; -} - -} // namespace crepe diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 4eefcda..b589759 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -11,6 +11,7 @@ Sound::Sound(const Asset & src) : Resource(src) { this->sample.load(src.get_path().c_str()); dbg_trace(); } +Sound::~Sound() { dbg_trace(); } void Sound::play() { SoundContext & ctx = this->context.get(); @@ -52,3 +53,7 @@ void Sound::set_looping(bool looping) { ctx.engine.setLooping(this->handle, this->looping); } +void Sound::set_context(SoundContext & ctx) { + this->context = ctx; +} + diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 6f8462a..94b1996 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -20,6 +19,7 @@ class SoundContext; class Sound : public Resource { public: Sound(const Asset & src); + ~Sound(); // dbg_trace /** * \brief Pause this sample * diff --git a/src/crepe/util/OptionalRef.hpp b/src/crepe/util/OptionalRef.hpp index e603a25..7b201b0 100644 --- a/src/crepe/util/OptionalRef.hpp +++ b/src/crepe/util/OptionalRef.hpp @@ -60,7 +60,7 @@ OptionalRef & OptionalRef::operator=(T & ref) { template OptionalRef::operator bool() const noexcept { - return this->ref == nullptr; + return this->ref != nullptr; } } diff --git a/src/test/OptionalRefTest.cpp b/src/test/OptionalRefTest.cpp index 65bd816..219ccca 100644 --- a/src/test/OptionalRefTest.cpp +++ b/src/test/OptionalRefTest.cpp @@ -9,8 +9,30 @@ using namespace testing; TEST(OptionalRefTest, Explicit) { string value = "foo"; OptionalRef ref; + EXPECT_FALSE(ref); + ASSERT_THROW(ref.get(), runtime_error); + + ref.set(value); + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); + + ref.clear(); + EXPECT_FALSE(ref); + ASSERT_THROW(ref.get(), runtime_error); +} + +TEST(OptionalRefTest, Implicit) { + string value = "foo"; + OptionalRef ref = value; + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); - EXPECT_FALSE(bool(ref)); + ref.clear(); + EXPECT_FALSE(ref); ASSERT_THROW(ref.get(), runtime_error); + + ref = value; + EXPECT_TRUE(ref); + ASSERT_NO_THROW(ref.get()); } diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 42b6b5d..5d1ae7a 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include using namespace std; @@ -8,14 +10,42 @@ using namespace testing; class ResourceManagerTest : public Test { public: - ResourceManager & manager = ResourceManager::get_instance(); + ResourceManager & resman = ResourceManager::get_instance(); + Config & cfg = Config::get_instance(); void SetUp() override { - this->manager.clear(); + cfg.log.level = Log::Level::TRACE; + resman.clear(); } }; TEST_F(ResourceManagerTest, Main) { - Sound & sound = this->manager.cache("mwe/audio/sfx1.wav"); + Asset path1 = "mwe/audio/sfx1.wav"; + Asset path2 = "mwe/audio/sfx1.wav"; + ASSERT_EQ(path1, path2); + + Sound * ptr1 = nullptr; + Sound * ptr2 = nullptr; + { + Log::logf(Log::Level::DEBUG, "Get first sound (constructor call)"); + Sound & sound = resman.cache(path1); + ptr1 = &sound; + } + { + Log::logf(Log::Level::DEBUG, "Get same sound (NO constructor call)"); + Sound & sound = resman.cache(path2); + ptr2 = &sound; + } + EXPECT_EQ(ptr1, ptr2); + + Log::logf(Log::Level::DEBUG, "Clear cache (destructor call)"); + resman.clear(); + + Log::logf(Log::Level::DEBUG, "Get first sound again (constructor call)"); + Sound & sound = resman.cache(path1); + + // NOTE: there is no way (that I know of) to ensure the above statement + // allocates the new Sound instance in a different location than the first, + // so this test was verified using the above print statements. } diff --git a/src/test/main.cpp b/src/test/main.cpp index 241015d..19a8d6e 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -5,11 +5,22 @@ using namespace crepe; using namespace testing; +class GlobalConfigReset : public EmptyTestEventListener { +public: + Config & cfg = Config::get_instance(); + + // This function is called before each test + void OnTestStart(const TestInfo &) override { + cfg.log.level = Log::Level::WARNING; + } +}; + int main(int argc, char ** argv) { InitGoogleTest(&argc, argv); - auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::ERROR; + UnitTest & ut = *UnitTest::GetInstance(); + ut.listeners().Append(new GlobalConfigReset); return RUN_ALL_TESTS(); } + -- cgit v1.2.3 From d258fcc8efdb6a968a220c4590a204292a16ad42 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 14 Nov 2024 20:27:05 +0100 Subject: added thoughts --- src/crepe/Component.h | 12 +++++++++--- src/crepe/api/AudioSource.h | 11 ++++++++++- src/crepe/system/AudioSystem.cpp | 37 ++++++++++++++++++++++++++++++++++++- src/crepe/system/AudioSystem.h | 2 ++ src/test/ResourceManagerTest.cpp | 11 ++++++----- 5 files changed, 63 insertions(+), 10 deletions(-) (limited to 'src/test') diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 12c10cb..7734335 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -16,7 +16,12 @@ class Component { public: //! Whether the component is active bool active = true; - //! The id of the GameObject this component belongs to + /** + * \brief The id of the GameObject this component belongs to + * + * \note Only systems are supposed to use this member, but since friend + * relations aren't inherited this needs to be public. + */ const game_object_id_t game_object_id; protected: @@ -24,10 +29,9 @@ protected: * \param id The id of the GameObject this component belongs to */ Component(game_object_id_t id); - //! Only the ComponentManager can create components + //! Only ComponentManager can create components friend class ComponentManager; -public: /** * \brief Get the maximum number of instances for this component * @@ -38,6 +42,8 @@ public: * \return The maximum number of instances for this component */ virtual int get_instances_max() const { return -1; } + //! Only ComponentManager needs to know the max instance count + friend class ComponentManager; }; } // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 8a78927..1264790 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -7,10 +7,19 @@ namespace crepe { +class AudioSystem; + //! Audio source component class AudioSource : public Component { -public: + //! AudioSource components are handled by AudioSystem + friend class AudioSystem; + +protected: AudioSource(game_object_id_t id, const Asset & source); + //! Only ComponentManager can create components + friend class ComponentManager; +public: + // But std::unique_ptr needs to be able to destoy this component again virtual ~AudioSource() = default; public: diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 67967ef..c8dae9d 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -14,7 +14,42 @@ void AudioSystem::update() { AudioSource & component = component_ref.get(); if (!component.active) continue; - // TODO: fetch Sound instance from resourcemanager + /** + * How this is supposed to work: + * - Get an instance of Sound for this resource/component combo (Sound + * instance is supposed to be unique per component, even if they use the + * same underlying asset). + * OR + * - Use the same instance of Sound if this is what the cache returns + * (= what the game programmer's wishes to do). + * + * NOT supposed to happen but still the case: + * - Below function call causes assets to be cached unintentionally + * - Cached assets are deleted at the end of a scene (i think?) + * - I'm not sure if the ResourceManager is even supposed to have a public + * `.clear()` method since the control over resource lifetime is + * explicitly handed over to the game programmer by using ResourceManager + * to cache/uncache. I believe the proper methods are supposed to be: + * + * - get() get a reference to resource (used here) + * - clear() clears NON-cached assets + * - cache() marks asset as "do not delete at end of scene" + * - uncache() undoes the above + * + * I think somewhere in the above function calls a unique identifier for + * the Asset/GameObject should be given to make sure the unique instance + * shit works as intended. The resource manager is also used for things + * other than sounds. + * + * Also need to check: + * - Is it an issue if there are multiple AudioSource components playing + * the same sample (= identical Asset), while they are all triggered + * using the same underlying instance of Sound (esp. w/ + * play/pause/retrigger behavior). + */ + Sound & sound = this->resman.cache(component.source); + sound.set_context(this->context); + // TODO: lots of state diffing } } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index e037f51..d0b4f9a 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,6 +1,7 @@ #pragma once #include "../facade/SoundContext.h" +#include "../api/ResourceManager.h" #include "System.h" @@ -13,6 +14,7 @@ public: private: SoundContext context {}; + ResourceManager & resman = ResourceManager::get_instance(); }; } // namespace crepe diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 5d1ae7a..3fc9ebd 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -14,12 +14,17 @@ public: Config & cfg = Config::get_instance(); void SetUp() override { - cfg.log.level = Log::Level::TRACE; resman.clear(); } }; TEST_F(ResourceManagerTest, Main) { + // NOTE: there is no way (that I know of) to ensure the last cache call + // allocates the new Sound instance in a different location than the first, + // so this test should be verified manually using these print statements and + // debug trace messages. + cfg.log.level = Log::Level::TRACE; + Asset path1 = "mwe/audio/sfx1.wav"; Asset path2 = "mwe/audio/sfx1.wav"; ASSERT_EQ(path1, path2); @@ -43,9 +48,5 @@ TEST_F(ResourceManagerTest, Main) { Log::logf(Log::Level::DEBUG, "Get first sound again (constructor call)"); Sound & sound = resman.cache(path1); - - // NOTE: there is no way (that I know of) to ensure the above statement - // allocates the new Sound instance in a different location than the first, - // so this test was verified using the above print statements. } -- cgit v1.2.3 From a685e5f743786cc6499e7ce8973bb78a83d101f7 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Tue, 26 Nov 2024 20:03:41 +0100 Subject: big WIP --- src/crepe/CMakeLists.txt | 2 + src/crepe/ResourceManager.cpp | 36 ++++++++++++ src/crepe/ResourceManager.h | 76 +++++++++++++++++++++++++ src/crepe/api/AudioSource.h | 1 + src/crepe/api/CMakeLists.txt | 2 - src/crepe/api/Config.h | 27 +-------- src/crepe/api/ResourceManager.cpp | 41 -------------- src/crepe/api/ResourceManager.h | 69 ----------------------- src/crepe/system/AudioSystem.cpp | 2 +- src/crepe/system/AudioSystem.h | 4 +- src/crepe/util/Log.cpp | 2 +- src/crepe/util/Log.h | 16 ++++++ src/test/EventTest.cpp | 1 - src/test/ResourceManagerTest.cpp | 115 +++++++++++++++++++++++++++----------- src/test/main.cpp | 12 ++-- 15 files changed, 224 insertions(+), 182 deletions(-) create mode 100644 src/crepe/ResourceManager.cpp create mode 100644 src/crepe/ResourceManager.h delete mode 100644 src/crepe/api/ResourceManager.cpp delete mode 100644 src/crepe/api/ResourceManager.h (limited to 'src/test') diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt index df15b8f..0313dfa 100644 --- a/src/crepe/CMakeLists.txt +++ b/src/crepe/CMakeLists.txt @@ -3,6 +3,7 @@ target_sources(crepe PUBLIC ComponentManager.cpp Component.cpp Collider.cpp + ResourceManager.cpp Resource.cpp ) @@ -13,6 +14,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Collider.h ValueBroker.h ValueBroker.hpp + ResourceManager.h Resource.h ) diff --git a/src/crepe/ResourceManager.cpp b/src/crepe/ResourceManager.cpp new file mode 100644 index 0000000..111b9e0 --- /dev/null +++ b/src/crepe/ResourceManager.cpp @@ -0,0 +1,36 @@ +#include + +#include "util/Log.h" + +#include "ResourceManager.h" + +using namespace crepe; +using namespace std; + +ResourceManager::~ResourceManager() { dbg_trace(); } +ResourceManager::ResourceManager() { dbg_trace(); } + +void ResourceManager::clear() { + this->resources.clear(); +} + +// template +// T & ResourceManager::cache(const Asset & asset) { +// dbg_trace(); +// static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); +// +// if (!this->resources.contains(asset)) +// this->resources[asset] = make_unique(asset); +// +// Resource * resource = this->resources.at(asset).get(); +// T * concrete_resource = dynamic_cast(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 &); + diff --git a/src/crepe/ResourceManager.h b/src/crepe/ResourceManager.h new file mode 100644 index 0000000..26a86a8 --- /dev/null +++ b/src/crepe/ResourceManager.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#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. + * + * The ResourceManager ensures that assets are loaded once and can be accessed + * across different scenes. It caches assets to avoid reloading them every time + * a scene is loaded. Assets are retained in memory until the ResourceManager is + * destroyed, at which point the cached assets are cleared. + */ +class ResourceManager { +public: + ResourceManager(); // dbg_trace + virtual ~ResourceManager(); // dbg_trace + +private: + template + Resource & get_internal(const Component & component, const Asset & asset); + + template + const Asset & get_source(const Component & component) const; + + //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + std::unordered_map> resources; + +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 + void cache(const Asset & asset, bool persistent = false); + + template + void cache(const Component & component, bool persistent = false); + + // void resman.cache(Asset, Lifetime); + // void resman.cache(Component, Asset, Lifetime); + + template + Resource & get(const Component & component); + + //! Clear the resource cache + void clear(); +}; + +class Sound; +class AudioSource; +template <> +Sound & ResourceManager::get(const AudioSource & component); + +} // namespace crepe + diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 1264790..0950129 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -3,6 +3,7 @@ #include "../Component.h" #include "../types.h" +#include "GameObject.h" #include "Asset.h" namespace crepe { diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index a2e21fa..ad82924 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -7,7 +7,6 @@ target_sources(crepe PUBLIC Transform.cpp Color.cpp Texture.cpp - ResourceManager.cpp Sprite.cpp SaveManager.cpp Config.cpp @@ -39,7 +38,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Vector2.hpp Color.h Texture.h - ResourceManager.h SaveManager.h Scene.h Metadata.h diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 0c9d116..5bd6913 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -11,35 +11,12 @@ namespace crepe { * modified *before* execution is handed over from the game programmer to the engine (i.e. the * main loop is started). */ -class Config final { -public: +struct Config final { //! Retrieve handle to global Config instance static Config & get_instance(); -private: - Config() = default; - ~Config() = default; - Config(const Config &) = default; - Config(Config &&) = default; - Config & operator=(const Config &) = default; - Config & operator=(Config &&) = default; - -public: //! Logging-related settings - struct { - /** - * \brief Log level - * - * Only messages with equal or higher priority than this value will be logged. - */ - Log::Level level = Log::Level::INFO; - /** - * \brief Colored log output - * - * Enables log coloring using ANSI escape codes. - */ - bool color = true; - } log; + Log::Config log; //! Save manager struct { diff --git a/src/crepe/api/ResourceManager.cpp b/src/crepe/api/ResourceManager.cpp deleted file mode 100644 index 7877ed9..0000000 --- a/src/crepe/api/ResourceManager.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include "util/Log.h" - -#include "ResourceManager.h" - -using namespace crepe; -using namespace std; - -ResourceManager & ResourceManager::get_instance() { - static ResourceManager instance; - return instance; -} - -ResourceManager::~ResourceManager() { dbg_trace(); } -ResourceManager::ResourceManager() { dbg_trace(); } - -void ResourceManager::clear() { - this->resources.clear(); -} - -template -T & ResourceManager::cache(const Asset & asset) { - dbg_trace(); - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); - - if (!this->resources.contains(asset)) - this->resources[asset] = make_unique(asset); - - Resource * resource = this->resources.at(asset).get(); - T * concrete_resource = dynamic_cast(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 &); - diff --git a/src/crepe/api/ResourceManager.h b/src/crepe/api/ResourceManager.h deleted file mode 100644 index efdd5c5..0000000 --- a/src/crepe/api/ResourceManager.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -#include "Asset.h" -#include "Resource.h" - -namespace crepe { - -class Sound; - -/** - * \brief The ResourceManager is responsible for storing and managing assets over - * multiple scenes. - * - * The ResourceManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the ResourceManager is - * destroyed, at which point the cached assets are cleared. - */ -class ResourceManager { - -private: - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map> resources; - -private: - ResourceManager(); // dbg_trace - virtual ~ResourceManager(); // dbg_trace - - ResourceManager(const ResourceManager &) = delete; - ResourceManager(ResourceManager &&) = delete; - ResourceManager & operator=(const ResourceManager &) = delete; - ResourceManager & operator=(ResourceManager &&) = delete; - -public: - /** - * \brief Retrieves the singleton instance of the ResourceManager. - * - * \return A reference to the single instance of the ResourceManager. - */ - static ResourceManager & get_instance(); - -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 - T & cache(const Asset & asset); - - //! Clear the resource cache - void clear(); -}; - -} // namespace crepe - diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b7ac1f2..6c30b85 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -47,7 +47,7 @@ void AudioSystem::update() { * using the same underlying instance of Sound (esp. w/ * play/pause/retrigger behavior). */ - Sound & sound = this->resman.cache(component.source); + // Sound & sound = this->resource_manager.get(component); // TODO: lots of state diffing } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index d0b4f9a..d3d5aeb 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,7 +1,7 @@ #pragma once #include "../facade/SoundContext.h" -#include "../api/ResourceManager.h" +#include "../ResourceManager.h" #include "System.h" @@ -14,7 +14,7 @@ public: private: SoundContext context {}; - ResourceManager & resman = ResourceManager::get_instance(); + ResourceManager resource_manager {}; }; } // namespace crepe diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp index 84d80a8..bc86c7e 100644 --- a/src/crepe/util/Log.cpp +++ b/src/crepe/util/Log.cpp @@ -25,7 +25,7 @@ string Log::prefix(const Level & level) { } void Log::log(const Level & level, const string & msg) { - auto & cfg = Config::get_instance(); + auto & cfg = crepe::Config::get_instance(); if (level < cfg.log.level) return; string out = Log::prefix(level) + msg; diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h index fc0bb3a..914145a 100644 --- a/src/crepe/util/Log.h +++ b/src/crepe/util/Log.h @@ -77,6 +77,22 @@ private: * \return Colored message severity prefix string */ static std::string prefix(const Level & level); + +public: + struct Config { + /** + * \brief Log level + * + * Only messages with equal or higher priority than this value will be logged. + */ + Level level = INFO; + /** + * \brief Colored log output + * + * Enables log coloring using ANSI escape codes. + */ + bool color = true; + }; }; } // namespace crepe diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index b0e6c9c..a21a851 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -37,7 +37,6 @@ public: TEST_F(EventManagerTest, EventSubscription) { EventHandler key_handler = [](const KeyPressEvent & e) { - std::cout << "Key Event Triggered" << std::endl; return true; }; diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 3fc9ebd..f57c419 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -1,52 +1,101 @@ #include +#define private public +#define protected public + #include -#include -#include +#include +#include +#include +#include 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(component, component.source); +} + class ResourceManagerTest : public Test { public: - ResourceManager & resman = ResourceManager::get_instance(); - Config & cfg = Config::get_instance(); + ResourceManager resource_manager{}; + + static constexpr const char * ASSET_LOCATION = "asset/texture/img.png"; + + TestComponent a{0, ASSET_LOCATION}; + TestComponent b{1, ASSET_LOCATION}; +private: void SetUp() override { - resman.clear(); + TestResource::instances = 0; } }; -TEST_F(ResourceManagerTest, Main) { - // NOTE: there is no way (that I know of) to ensure the last cache call - // allocates the new Sound instance in a different location than the first, - // so this test should be verified manually using these print statements and - // debug trace messages. - cfg.log.level = Log::Level::TRACE; - - Asset path1 = "mwe/audio/sfx1.wav"; - Asset path2 = "mwe/audio/sfx1.wav"; - ASSERT_EQ(path1, path2); - - Sound * ptr1 = nullptr; - Sound * ptr2 = nullptr; - { - Log::logf(Log::Level::DEBUG, "Get first sound (constructor call)"); - Sound & sound = resman.cache(path1); - ptr1 = &sound; - } - { - Log::logf(Log::Level::DEBUG, "Get same sound (NO constructor call)"); - Sound & sound = resman.cache(path2); - ptr2 = &sound; - } - EXPECT_EQ(ptr1, ptr2); +TEST_F(ResourceManagerTest, Uncached) { + TestResource & res_1 = resource_manager.get(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 2 + TestResource & res_4 = resource_manager.get(b); // 2 + + 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(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 2 + TestResource & res_4 = resource_manager.get(b); // 2 + + ASSERT_EQ(res_1, res_2); + ASSERT_EQ(res_3, res_4); + EXPECT_NE(res_1, res_3); + + EXPECT_EQ(TestResource::instances, 2); +} + +TEST_F(ResourceManagerTest, PerAsset) { + resource_manager.cache(ASSET_LOCATION); + EXPECT_EQ(TestResource::instances, 1); + + TestResource & res_1 = resource_manager.get(a); // 1 + TestResource & res_2 = resource_manager.get(a); // 1 + TestResource & res_3 = resource_manager.get(b); // 1 + TestResource & res_4 = resource_manager.get(b); // 1 - Log::logf(Log::Level::DEBUG, "Clear cache (destructor call)"); - resman.clear(); + EXPECT_EQ(res_1, res_2); + EXPECT_EQ(res_2, res_3); + EXPECT_EQ(res_3, res_4); - Log::logf(Log::Level::DEBUG, "Get first sound again (constructor call)"); - Sound & sound = resman.cache(path1); + EXPECT_EQ(TestResource::instances, 1); } diff --git a/src/test/main.cpp b/src/test/main.cpp index e03a989..54f74fd 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,8 +1,4 @@ #include - -#define protected public -#define private public - #include using namespace crepe; @@ -11,12 +7,14 @@ using namespace testing; class GlobalConfigReset : public EmptyTestEventListener { public: Config & cfg = Config::get_instance(); - Config cfg_default = Config(); // This function is called before each test void OnTestStart(const TestInfo &) override { - cfg = cfg_default; - cfg.log.level = Log::Level::WARNING; + cfg = { + .log = { + .level = Log::Level::WARNING, + }, + }; } }; -- cgit v1.2.3 From 0e45d4835f65ff9127a16adcbe9a9f0a20370cfc Mon Sep 17 00:00:00 2001 From: Loek Le Blansch 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 (limited to 'src/test') 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 - #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 & pair) { + const CacheEntry & entry = pair.second; + return entry.persistent == false; + }); +} + +void ResourceManager::clear_all() { this->resources.clear(); } -// template -// T & ResourceManager::cache(const Asset & asset) { -// dbg_trace(); -// static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); -// -// if (!this->resources.contains(asset)) -// this->resources[asset] = make_unique(asset); -// -// Resource * resource = this->resources.at(asset).get(); -// T * concrete_resource = dynamic_cast(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 - Resource & get_internal(const Component & component, const Asset & asset); - - template - const Asset & get_source(const Component & component) const; - + struct CacheEntry { + std::unique_ptr resource = nullptr; + bool persistent = false; + }; //! A cache that holds all the assets, accessible by their file path, over multiple scenes. - std::unordered_map> resources; + std::unordered_map 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 - void cache(const Asset & asset, bool persistent = false); - - template - void cache(const Component & component, bool persistent = false); - - // void resman.cache(Asset, Lifetime); - // void resman.cache(Component, Asset, Lifetime); + void set_persistent(const Asset & asset, bool persistent); - template - Resource & get(const Component & component); + template + 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 + +#include "ResourceManager.h" + +namespace crepe { + +template +T & ResourceManager::get(const Asset & asset) { + using namespace std; + static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); + + CacheEntry & entry = this->get_entry(asset); + if (entry.resource == nullptr) + entry.resource = make_unique(asset); + + T * concrete_resource = dynamic_cast(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 #include #include -#include -#include 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(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(a); // 1 - TestResource & res_2 = resource_manager.get(a); // 1 - TestResource & res_3 = resource_manager.get(b); // 2 - TestResource & res_4 = resource_manager.get(b); // 2 +TEST_F(ResourceManagerTest, Default) { + TestResource & res_1 = resource_manager.get(asset_a); + TestResource & res_2 = resource_manager.get(asset_a); + TestResource & res_3 = resource_manager.get(asset_b); + TestResource & res_4 = resource_manager.get(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(a); // 1 - TestResource & res_2 = resource_manager.get(a); // 1 - TestResource & res_3 = resource_manager.get(b); // 2 - TestResource & res_4 = resource_manager.get(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(asset_a); + resource_manager.get(asset_a); + resource_manager.get(asset_b); + resource_manager.get(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(a); // 1 - TestResource & res_2 = resource_manager.get(a); // 1 - TestResource & res_3 = resource_manager.get(b); // 1 - TestResource & res_4 = resource_manager.get(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 From c59d460f12e1393e0ddbaaa1c6f5522eb12f8ff9 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 29 Nov 2024 16:23:52 +0100 Subject: more utility classes for Audio system --- src/crepe/api/AudioSource.h | 11 ++----- src/crepe/facade/Sound.h | 4 +++ src/crepe/system/AudioSystem.cpp | 17 +++++++++-- src/crepe/system/AudioSystem.h | 21 +++++++++++++ src/crepe/util/CMakeLists.txt | 3 ++ src/crepe/util/Private.cpp | 29 ++++++++++++++++++ src/crepe/util/Private.h | 34 +++++++++++++++++++++ src/crepe/util/Private.hpp | 31 +++++++++++++++++++ src/example/CMakeLists.txt | 1 - src/example/asset_manager.cpp | 36 ---------------------- src/test/AudioTest.cpp | 5 ++- src/test/CMakeLists.txt | 1 + src/test/PrivateTest.cpp | 66 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 210 insertions(+), 49 deletions(-) create mode 100644 src/crepe/util/Private.cpp create mode 100644 src/crepe/util/Private.h create mode 100644 src/crepe/util/Private.hpp delete mode 100644 src/example/asset_manager.cpp create mode 100644 src/test/PrivateTest.cpp (limited to 'src/test') diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 0950129..9d76f0b 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -2,6 +2,7 @@ #include "../Component.h" #include "../types.h" +#include "../util/Private.h" #include "GameObject.h" #include "Asset.h" @@ -47,14 +48,8 @@ private: bool rewind = false; private: - //! Value of \c active after last system update - bool last_active = false; - //! Value of \c playing after last system update - bool last_playing = false; - //! Value of \c volume after last system update - float last_volume = 1.0; - //! Value of \c loop after last system update - bool last_loop = false; + //! AudioSystem::ComponentPrivate + Private private_data; }; } // namespace crepe diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index b0b80f8..f33ee58 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -9,6 +9,10 @@ namespace crepe { class SoundContext; +struct SoundHandle { + SoLoud::handle handle; +}; + /** * \brief Sound resource facade * diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 97cf966..0f943be 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -1,6 +1,5 @@ #include "AudioSystem.h" -#include "../api/AudioSource.h" #include "../manager/ComponentManager.h" #include "../manager/ResourceManager.h" #include "../types.h" @@ -13,12 +12,24 @@ void AudioSystem::update() { ResourceManager & resource_manager = this->mediator.resource_manager; RefVector components = component_manager.get_components_by_type(); - for (auto component_ref : components) { - AudioSource & component = component_ref.get(); + for (AudioSource & component : components) { if (!component.active) continue; Sound & sound = resource_manager.get(component.source); + if (component.private_data.empty()) + component.private_data.set(); + auto & data = component.private_data.get(); // TODO: lots of state diffing + + + this->update_private(component, data); } } +void AudioSystem::update_private(const AudioSource & component, ComponentPrivate & data) { + data.last_active = component.active; + data.last_loop = component.loop; + data.last_playing = component.playing; + data.last_volume = component.volume; +} + diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index e037f51..7f41fda 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,6 +1,7 @@ #pragma once #include "../facade/SoundContext.h" +#include "../api/AudioSource.h" #include "System.h" @@ -11,6 +12,26 @@ public: using System::System; void update() override; +private: + /** + * \brief Private data stored by AudioSystem on AudioSource component + */ + struct ComponentPrivate { + //! This sample's voice handle + SoLoud::handle handle; + + //! Value of \c active after last system update + bool last_active = false; + //! Value of \c playing after last system update + bool last_playing = false; + //! Value of \c volume after last system update + float last_volume = 1.0; + //! Value of \c loop after last system update + bool last_loop = false; + }; + + void update_private(const AudioSource & component, ComponentPrivate & data); + private: SoundContext context {}; }; diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 94ed906..f49d851 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(crepe PUBLIC LogColor.cpp Log.cpp + Private.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -11,5 +12,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Proxy.hpp OptionalRef.h OptionalRef.hpp + Private.h + Private.hpp ) diff --git a/src/crepe/util/Private.cpp b/src/crepe/util/Private.cpp new file mode 100644 index 0000000..c5b5b30 --- /dev/null +++ b/src/crepe/util/Private.cpp @@ -0,0 +1,29 @@ +#include "Private.h" + +using namespace crepe; + +bool Private::empty() const noexcept { + return this->instance == nullptr; +} + +Private::~Private() { + if (this->instance == nullptr) return; + this->destructor(this->instance); +} + +Private::Private(Private && other) { + *this = std::move(other); +} + +Private & Private::operator=(Private && other) { + // TODO: ideally this function checks for self-assignment + this->instance = other.instance; + this->destructor = other.destructor; + this->type = other.type; + + other.instance = nullptr; + other.destructor = [](void*){}; + + return *this; +} + diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h new file mode 100644 index 0000000..fc3728f --- /dev/null +++ b/src/crepe/util/Private.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace crepe { + +class Private { +public: + Private() = default; + ~Private(); + Private(Private &&); + Private & operator=(Private &&); + Private(const Private &) = delete; + Private & operator=(const Private &) = delete; + + template + T & get(); + + template + void set(Args &&... args); + + bool empty() const noexcept; + +private: + std::function destructor; + std::type_index type = typeid(void); + void * instance = nullptr; +}; + +} + +#include "Private.hpp" + diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp new file mode 100644 index 0000000..30c8146 --- /dev/null +++ b/src/crepe/util/Private.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "Private.h" + +namespace crepe { + +template +void Private::set(Args &&... args) { + T * instance = new T(std::forward(args)...); + this->instance = static_cast(instance); + this->destructor = [](void * instance) { + delete static_cast(instance); + }; + this->type = typeid(T); +} + +template +T & Private::get() { + using namespace std; + if (this->empty()) + throw out_of_range("Private: get() called on empty object"); + type_index requested_type = typeid(T); + if (this->type != requested_type) + throw logic_error(format("Private: get() called with [T = {}] (actual is [T = {}])", requested_type.name(), this->type.name())); + return *static_cast(this->instance); +} + +} diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 560e2bc..9c3c550 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(asset_manager) add_example(savemgr) add_example(rendering_particle) add_example(gameloop) diff --git a/src/example/asset_manager.cpp b/src/example/asset_manager.cpp deleted file mode 100644 index 660b318..0000000 --- a/src/example/asset_manager.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -using namespace crepe; - -int main() { - - // this needs to be called before the asset manager otherwise the destructor of sdl is not in - // the right order - { Texture test("asset/texture/img.png"); } - // FIXME: make it so the issue described by the above comment is not possible (i.e. the order - // in which internal classes are instantiated should not impact the way the engine works). - - auto & mgr = AssetManager::get_instance(); - - { - // TODO: [design] the Sound class can't be directly included by the user as it includes - // SoLoud headers. - auto bgm = mgr.cache("mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("mwe/audio/sfx2.wav"); - - auto img = mgr.cache("asset/texture/img.png"); - auto img1 = mgr.cache("asset/texture/second.png"); - } - - { - auto bgm = mgr.cache("mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("mwe/audio/sfx2.wav"); - - auto img = mgr.cache("asset/texture/img.png"); - auto img1 = mgr.cache("asset/texture/second.png"); - } -} diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index afd2672..3be5afa 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -13,15 +14,17 @@ class AudioTest : public Test { Mediator mediator; public: ComponentManager component_manager{mediator}; + ResourceManager resource_manager{mediator}; AudioSystem system {mediator}; void SetUp() override { auto & mgr = this->component_manager; GameObject entity = mgr.new_object("name"); - entity.add_component("mwe/audio/sfx1.wav"); + AudioSource & audio_source = entity.add_component("mwe/audio/sfx1.wav"); } }; TEST_F(AudioTest, Default) { + system.update(); } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 4174926..8c4b855 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -16,4 +16,5 @@ target_sources(test_main PUBLIC Vector2Test.cpp ScriptEventTest.cpp ScriptSceneTest.cpp + PrivateTest.cpp ) diff --git a/src/test/PrivateTest.cpp b/src/test/PrivateTest.cpp new file mode 100644 index 0000000..f0d2b1a --- /dev/null +++ b/src/test/PrivateTest.cpp @@ -0,0 +1,66 @@ +#include + +#include + +using namespace std; +using namespace crepe; +using namespace testing; + +class PrivateTest : public Test { +public: + static unsigned constructors; + static unsigned destructors; + + void SetUp() override { + PrivateTest::constructors = 0; + PrivateTest::destructors = 0; + } + + class TestClass { + public: + TestClass() { PrivateTest::constructors++; } + ~TestClass() { PrivateTest::destructors++; } + }; + class Unrelated {}; +}; +unsigned PrivateTest::constructors; +unsigned PrivateTest::destructors; + +TEST_F(PrivateTest, Empty) { + { + Private foo; + } + + EXPECT_EQ(PrivateTest::constructors, 0); + EXPECT_EQ(PrivateTest::destructors, 0); +} + +TEST_F(PrivateTest, WithObject) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + } + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, EmptyException) { + Private foo; + EXPECT_THROW(foo.get(), std::out_of_range); + + foo.set(); + EXPECT_NO_THROW(foo.get()); +} + +TEST_F(PrivateTest, IncorrectTypeException) { + Private foo; + foo.set(); + + EXPECT_THROW(foo.get(), std::logic_error); + EXPECT_NO_THROW(foo.get()); +} + -- cgit v1.2.3 From 693355f55193cb2ea4c29616073227e37665afc1 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 29 Nov 2024 17:30:45 +0100 Subject: more audio system WIP --- src/crepe/facade/Sound.cpp | 64 ++++++++++++++-------------- src/crepe/facade/Sound.h | 66 +++++----------------------- src/crepe/facade/SoundContext.h | 3 +- src/crepe/system/AudioSystem.cpp | 29 ++++++++++--- src/crepe/system/AudioSystem.h | 26 +++++++----- src/crepe/util/Private.cpp | 5 +++ src/crepe/util/Private.h | 6 +-- src/crepe/util/Private.hpp | 4 +- src/test/AudioTest.cpp | 51 +++++++++++++++++++--- src/test/PrivateTest.cpp | 92 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 229 insertions(+), 117 deletions(-) (limited to 'src/test') diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 52496af..0df1f48 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -13,36 +13,36 @@ Sound::Sound(const Asset & src) : Resource(src) { } Sound::~Sound() { dbg_trace(); } -void Sound::play(SoundContext & ctx) { - if (ctx.engine.getPause(this->handle)) { - // resume if paused - ctx.engine.setPause(this->handle, false); - } else { - // or start new sound - this->handle = ctx.engine.play(this->sample, this->volume); - ctx.engine.setLooping(this->handle, this->looping); - } -} - -void Sound::pause(SoundContext & ctx) { - if (ctx.engine.getPause(this->handle)) return; - ctx.engine.setPause(this->handle, true); -} - -void Sound::rewind(SoundContext & ctx) { - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.seek(this->handle, 0); -} - -void Sound::set_volume(SoundContext & ctx, float volume) { - this->volume = volume; - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.setVolume(this->handle, this->volume); -} - -void Sound::set_looping(SoundContext & ctx, bool looping) { - this->looping = looping; - if (!ctx.engine.isValidVoiceHandle(this->handle)) return; - ctx.engine.setLooping(this->handle, this->looping); -} +// void Sound::play(SoundContext & ctx) { +// if (ctx.engine.getPause(this->handle)) { +// // resume if paused +// ctx.engine.setPause(this->handle, false); +// } else { +// // or start new sound +// this->handle = ctx.engine.play(this->sample, this->volume); +// ctx.engine.setLooping(this->handle, this->looping); +// } +// } +// +// void Sound::pause(SoundContext & ctx) { +// if (ctx.engine.getPause(this->handle)) return; +// ctx.engine.setPause(this->handle, true); +// } +// +// void Sound::rewind(SoundContext & ctx) { +// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// ctx.engine.seek(this->handle, 0); +// } +// +// void Sound::set_volume(SoundContext & ctx, float volume) { +// this->volume = volume; +// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// ctx.engine.setVolume(this->handle, this->volume); +// } +// +// void Sound::set_looping(SoundContext & ctx, bool looping) { +// this->looping = looping; +// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; +// ctx.engine.setLooping(this->handle, this->looping); +// } diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index f33ee58..10d7c3c 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -9,10 +9,6 @@ namespace crepe { class SoundContext; -struct SoundHandle { - SoLoud::handle handle; -}; - /** * \brief Sound resource facade * @@ -23,62 +19,20 @@ class Sound : public Resource { public: Sound(const Asset & src); ~Sound(); // dbg_trace - /** - * \brief Pause this sample - * - * Pauses this sound if it is playing, or does nothing if it is already paused. The playhead - * position is saved, such that calling \c play() after this function makes the sound resume. - */ - void pause(SoundContext & ctx); - /** - * \brief Play this sample - * - * Resume playback if this sound is paused, or start from the beginning of the sample. - * - * \note This class only saves a reference to the most recent 'voice' of this sound. Calling - * \c play() while the sound is already playing causes multiple instances of the sample to - * play simultaniously. The sample started last is the one that is controlled afterwards. - */ - void play(SoundContext & ctx); - /** - * \brief Reset playhead position - * - * Resets the playhead position so that calling \c play() after this function makes it play - * from the start of the sample. If the sound is not paused before calling this function, - * this function will stop playback. - */ - void rewind(SoundContext & ctx); - /** - * \brief Set playback volume / gain - * - * \param volume Volume (0 = muted, 1 = full volume) - */ - void set_volume(SoundContext & ctx, float volume); - /** - * \brief Get playback volume / gain - * - * \return Volume - */ - float get_volume() const { return this->volume; } - /** - * \brief Set looping behavior for this sample - * - * \param looping Looping behavior (false = one-shot, true = loop) - */ - void set_looping(SoundContext & ctx, bool looping); - /** - * \brief Get looping behavior - * - * \return true if looping, false if one-shot - */ - bool get_looping() const { return this->looping; } + + class Handle { + private: + SoLoud::handle handle; + float volume = 1.0f; + bool looping = false; + + friend class SoundContext; + }; private: SoLoud::Wav sample; - SoLoud::handle handle; - float volume = 1.0f; - bool looping = false; + friend class SoundContext; }; } // namespace crepe diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index d22ff7a..286ced8 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -22,10 +22,9 @@ public: SoundContext & operator=(const SoundContext &) = delete; SoundContext & operator=(SoundContext &&) = delete; + private: SoLoud::Soloud engine; - //! Sound directly calls methods on \c engine - friend class Sound; }; } // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 0f943be..191dbbb 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -15,18 +15,35 @@ void AudioSystem::update() { for (AudioSource & component : components) { if (!component.active) continue; - Sound & sound = resource_manager.get(component.source); - if (component.private_data.empty()) - component.private_data.set(); + Sound & resource = resource_manager.get(component.source); + + if (component.private_data.empty()) { + auto & data = component.private_data.set(); + this->update_last(component, data); + } auto & data = component.private_data.get(); - // TODO: lots of state diffing + this->diff_update(component, data, resource); + + this->update_last(component, data); + } +} + +void AudioSystem::diff_update(AudioSource & component, const ComponentPrivate & data, Sound & resource) { + bool update_playing = component.playing != data.last_playing; + bool update_volume = component.volume != data.last_volume; + bool update_loop = component.loop != data.last_loop; + bool update_active = component.active != data.last_active; - this->update_private(component, data); + if (update_active) + if (component.rewind) { + component.playing = false; + // this->context.rewind(resource, data.handle); } + } -void AudioSystem::update_private(const AudioSource & component, ComponentPrivate & data) { +void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & data) { data.last_active = component.active; data.last_loop = component.loop; data.last_playing = component.playing; diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 7f41fda..4650178 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,6 +1,7 @@ #pragma once #include "../facade/SoundContext.h" +#include "../facade/Sound.h" #include "../api/AudioSource.h" #include "System.h" @@ -18,19 +19,22 @@ private: */ struct ComponentPrivate { //! This sample's voice handle - SoLoud::handle handle; - - //! Value of \c active after last system update - bool last_active = false; - //! Value of \c playing after last system update - bool last_playing = false; - //! Value of \c volume after last system update - float last_volume = 1.0; - //! Value of \c loop after last system update - bool last_loop = false; + Sound::Handle handle; + + /** + * \name State diffing variables + * \{ + */ + typeof(AudioSource::active) last_active; + typeof(AudioSource::playing) last_playing; + typeof(AudioSource::volume) last_volume; + typeof(AudioSource::loop) last_loop; + //! \} }; - void update_private(const AudioSource & component, ComponentPrivate & data); + void update_last(const AudioSource & component, ComponentPrivate & data); + + void diff_update(AudioSource & component, const ComponentPrivate & data, Sound & resource); private: SoundContext context {}; diff --git a/src/crepe/util/Private.cpp b/src/crepe/util/Private.cpp index c5b5b30..cb4cb5b 100644 --- a/src/crepe/util/Private.cpp +++ b/src/crepe/util/Private.cpp @@ -27,3 +27,8 @@ Private & Private::operator=(Private && other) { return *this; } +Private::Private(const Private & other) { } +Private & Private::operator=(const Private & other) { + return *this; +} + diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h index fc3728f..6dd28bb 100644 --- a/src/crepe/util/Private.h +++ b/src/crepe/util/Private.h @@ -9,16 +9,16 @@ class Private { public: Private() = default; ~Private(); + Private(const Private &); Private(Private &&); + Private & operator=(const Private &); Private & operator=(Private &&); - Private(const Private &) = delete; - Private & operator=(const Private &) = delete; template T & get(); template - void set(Args &&... args); + T & set(Args &&... args); bool empty() const noexcept; diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp index 30c8146..d6ab23f 100644 --- a/src/crepe/util/Private.hpp +++ b/src/crepe/util/Private.hpp @@ -8,13 +8,15 @@ namespace crepe { template -void Private::set(Args &&... args) { +T & Private::set(Args &&... args) { + if (!this->empty()) this->destructor(this->instance); T * instance = new T(std::forward(args)...); this->instance = static_cast(instance); this->destructor = [](void * instance) { delete static_cast(instance); }; this->type = typeid(T); + return *instance; } template diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 3be5afa..c6f0097 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,6 +8,7 @@ #include using namespace std; +using namespace std::chrono_literals; using namespace crepe; using namespace testing; @@ -17,14 +19,51 @@ public: ResourceManager resource_manager{mediator}; AudioSystem system {mediator}; - void SetUp() override { - auto & mgr = this->component_manager; - GameObject entity = mgr.new_object("name"); - AudioSource & audio_source = entity.add_component("mwe/audio/sfx1.wav"); - } +private: + GameObject entity = component_manager.new_object("name"); +public: + AudioSource & bgm = entity.add_component("mwe/audio/bgm.ogg"); + AudioSource & sfx1 = entity.add_component("mwe/audio/sfx1.wav"); + AudioSource & sfx2 = entity.add_component("mwe/audio/sfx2.wav"); + AudioSource & sfx3 = entity.add_component("mwe/audio/sfx3.wav"); + }; TEST_F(AudioTest, Default) { - system.update(); + bool example_done = false; + + future example = async([&](){ + // Start the background track + bgm.play(); + + // Play each sample sequentially while pausing and resuming the background track + this_thread::sleep_for(500ms); + sfx1.play(); + this_thread::sleep_for(500ms); + sfx2.play(); + bgm.stop(); + this_thread::sleep_for(500ms); + sfx3.play(); + bgm.play(); + this_thread::sleep_for(500ms); + + // Play all samples simultaniously + sfx1.play(); + sfx2.play(); + sfx3.play(); + this_thread::sleep_for(1000ms); + }); + + future system_loop = async([&](){ + while (!example_done) { + auto next = chrono::steady_clock::now() + 25ms; + system.update(); + this_thread::sleep_until(next); + } + }); + + example.wait(); + example_done = true; + system_loop.wait(); } diff --git a/src/test/PrivateTest.cpp b/src/test/PrivateTest.cpp index f0d2b1a..0ea67d6 100644 --- a/src/test/PrivateTest.cpp +++ b/src/test/PrivateTest.cpp @@ -64,3 +64,95 @@ TEST_F(PrivateTest, IncorrectTypeException) { EXPECT_NO_THROW(foo.get()); } +TEST_F(PrivateTest, MoveConstructor) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + + Private bar(std::move(foo)); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + } + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, MoveOperator) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + + Private bar = std::move(foo); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + } + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, CopyConstructor) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + + Private bar(foo); + + EXPECT_TRUE(bar.empty()); + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + } + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, CopyOperator) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + + Private bar = foo; + + EXPECT_TRUE(bar.empty()); + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + } + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 1); +} + +TEST_F(PrivateTest, DoubleAssignment) { + { + Private foo; + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 1); + EXPECT_EQ(PrivateTest::destructors, 0); + + foo.set(); + + EXPECT_EQ(PrivateTest::constructors, 2); + EXPECT_EQ(PrivateTest::destructors, 1); + } + + EXPECT_EQ(PrivateTest::constructors, 2); + EXPECT_EQ(PrivateTest::destructors, 2); +} + -- cgit v1.2.3 From 30e2b2b0cbb503d83a087d8d326940c3c4bc8fff Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 15:08:57 +0100 Subject: fix audio system implementation --- src/crepe/api/AudioSource.cpp | 4 ++-- src/crepe/api/AudioSource.h | 15 +++++++++++++-- src/crepe/facade/SoundContext.cpp | 6 ------ src/crepe/facade/SoundContext.h | 1 - src/crepe/system/AudioSystem.cpp | 23 ++++++++++------------- src/crepe/system/AudioSystem.h | 1 - src/test/AudioTest.cpp | 14 ++++++++++---- 7 files changed, 35 insertions(+), 29 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp index c646aeb..cc70801 100644 --- a/src/crepe/api/AudioSource.cpp +++ b/src/crepe/api/AudioSource.cpp @@ -10,10 +10,10 @@ AudioSource::AudioSource(game_object_id_t id, const Asset & src) : void AudioSource::play(bool looping) { this->loop = looping; - this->playing = true; + this->oneshot_play = true; } void AudioSource::stop() { - this->playing = false; + this->oneshot_stop = true; } diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 8dc1645..1899c22 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -42,8 +42,19 @@ private: //! This audio source's clip const Asset source; - //! If this source is playing audio - bool playing = false; + /** + * \name One-shot state variables + * + * These variables trigger function calls when set to true, and are unconditionally reset on + * every system update. + * + * \{ + */ + //! Play this sample + bool oneshot_play = false; + //! Stop this sample + bool oneshot_stop = false; + //! \} private: //! AudioSystem::ComponentPrivate diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 3e9a3d1..3ae5956 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -34,9 +34,3 @@ void SoundContext::set_loop(Sound & resource, Sound::Handle & handle, bool loop) this->engine.setLooping(handle.handle, loop); } -bool SoundContext::get_playing(Sound::Handle & handle) { - // See Soloud::stopVoice_internal in soloud/src/core/soloud_core_voiceops.cpp for why this is - // the correct method to use here (samples are currently never paused) - return this->engine.isValidVoiceHandle(handle.handle); -} - diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index e02977e..91d3fe9 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -28,7 +28,6 @@ public: void stop(Sound::Handle &); void set_volume(Sound &, Sound::Handle &, float); void set_loop(Sound &, Sound::Handle &, bool); - bool get_playing(Sound::Handle &); private: SoLoud::Soloud engine; diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 98aff58..b105a4d 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -13,14 +13,12 @@ void AudioSystem::update() { RefVector components = component_manager.get_components_by_type(); for (AudioSource & component : components) { - if (!component.active) continue; - Sound & resource = resource_manager.get(component.source); if (component.private_data.empty()) { auto & data = component.private_data.set(); this->update_last(component, data); - data.last_playing = false; // always start + data.last_active = false; } auto & data = component.private_data.get(); @@ -31,27 +29,26 @@ void AudioSystem::update() { } void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource) { - bool update_playing = component.playing != data.last_playing; bool update_volume = component.volume != data.last_volume; bool update_loop = component.loop != data.last_loop; bool update_active = component.active != data.last_active; if (update_active) { if (component.active) { - update_playing = true; - if (component.play_on_awake) - component.playing = true; + component.oneshot_play = component.play_on_awake; } else { this->context.stop(data.handle); return; } } if (!component.active) return; - if (update_playing) { - if (component.playing) data.handle = this->context.play(resource); - else this->context.stop(data.handle); - } else { - component.playing = this->context.get_playing(data.handle); + if (component.oneshot_play) { + data.handle = this->context.play(resource); + component.oneshot_play = false; + } + if (component.oneshot_stop) { + this->context.stop(data.handle); + component.oneshot_stop = false; } if (update_volume) { this->context.set_volume(resource, data.handle, component.volume); @@ -63,8 +60,8 @@ void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & data) { data.last_active = component.active; + if (!component.active) return; data.last_loop = component.loop; - data.last_playing = component.playing; data.last_volume = component.volume; } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 3404878..dee82f6 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -26,7 +26,6 @@ private: * \{ */ typeof(AudioSource::active) last_active; - typeof(AudioSource::playing) last_playing; typeof(AudioSource::volume) last_volume; typeof(AudioSource::loop) last_loop; //! \} diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index c6f0097..2a2d0e1 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,3 +1,4 @@ +#include "util/Log.h" #include #include @@ -27,24 +28,29 @@ public: AudioSource & sfx2 = entity.add_component("mwe/audio/sfx2.wav"); AudioSource & sfx3 = entity.add_component("mwe/audio/sfx3.wav"); + void SetUp() override { + bgm.play_on_awake = true; + } }; TEST_F(AudioTest, Default) { bool example_done = false; future example = async([&](){ - // Start the background track - bgm.play(); + // Start the background track. This happens automatically due to the play_on_awake property + // being true. The following call is optional and doesn't start two simultanious voices if + // left in: + // bgm.play(); // Play each sample sequentially while pausing and resuming the background track this_thread::sleep_for(500ms); sfx1.play(); this_thread::sleep_for(500ms); sfx2.play(); - bgm.stop(); + bgm.active = false; this_thread::sleep_for(500ms); sfx3.play(); - bgm.play(); + bgm.active = true; this_thread::sleep_for(500ms); // Play all samples simultaniously -- cgit v1.2.3 From eefaeb807eb5d0b08353389070bf3d27c3d0d958 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 16:32:13 +0100 Subject: test and debug audio system --- src/crepe/facade/SoundContext.h | 8 +- src/crepe/system/AudioSystem.cpp | 28 ++++--- src/crepe/system/AudioSystem.h | 4 +- src/crepe/system/System.cpp | 4 +- src/test/AudioTest.cpp | 174 ++++++++++++++++++++++++++++----------- 5 files changed, 150 insertions(+), 68 deletions(-) (limited to 'src/test') diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index 91d3fe9..c651cd5 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -24,10 +24,10 @@ public: SoundContext & operator=(const SoundContext &) = delete; SoundContext & operator=(SoundContext &&) = delete; - Sound::Handle play(Sound & resource); - void stop(Sound::Handle &); - void set_volume(Sound &, Sound::Handle &, float); - void set_loop(Sound &, Sound::Handle &, bool); + virtual Sound::Handle play(Sound & resource); + virtual void stop(Sound::Handle &); + virtual void set_volume(Sound &, Sound::Handle &, float); + virtual void set_loop(Sound &, Sound::Handle &, bool); private: SoLoud::Soloud engine; diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b105a4d..84a101a 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -29,39 +29,43 @@ void AudioSystem::update() { } void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource) { - bool update_volume = component.volume != data.last_volume; - bool update_loop = component.loop != data.last_loop; - bool update_active = component.active != data.last_active; + SoundContext & context = this->get_context(); - if (update_active) { + if (component.active != data.last_active) { if (component.active) { component.oneshot_play = component.play_on_awake; } else { - this->context.stop(data.handle); + context.stop(data.handle); return; } } if (!component.active) return; + if (component.oneshot_play) { - data.handle = this->context.play(resource); + data.handle = context.play(resource); component.oneshot_play = false; } if (component.oneshot_stop) { - this->context.stop(data.handle); + context.stop(data.handle); component.oneshot_stop = false; } - if (update_volume) { - this->context.set_volume(resource, data.handle, component.volume); + if (component.volume != data.last_volume) { + context.set_volume(resource, data.handle, component.volume); } - if (update_loop) { - this->context.set_loop(resource, data.handle, component.loop); + if (component.loop != data.last_loop) { + context.set_loop(resource, data.handle, component.loop); } } void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & data) { data.last_active = component.active; - if (!component.active) return; data.last_loop = component.loop; data.last_volume = component.volume; } +SoundContext & AudioSystem::get_context() { + if (this->context.empty()) + this->context.set(); + return this->context.get(); +} + diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index dee82f6..a004c60 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -35,8 +35,10 @@ private: void diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource); +protected: + virtual SoundContext & get_context(); private: - SoundContext context {}; + Private context; }; } // namespace crepe diff --git a/src/crepe/system/System.cpp b/src/crepe/system/System.cpp index f68549b..ecc740d 100644 --- a/src/crepe/system/System.cpp +++ b/src/crepe/system/System.cpp @@ -1,7 +1,5 @@ -#include "../util/Log.h" - #include "System.h" using namespace crepe; -System::System(const Mediator & mediator) : mediator(mediator) { dbg_trace(); } +System::System(const Mediator & mediator) : mediator(mediator) {} diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 2a2d0e1..9c3cb9c 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,6 +1,5 @@ -#include "util/Log.h" #include -#include +#include #include #include @@ -14,62 +13,141 @@ using namespace crepe; using namespace testing; class AudioTest : public Test { +private: + class TestSoundContext : public SoundContext { + public: + MOCK_METHOD(Sound::Handle, play, (Sound & resource), (override)); + MOCK_METHOD(void, stop, (Sound::Handle &), (override)); + MOCK_METHOD(void, set_volume, (Sound &, Sound::Handle &, float), (override)); + MOCK_METHOD(void, set_loop, (Sound &, Sound::Handle &, bool), (override)); + }; + + class TestAudioSystem : public AudioSystem { + public: + using AudioSystem::AudioSystem; + StrictMock context; + virtual SoundContext & get_context() { + return this->context; + } + }; + +private: Mediator mediator; -public: ComponentManager component_manager{mediator}; ResourceManager resource_manager{mediator}; - AudioSystem system {mediator}; +public: + TestAudioSystem system {mediator}; + TestSoundContext & context = system.context; private: GameObject entity = component_manager.new_object("name"); public: - AudioSource & bgm = entity.add_component("mwe/audio/bgm.ogg"); - AudioSource & sfx1 = entity.add_component("mwe/audio/sfx1.wav"); - AudioSource & sfx2 = entity.add_component("mwe/audio/sfx2.wav"); - AudioSource & sfx3 = entity.add_component("mwe/audio/sfx3.wav"); - - void SetUp() override { - bgm.play_on_awake = true; - } + AudioSource & component = entity.add_component("mwe/audio/bgm.ogg"); }; TEST_F(AudioTest, Default) { - bool example_done = false; - - future example = async([&](){ - // Start the background track. This happens automatically due to the play_on_awake property - // being true. The following call is optional and doesn't start two simultanious voices if - // left in: - // bgm.play(); - - // Play each sample sequentially while pausing and resuming the background track - this_thread::sleep_for(500ms); - sfx1.play(); - this_thread::sleep_for(500ms); - sfx2.play(); - bgm.active = false; - this_thread::sleep_for(500ms); - sfx3.play(); - bgm.active = true; - this_thread::sleep_for(500ms); - - // Play all samples simultaniously - sfx1.play(); - sfx2.play(); - sfx3.play(); - this_thread::sleep_for(1000ms); - }); - - future system_loop = async([&](){ - while (!example_done) { - auto next = chrono::steady_clock::now() + 25ms; - system.update(); - this_thread::sleep_until(next); - } - }); + EXPECT_CALL(context, play(_)).Times(0); + EXPECT_CALL(context, stop(_)).Times(0); + EXPECT_CALL(context, set_volume(_, _, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + system.update(); +} + +TEST_F(AudioTest, Play) { + system.update(); + + { + InSequence seq; - example.wait(); - example_done = true; - system_loop.wait(); + EXPECT_CALL(context, play(_)).Times(0); + component.play(); + } + + { + InSequence seq; + + EXPECT_CALL(context, play(_)).Times(1); + system.update(); + } +} + +TEST_F(AudioTest, Stop) { + system.update(); + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(0); + component.stop(); + } + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(1); + system.update(); + } +} + +TEST_F(AudioTest, Volume) { + system.update(); + + { + InSequence seq; + + EXPECT_CALL(context, set_volume(_, _, _)).Times(0); + component.volume += 0.2; + } + + { + InSequence seq; + + EXPECT_CALL(context, set_volume(_, _, component.volume)).Times(1); + system.update(); + } +} + +TEST_F(AudioTest, Looping) { + system.update(); + + { + InSequence seq; + + EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + component.loop = !component.loop; + } + + { + InSequence seq; + + EXPECT_CALL(context, set_loop(_, _, component.loop)).Times(1); + system.update(); + } +} + +TEST_F(AudioTest, StopOnDeactivate) { + system.update(); + + { + InSequence seq; + + EXPECT_CALL(context, stop(_)).Times(1); + component.active = false; + system.update(); + } +} + +TEST_F(AudioTest, PlayOnActive) { + component.active = false; + component.play_on_awake = true; + system.update(); + + { + InSequence seq; + + EXPECT_CALL(context, play(_)).Times(1); + component.active = true; + system.update(); + } } -- cgit v1.2.3 From 9eff2e24fa4cf0ffad2b47cc922a6558bc1a9fa1 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 16:42:21 +0100 Subject: `make format` --- src/crepe/Resource.cpp | 3 +-- src/crepe/api/AudioSource.cpp | 12 ++++-------- src/crepe/api/AudioSource.h | 4 ++-- src/crepe/api/LoopManager.cpp | 5 +---- src/crepe/api/LoopManager.h | 2 +- src/crepe/api/Scene.h | 3 ++- src/crepe/api/Script.cpp | 1 - src/crepe/api/Script.h | 4 ++-- src/crepe/facade/Sound.cpp | 9 ++++----- src/crepe/facade/SoundContext.cpp | 5 +---- src/crepe/manager/ComponentManager.cpp | 2 +- src/crepe/manager/Manager.cpp | 3 +-- src/crepe/manager/Manager.h | 3 +-- src/crepe/manager/Mediator.h | 4 ++-- src/crepe/manager/ResourceManager.cpp | 8 ++------ src/crepe/manager/ResourceManager.hpp | 13 +++++++------ src/crepe/manager/SaveManager.cpp | 4 ++-- src/crepe/system/AudioSystem.cpp | 10 +++++----- src/crepe/system/AudioSystem.h | 6 +++--- src/crepe/system/PhysicsSystem.cpp | 2 +- src/crepe/system/RenderSystem.cpp | 2 +- src/crepe/system/ScriptSystem.cpp | 2 +- src/crepe/util/Private.cpp | 17 +++++------------ src/crepe/util/Private.h | 5 ++--- src/crepe/util/Private.hpp | 18 ++++++++---------- src/test/AudioTest.cpp | 15 +++++++-------- src/test/ECSTest.cpp | 3 ++- src/test/EventTest.cpp | 6 ++---- src/test/ParticleTest.cpp | 3 ++- src/test/PhysicsTest.cpp | 3 ++- src/test/PrivateTest.cpp | 5 +---- src/test/RenderSystemTest.cpp | 3 ++- src/test/ResourceManagerTest.cpp | 16 ++++++---------- src/test/SceneManagerTest.cpp | 7 ++++--- src/test/ScriptEventTest.cpp | 7 +++---- src/test/ScriptSceneTest.cpp | 3 +-- src/test/ScriptTest.cpp | 3 +-- src/test/ScriptTest.h | 8 +++++--- src/test/main.cpp | 3 +-- 39 files changed, 99 insertions(+), 133 deletions(-) (limited to 'src/test') diff --git a/src/crepe/Resource.cpp b/src/crepe/Resource.cpp index e254695..27b4c4b 100644 --- a/src/crepe/Resource.cpp +++ b/src/crepe/Resource.cpp @@ -2,5 +2,4 @@ using namespace crepe; -Resource::Resource(const Asset & asset) { } - +Resource::Resource(const Asset & asset) {} diff --git a/src/crepe/api/AudioSource.cpp b/src/crepe/api/AudioSource.cpp index cc70801..7b05cb1 100644 --- a/src/crepe/api/AudioSource.cpp +++ b/src/crepe/api/AudioSource.cpp @@ -3,17 +3,13 @@ using namespace crepe; using namespace std; -AudioSource::AudioSource(game_object_id_t id, const Asset & src) : - Component(id), - source(src) -{ } +AudioSource::AudioSource(game_object_id_t id, const Asset & src) + : Component(id), + source(src) {} void AudioSource::play(bool looping) { this->loop = looping; this->oneshot_play = true; } -void AudioSource::stop() { - this->oneshot_stop = true; -} - +void AudioSource::stop() { this->oneshot_stop = true; } diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 1899c22..63b4bc4 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -4,8 +4,8 @@ #include "../types.h" #include "../util/Private.h" -#include "GameObject.h" #include "Asset.h" +#include "GameObject.h" namespace crepe { @@ -20,6 +20,7 @@ protected: AudioSource(game_object_id_t id, const Asset & source); //! Only ComponentManager can create components friend class ComponentManager; + public: // But std::unique_ptr needs to be able to destoy this component again virtual ~AudioSource() = default; @@ -62,4 +63,3 @@ private: }; } // namespace crepe - diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index b277185..731cfb7 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -22,9 +22,7 @@ LoopManager::LoopManager() { this->load_system(); } -void LoopManager::process_input() { - this->sdl_context.handle_events(this->game_running); -} +void LoopManager::process_input() { this->sdl_context.handle_events(this->game_running); } void LoopManager::start() { this->setup(); @@ -69,4 +67,3 @@ void LoopManager::render() { } void LoopManager::update() {} - diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 6ea5ccc..d8910a0 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -2,10 +2,10 @@ #include +#include "../facade/SDLContext.h" #include "../manager/ComponentManager.h" #include "../manager/SceneManager.h" #include "../system/System.h" -#include "../facade/SDLContext.h" #include "LoopTimer.h" diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index 66dad17..9f1e8ce 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -2,8 +2,8 @@ #include -#include "../util/OptionalRef.h" #include "../manager/Mediator.h" +#include "../util/OptionalRef.h" namespace crepe { @@ -37,6 +37,7 @@ public: // TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests // keep passing, but this reference should not be directly accessible by the user!!! + protected: /** * \name Late references diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index a27838e..4091fd4 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -25,4 +25,3 @@ void Script::set_next_scene(const string & name) { SceneManager & mgr = mediator.scene_manager; mgr.set_next_scene(name); } - diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index e1f86b2..1b339b0 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,10 +2,10 @@ #include +#include "../manager/EventManager.h" +#include "../manager/Mediator.h" #include "../types.h" #include "../util/OptionalRef.h" -#include "../manager/Mediator.h" -#include "../manager/EventManager.h" namespace crepe { diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 0df1f48..33a0c47 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -23,26 +23,25 @@ Sound::~Sound() { dbg_trace(); } // ctx.engine.setLooping(this->handle, this->looping); // } // } -// +// // void Sound::pause(SoundContext & ctx) { // if (ctx.engine.getPause(this->handle)) return; // ctx.engine.setPause(this->handle, true); // } -// +// // void Sound::rewind(SoundContext & ctx) { // if (!ctx.engine.isValidVoiceHandle(this->handle)) return; // ctx.engine.seek(this->handle, 0); // } -// +// // void Sound::set_volume(SoundContext & ctx, float volume) { // this->volume = volume; // if (!ctx.engine.isValidVoiceHandle(this->handle)) return; // ctx.engine.setVolume(this->handle, this->volume); // } -// +// // void Sound::set_looping(SoundContext & ctx, bool looping) { // this->looping = looping; // if (!ctx.engine.isValidVoiceHandle(this->handle)) return; // ctx.engine.setLooping(this->handle, this->looping); // } - diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 3ae5956..470b3cc 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -21,9 +21,7 @@ Sound::Handle SoundContext::play(Sound & resource) { }; } -void SoundContext::stop(Sound::Handle & handle) { - this->engine.stop(handle.handle); -} +void SoundContext::stop(Sound::Handle & handle) { this->engine.stop(handle.handle); } void SoundContext::set_volume(Sound & resource, Sound::Handle & handle, float volume) { this->engine.setVolume(handle.handle, volume); @@ -33,4 +31,3 @@ void SoundContext::set_volume(Sound & resource, Sound::Handle & handle, float vo void SoundContext::set_loop(Sound & resource, Sound::Handle & handle, bool loop) { this->engine.setLooping(handle.handle, loop); } - diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 5a96158..80cf8b4 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,6 +1,6 @@ #include "../api/GameObject.h" -#include "../util/Log.h" #include "../types.h" +#include "../util/Log.h" #include "ComponentManager.h" diff --git a/src/crepe/manager/Manager.cpp b/src/crepe/manager/Manager.cpp index fe7c936..1182785 100644 --- a/src/crepe/manager/Manager.cpp +++ b/src/crepe/manager/Manager.cpp @@ -2,5 +2,4 @@ using namespace crepe; -Manager::Manager(Mediator & mediator) : mediator(mediator) { } - +Manager::Manager(Mediator & mediator) : mediator(mediator) {} diff --git a/src/crepe/manager/Manager.h b/src/crepe/manager/Manager.h index 9adfd0b..4f21ef4 100644 --- a/src/crepe/manager/Manager.h +++ b/src/crepe/manager/Manager.h @@ -13,5 +13,4 @@ protected: Mediator & mediator; }; -} - +} // namespace crepe diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 475aed9..e9c10b1 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -3,8 +3,8 @@ #include "../util/OptionalRef.h" // TODO: remove these singletons: -#include "SaveManager.h" #include "EventManager.h" +#include "SaveManager.h" namespace crepe { @@ -32,4 +32,4 @@ struct Mediator { OptionalRef resource_manager; }; -} +} // namespace crepe diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp index 87585ad..7c01808 100644 --- a/src/crepe/manager/ResourceManager.cpp +++ b/src/crepe/manager/ResourceManager.cpp @@ -18,17 +18,13 @@ void ResourceManager::clear() { }); } -void ResourceManager::clear_all() { - this->resources.clear(); -} +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] = {}; + if (!this->resources.contains(asset)) this->resources[asset] = {}; return this->resources.at(asset); } - diff --git a/src/crepe/manager/ResourceManager.hpp b/src/crepe/manager/ResourceManager.hpp index 8270bc5..5167d71 100644 --- a/src/crepe/manager/ResourceManager.hpp +++ b/src/crepe/manager/ResourceManager.hpp @@ -9,18 +9,19 @@ namespace crepe { template T & ResourceManager::get(const Asset & asset) { using namespace std; - static_assert(is_base_of::value, "cache must recieve a derivative class of Resource"); + static_assert(is_base_of::value, + "cache must recieve a derivative class of Resource"); CacheEntry & entry = this->get_entry(asset); - if (entry.resource == nullptr) - entry.resource = make_unique(asset); + if (entry.resource == nullptr) entry.resource = make_unique(asset); T * concrete_resource = dynamic_cast(entry.resource.get()); if (concrete_resource == nullptr) - throw runtime_error(format("ResourceManager: mismatch between requested type and actual type of resource ({})", asset.get_path())); + 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 121d017..d4ed1c1 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,7 +1,7 @@ +#include "../ValueBroker.h" +#include "../api/Config.h" #include "../facade/DB.h" #include "../util/Log.h" -#include "../api/Config.h" -#include "../ValueBroker.h" #include "SaveManager.h" diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 84a101a..0696b34 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -10,7 +10,8 @@ using namespace std; void AudioSystem::update() { ComponentManager & component_manager = this->mediator.component_manager; ResourceManager & resource_manager = this->mediator.resource_manager; - RefVector components = component_manager.get_components_by_type(); + RefVector components + = component_manager.get_components_by_type(); for (AudioSource & component : components) { Sound & resource = resource_manager.get(component.source); @@ -28,7 +29,8 @@ void AudioSystem::update() { } } -void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource) { +void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, + Sound & resource) { SoundContext & context = this->get_context(); if (component.active != data.last_active) { @@ -64,8 +66,6 @@ void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & } SoundContext & AudioSystem::get_context() { - if (this->context.empty()) - this->context.set(); + if (this->context.empty()) this->context.set(); return this->context.get(); } - diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index a004c60..c941470 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -1,8 +1,8 @@ #pragma once -#include "../facade/SoundContext.h" -#include "../facade/Sound.h" #include "../api/AudioSource.h" +#include "../facade/Sound.h" +#include "../facade/SoundContext.h" #include "System.h" @@ -37,9 +37,9 @@ private: protected: virtual SoundContext & get_context(); + private: Private context; }; } // namespace crepe - diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index eba9dfa..bebcf3d 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -1,10 +1,10 @@ #include -#include "../manager/ComponentManager.h" #include "../api/Config.h" #include "../api/Rigidbody.h" #include "../api/Transform.h" #include "../api/Vector2.h" +#include "../manager/ComponentManager.h" #include "PhysicsSystem.h" diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 4e97b3e..0ad685c 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -5,12 +5,12 @@ #include #include -#include "../manager/ComponentManager.h" #include "../api/Camera.h" #include "../api/ParticleEmitter.h" #include "../api/Sprite.h" #include "../api/Transform.h" #include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h" #include "RenderSystem.h" diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp index 2e16eb0..d6b2ca1 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -1,6 +1,6 @@ -#include "../manager/ComponentManager.h" #include "../api/BehaviorScript.h" #include "../api/Script.h" +#include "../manager/ComponentManager.h" #include "ScriptSystem.h" diff --git a/src/crepe/util/Private.cpp b/src/crepe/util/Private.cpp index cb4cb5b..262620d 100644 --- a/src/crepe/util/Private.cpp +++ b/src/crepe/util/Private.cpp @@ -2,18 +2,14 @@ using namespace crepe; -bool Private::empty() const noexcept { - return this->instance == nullptr; -} +bool Private::empty() const noexcept { return this->instance == nullptr; } Private::~Private() { if (this->instance == nullptr) return; this->destructor(this->instance); } -Private::Private(Private && other) { - *this = std::move(other); -} +Private::Private(Private && other) { *this = std::move(other); } Private & Private::operator=(Private && other) { // TODO: ideally this function checks for self-assignment @@ -22,13 +18,10 @@ Private & Private::operator=(Private && other) { this->type = other.type; other.instance = nullptr; - other.destructor = [](void*){}; - - return *this; -} + other.destructor = [](void *) {}; -Private::Private(const Private & other) { } -Private & Private::operator=(const Private & other) { return *this; } +Private::Private(const Private & other) {} +Private & Private::operator=(const Private & other) { return *this; } diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h index 6dd28bb..62a2e1a 100644 --- a/src/crepe/util/Private.h +++ b/src/crepe/util/Private.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include namespace crepe { @@ -28,7 +28,6 @@ private: void * instance = nullptr; }; -} +} // namespace crepe #include "Private.hpp" - diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp index d6ab23f..3a87a9f 100644 --- a/src/crepe/util/Private.hpp +++ b/src/crepe/util/Private.hpp @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include "Private.h" @@ -11,10 +11,8 @@ template T & Private::set(Args &&... args) { if (!this->empty()) this->destructor(this->instance); T * instance = new T(std::forward(args)...); - this->instance = static_cast(instance); - this->destructor = [](void * instance) { - delete static_cast(instance); - }; + this->instance = static_cast(instance); + this->destructor = [](void * instance) { delete static_cast(instance); }; this->type = typeid(T); return *instance; } @@ -22,12 +20,12 @@ T & Private::set(Args &&... args) { template T & Private::get() { using namespace std; - if (this->empty()) - throw out_of_range("Private: get() called on empty object"); + if (this->empty()) throw out_of_range("Private: get() called on empty object"); type_index requested_type = typeid(T); if (this->type != requested_type) - throw logic_error(format("Private: get() called with [T = {}] (actual is [T = {}])", requested_type.name(), this->type.name())); - return *static_cast(this->instance); + throw logic_error(format("Private: get() called with [T = {}] (actual is [T = {}])", + requested_type.name(), this->type.name())); + return *static_cast(this->instance); } -} +} // namespace crepe diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 9c3cb9c..14f57bd 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -1,10 +1,10 @@ -#include #include +#include -#include -#include #include #include +#include +#include #include using namespace std; @@ -26,21 +26,21 @@ private: public: using AudioSystem::AudioSystem; StrictMock context; - virtual SoundContext & get_context() { - return this->context; - } + virtual SoundContext & get_context() { return this->context; } }; private: Mediator mediator; ComponentManager component_manager{mediator}; ResourceManager resource_manager{mediator}; + public: - TestAudioSystem system {mediator}; + TestAudioSystem system{mediator}; TestSoundContext & context = system.context; private: GameObject entity = component_manager.new_object("name"); + public: AudioSource & component = entity.add_component("mwe/audio/bgm.ogg"); }; @@ -150,4 +150,3 @@ TEST_F(AudioTest, PlayOnActive) { system.update(); } } - diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index 22c4fe7..3e6c61c 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -2,17 +2,18 @@ #define protected public -#include #include #include #include #include +#include using namespace std; using namespace crepe; class ECSTest : public ::testing::Test { Mediator m; + public: ComponentManager mgr{m}; }; diff --git a/src/test/EventTest.cpp b/src/test/EventTest.cpp index 350dd07..dccd554 100644 --- a/src/test/EventTest.cpp +++ b/src/test/EventTest.cpp @@ -2,9 +2,9 @@ #include #include -#include #include #include +#include using namespace std; using namespace std::chrono_literals; @@ -37,9 +37,7 @@ public: }; TEST_F(EventManagerTest, EventSubscription) { - EventHandler key_handler = [](const KeyPressEvent & e) { - return true; - }; + EventHandler key_handler = [](const KeyPressEvent & e) { return true; }; // Subscribe to KeyPressEvent EventManager::get_instance().subscribe(key_handler, 1); diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 4e9fa4e..a659fe5 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +17,7 @@ using namespace crepe; class ParticlesTest : public ::testing::Test { Mediator m; + public: ComponentManager component_manager{m}; ParticleSystem particle_system{m}; diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 01b7c51..43af8e4 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include #include @@ -12,6 +12,7 @@ using namespace crepe; class PhysicsTest : public ::testing::Test { Mediator m; + public: ComponentManager component_manager{m}; PhysicsSystem system{m}; diff --git a/src/test/PrivateTest.cpp b/src/test/PrivateTest.cpp index 0ea67d6..454789e 100644 --- a/src/test/PrivateTest.cpp +++ b/src/test/PrivateTest.cpp @@ -27,9 +27,7 @@ unsigned PrivateTest::constructors; unsigned PrivateTest::destructors; TEST_F(PrivateTest, Empty) { - { - Private foo; - } + { Private foo; } EXPECT_EQ(PrivateTest::constructors, 0); EXPECT_EQ(PrivateTest::destructors, 0); @@ -155,4 +153,3 @@ TEST_F(PrivateTest, DoubleAssignment) { EXPECT_EQ(PrivateTest::constructors, 2); EXPECT_EQ(PrivateTest::destructors, 2); } - diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index 3528e46..c105dcb 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -7,11 +7,11 @@ #define protected public #include -#include #include #include #include #include +#include #include @@ -21,6 +21,7 @@ using namespace testing; class RenderSystemTest : public Test { Mediator m; + public: ComponentManager mgr{m}; RenderSystem sys{m}; diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 1f56e23..b6be3c0 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -3,9 +3,9 @@ #define private public #define protected public -#include -#include #include +#include +#include using namespace std; using namespace crepe; @@ -13,6 +13,7 @@ using namespace testing; class ResourceManagerTest : public Test { Mediator mediator; + public: ResourceManager resource_manager{mediator}; @@ -25,19 +26,15 @@ public: public: const unsigned instance; - TestResource(const Asset & src) - : Resource(src), - instance(this->instances++) { } + TestResource(const Asset & src) : Resource(src), instance(this->instances++) {} ~TestResource() { this->instances--; } - bool operator == (const TestResource & other) const { + bool operator==(const TestResource & other) const { return this->instance == other.instance; } }; private: - void SetUp() override { - TestResource::instances = 0; - } + void SetUp() override { TestResource::instances = 0; } }; unsigned ResourceManagerTest::TestResource::instances = 0; @@ -72,4 +69,3 @@ TEST_F(ResourceManagerTest, Persistent) { resource_manager.clear_all(); EXPECT_EQ(TestResource::instances, 0); } - diff --git a/src/test/SceneManagerTest.cpp b/src/test/SceneManagerTest.cpp index d027d89..9bb260c 100644 --- a/src/test/SceneManagerTest.cpp +++ b/src/test/SceneManagerTest.cpp @@ -1,13 +1,13 @@ #include -#include -#include -#include #include #include #include #include #include +#include +#include +#include using namespace std; using namespace crepe; @@ -57,6 +57,7 @@ private: class SceneManagerTest : public ::testing::Test { Mediator m; + public: ComponentManager component_mgr{m}; SceneManager scene_mgr{m}; diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index 7a9abbb..5da31e7 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -4,13 +4,13 @@ #define private public #define protected public -#include -#include #include #include #include #include #include +#include +#include #include #include "ScriptTest.h" @@ -32,7 +32,7 @@ TEST_F(ScriptEventTest, Inactive) { EventManager & evmgr = this->event_manager; unsigned event_count = 0; - script.subscribe([&](const MyEvent &){ + script.subscribe([&](const MyEvent &) { event_count++; return true; }); @@ -48,4 +48,3 @@ TEST_F(ScriptEventTest, Inactive) { evmgr.trigger_event(); EXPECT_EQ(1, event_count); } - diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index f96ae8b..9ee1e52 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -4,8 +4,8 @@ #define private public #define protected public -#include #include "ScriptTest.h" +#include using namespace std; using namespace crepe; @@ -28,4 +28,3 @@ TEST_F(ScriptSceneTest, Inactive) { script.set_next_scene(non_default_value); EXPECT_EQ(non_default_value, scene_manager.next_scene); } - diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 6d0d5fb..1d2d6dd 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -1,5 +1,5 @@ -#include #include +#include // stupid hack to allow access to private/protected members under test #define private public @@ -75,4 +75,3 @@ TEST_F(ScriptTest, UpdateInactive) { system.update(); } } - diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 9a71ba7..1bbfdd3 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -1,22 +1,24 @@ #pragma once -#include #include +#include -#include -#include #include #include +#include +#include class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; + public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; class MyScript : public crepe::Script { // NOTE: explicitly stating `public:` is not required on actual scripts + public: MOCK_METHOD(void, init, (), (override)); MOCK_METHOD(void, update, (), (override)); diff --git a/src/test/main.cpp b/src/test/main.cpp index 54f74fd..ed2aed5 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,5 +1,5 @@ -#include #include +#include using namespace crepe; using namespace testing; @@ -26,4 +26,3 @@ int main(int argc, char ** argv) { return RUN_ALL_TESTS(); } - -- cgit v1.2.3 From b8194e02679dc88f5c0a240da83a4700ec5200cf Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 30 Nov 2024 17:27:16 +0100 Subject: add doxygen comments + clean up --- src/crepe/Resource.h | 18 +++++------ src/crepe/api/AudioSource.h | 9 ++++-- src/crepe/facade/Sound.cpp | 34 --------------------- src/crepe/facade/Sound.h | 9 ++++-- src/crepe/facade/SoundContext.cpp | 7 ++--- src/crepe/facade/SoundContext.h | 48 +++++++++++++++++++++++++---- src/crepe/manager/ResourceManager.h | 44 ++++++++++++++++++++++----- src/crepe/system/AudioSystem.cpp | 4 +-- src/crepe/system/AudioSystem.h | 29 ++++++++++++++++-- src/crepe/util/Private.h | 60 +++++++++++++++++++++++++++++++++++-- src/crepe/util/Private.hpp | 2 +- src/test/AudioTest.cpp | 16 +++++----- 12 files changed, 197 insertions(+), 83 deletions(-) (limited to 'src/test') diff --git a/src/crepe/Resource.h b/src/crepe/Resource.h index a0c8859..a2d65df 100644 --- a/src/crepe/Resource.h +++ b/src/crepe/Resource.h @@ -6,21 +6,19 @@ class ResourceManager; class Asset; /** - * Resource is an interface class used to represent a (deserialized) game - * resource (e.g. textures, sounds). + * \brief Resource interface + * + * Resource is an interface class used to represent a (deserialized) game resource (e.g. + * textures, sounds). Resources are always created from \ref Asset "assets" by ResourceManager. + * + * The game programmer has the ability to use the ResourceManager to keep instances of concrete + * resources between scenes, preventing them from being reinstantiated during a scene + * transition. */ class Resource { public: Resource(const Asset & src); virtual ~Resource() = default; - -private: - /** - * The resource manager uses \c clone to create new instances of the concrete - * resource class. This may be used to inherit references to classes that - * would otherwise need to be implemented as singletons. - */ - friend class ResourceManager; }; } // namespace crepe diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 63b4bc4..330e8e1 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -17,16 +17,19 @@ class AudioSource : public Component { friend class AudioSystem; protected: + /** + * \param source Sound sample to load + */ AudioSource(game_object_id_t id, const Asset & source); - //! Only ComponentManager can create components + //! Only ComponentManager creates components friend class ComponentManager; public: - // But std::unique_ptr needs to be able to destoy this component again + // std::unique_ptr needs to be able to destoy this component virtual ~AudioSource() = default; public: - //! Start or resume this audio source + //! Start this audio source void play(bool looping = false); //! Stop this audio source void stop(); diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 33a0c47..ad50637 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -2,7 +2,6 @@ #include "../util/Log.h" #include "Sound.h" -#include "SoundContext.h" using namespace crepe; using namespace std; @@ -12,36 +11,3 @@ Sound::Sound(const Asset & src) : Resource(src) { dbg_trace(); } Sound::~Sound() { dbg_trace(); } - -// void Sound::play(SoundContext & ctx) { -// if (ctx.engine.getPause(this->handle)) { -// // resume if paused -// ctx.engine.setPause(this->handle, false); -// } else { -// // or start new sound -// this->handle = ctx.engine.play(this->sample, this->volume); -// ctx.engine.setLooping(this->handle, this->looping); -// } -// } -// -// void Sound::pause(SoundContext & ctx) { -// if (ctx.engine.getPause(this->handle)) return; -// ctx.engine.setPause(this->handle, true); -// } -// -// void Sound::rewind(SoundContext & ctx) { -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.seek(this->handle, 0); -// } -// -// void Sound::set_volume(SoundContext & ctx, float volume) { -// this->volume = volume; -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.setVolume(this->handle, this->volume); -// } -// -// void Sound::set_looping(SoundContext & ctx, bool looping) { -// this->looping = looping; -// if (!ctx.engine.isValidVoiceHandle(this->handle)) return; -// ctx.engine.setLooping(this->handle, this->looping); -// } diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index 35bccdb..a78a2a7 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -12,21 +12,24 @@ class SoundContext; /** * \brief Sound resource facade * - * This class is a wrapper around a \c SoLoud::Wav instance, which holds a - * single sample. It is part of the sound facade. + * This class is a wrapper around a \c SoLoud::Wav instance, which holds a single sample. It is + * part of the sound facade. */ class Sound : public Resource { public: Sound(const Asset & src); ~Sound(); // dbg_trace + //! Voice handle wrapper struct Handle { + //! Voice handle (soloud), used by SoundContext SoLoud::handle handle; }; private: + //! Deserialized resource (soloud) SoLoud::Wav sample; - + //! SoundContext uses \c sample friend class SoundContext; }; diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 470b3cc..8bd7e74 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -17,17 +17,16 @@ SoundContext::~SoundContext() { Sound::Handle SoundContext::play(Sound & resource) { return { - .handle = this->engine.play(resource.sample, this->default_volume), + .handle = this->engine.play(resource.sample, 1.0f), }; } void SoundContext::stop(Sound::Handle & handle) { this->engine.stop(handle.handle); } -void SoundContext::set_volume(Sound & resource, Sound::Handle & handle, float volume) { +void SoundContext::set_volume(Sound::Handle & handle, float volume) { this->engine.setVolume(handle.handle, volume); - this->default_volume = volume; } -void SoundContext::set_loop(Sound & resource, Sound::Handle & handle, bool loop) { +void SoundContext::set_loop(Sound::Handle & handle, bool loop) { this->engine.setLooping(handle.handle, loop); } diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index c651cd5..3bc8be5 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -11,8 +11,8 @@ namespace crepe { /** * \brief Sound engine facade * - * This class is a wrapper around a \c SoLoud::Soloud instance, which provides - * the methods for playing \c Sound instances. It is part of the sound facade. + * This class is a wrapper around a \c SoLoud::Soloud instance, which provides the methods for + * playing \c Sound instances. It is part of the sound facade. */ class SoundContext { public: @@ -24,15 +24,51 @@ public: SoundContext & operator=(const SoundContext &) = delete; SoundContext & operator=(SoundContext &&) = delete; + /** + * \brief Play a sample + * + * Plays a Sound from the beginning of the sample and returns a handle to control it later. + * + * \param resource Sound instance to play + * + * \returns Handle to control this voice + */ virtual Sound::Handle play(Sound & resource); - virtual void stop(Sound::Handle &); - virtual void set_volume(Sound &, Sound::Handle &, float); - virtual void set_loop(Sound &, Sound::Handle &, bool); + /** + * \brief Stop a voice immediately if it is still playing + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + */ + virtual void stop(Sound::Handle & handle); + /** + * \brief Change the volume of a voice + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + * \param volume New gain value (0=silent, 1=default) + */ + virtual void set_volume(Sound::Handle & handle, float volume); + /** + * \brief Set the looping behavior of a voice + * + * \note This function does nothing if the handle is invalid or if the sound is already + * stopped / finished playing. + * + * \param handle Voice handle returned by SoundContext::play + * \param loop Looping behavior (false=oneshot, true=loop) + */ + virtual void set_loop(Sound::Handle & handle, bool loop); private: + //! Abstracted class SoLoud::Soloud engine; - float default_volume = 1.0f; + //! Config reference Config & config = Config::get_instance(); }; diff --git a/src/crepe/manager/ResourceManager.h b/src/crepe/manager/ResourceManager.h index e7e6abc..84b275d 100644 --- a/src/crepe/manager/ResourceManager.h +++ b/src/crepe/manager/ResourceManager.h @@ -11,13 +11,11 @@ namespace crepe { /** - * \brief The ResourceManager is responsible for storing and managing assets over - * multiple scenes. + * \brief Owner of concrete Resource instances * - * The ResourceManager ensures that assets are loaded once and can be accessed - * across different scenes. It caches assets to avoid reloading them every time - * a scene is loaded. Assets are retained in memory until the ResourceManager is - * destroyed, at which point the cached assets are cleared. + * 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: @@ -25,21 +23,53 @@ public: virtual ~ResourceManager(); // dbg_trace private: + //! Cache entry struct CacheEntry { + //! Concrete resource instance std::unique_ptr resource = nullptr; + //! Prevent ResourceManager::clear from removing this entry bool persistent = false; }; - //! A cache that holds all the assets, accessible by their file path, over multiple scenes. + //! Internal cache std::unordered_map 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 Resource & get(const Asset & asset); + //! Clear non-persistent resources from cache void clear(); + //! Clear all resources from cache regardless of persistence void clear_all(); }; diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 0696b34..26913c0 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -52,10 +52,10 @@ void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, component.oneshot_stop = false; } if (component.volume != data.last_volume) { - context.set_volume(resource, data.handle, component.volume); + context.set_volume(data.handle, component.volume); } if (component.loop != data.last_loop) { - context.set_loop(resource, data.handle, component.loop); + context.set_loop(data.handle, component.loop); } } diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index c941470..4d21883 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -14,9 +14,7 @@ public: void update() override; private: - /** - * \brief Private data stored by AudioSystem on AudioSource component - */ + //! Private data stored by AudioSystem on AudioSource component struct ComponentPrivate { //! This sample's voice handle Sound::Handle handle; @@ -31,14 +29,39 @@ private: //! \} }; + /** + * \brief Update `last_*` members of \c data + * + * Copies all component properties stored for comparison between AudioSystem::update() calls + * + * \param component Source properties + * \param data Destination properties + */ void update_last(const AudioSource & component, ComponentPrivate & data); + /** + * \brief Compare update component + * + * Compares properties of \c component and \c data, and calls SoundContext functions where + * applicable. + * + * \param component AudioSource component to update + * \param data AudioSource's private data + * \param resource Sound instance for AudioSource's Asset + */ void diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource); protected: + /** + * \brief Get SoundContext + * + * SoundContext is retrieved through this function instead of being a direct member of + * AudioSystem to aid with testability. + */ virtual SoundContext & get_context(); private: + //! Actually stores SoundContext if the base AudioSystem::get_context implementation is used Private context; }; diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h index 62a2e1a..d725a5e 100644 --- a/src/crepe/util/Private.h +++ b/src/crepe/util/Private.h @@ -5,26 +5,82 @@ namespace crepe { +/** + * \brief Utility for storing type hidden from user + * + * This class can be used to store types which cannot be used in the API directly due to header + * distribution limitations. This class is similar to `std::any`, but provides a method for + * retrieving a mutable reference to the stored object. + */ class Private { public: Private() = default; ~Private(); + /** + * \name Copy + * + * \note These functions do not do anything, resulting in `*this` being an empty (default) + * instance. + * + * \{ + */ Private(const Private &); - Private(Private &&); Private & operator=(const Private &); + //! \} + /** + * \name Move + * + * These functions actually move the stored type if present. + * + * \{ + */ + Private(Private &&); Private & operator=(Private &&); + //! \} + /** + * \brief Get the stored object + * + * \tparam T Type of stored object + * + * \returns Mutable reference to stored object + * + * \throws std::out_of_range if this instance does not contain any object + * \throws std::logic_error if the stored type and requested type differ + */ template - T & get(); + T & get() const; + /** + * \brief Create and store an arbitrary object + * + * \tparam T Type of object + * \tparam Args Perfect forwarding arguments + * \param args Perfect forwarding arguments + * + * All arguments to this function are forwarded using `std::forward` to the constructor of T. + * + * \returns Mutable reference to stored object + * + * \note If this instance already contained an object, this function implicitly destroys the + * previous object. + */ template T & set(Args &&... args); + /** + * \brief Check if this instance contains an object + * + * \returns `true` if this instance is empty, `false` if it contains an object + */ bool empty() const noexcept; private: + //! Wrapper for destructor call of stored object type std::function destructor; + //! Stored object's type std::type_index type = typeid(void); + //! Stored object void * instance = nullptr; }; diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp index 3a87a9f..b2174c0 100644 --- a/src/crepe/util/Private.hpp +++ b/src/crepe/util/Private.hpp @@ -18,7 +18,7 @@ T & Private::set(Args &&... args) { } template -T & Private::get() { +T & Private::get() const { using namespace std; if (this->empty()) throw out_of_range("Private: get() called on empty object"); type_index requested_type = typeid(T); diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 14f57bd..7644ab7 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -18,8 +18,8 @@ private: public: MOCK_METHOD(Sound::Handle, play, (Sound & resource), (override)); MOCK_METHOD(void, stop, (Sound::Handle &), (override)); - MOCK_METHOD(void, set_volume, (Sound &, Sound::Handle &, float), (override)); - MOCK_METHOD(void, set_loop, (Sound &, Sound::Handle &, bool), (override)); + MOCK_METHOD(void, set_volume, (Sound::Handle &, float), (override)); + MOCK_METHOD(void, set_loop, (Sound::Handle &, bool), (override)); }; class TestAudioSystem : public AudioSystem { @@ -48,8 +48,8 @@ public: TEST_F(AudioTest, Default) { EXPECT_CALL(context, play(_)).Times(0); EXPECT_CALL(context, stop(_)).Times(0); - EXPECT_CALL(context, set_volume(_, _, _)).Times(0); - EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); system.update(); } @@ -95,14 +95,14 @@ TEST_F(AudioTest, Volume) { { InSequence seq; - EXPECT_CALL(context, set_volume(_, _, _)).Times(0); + EXPECT_CALL(context, set_volume(_, _)).Times(0); component.volume += 0.2; } { InSequence seq; - EXPECT_CALL(context, set_volume(_, _, component.volume)).Times(1); + EXPECT_CALL(context, set_volume(_, component.volume)).Times(1); system.update(); } } @@ -113,14 +113,14 @@ TEST_F(AudioTest, Looping) { { InSequence seq; - EXPECT_CALL(context, set_loop(_, _, _)).Times(0); + EXPECT_CALL(context, set_loop(_, _)).Times(0); component.loop = !component.loop; } { InSequence seq; - EXPECT_CALL(context, set_loop(_, _, component.loop)).Times(1); + EXPECT_CALL(context, set_loop(_, component.loop)).Times(1); system.update(); } } -- cgit v1.2.3 From 7a8657dfe019104aced61a5b63e63f61ad919f7a Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Mon, 2 Dec 2024 16:13:08 +0100 Subject: remove `Private` --- src/crepe/api/AudioSource.h | 15 +++- src/crepe/facade/Sound.h | 6 -- src/crepe/facade/SoundContext.cpp | 23 +++--- src/crepe/facade/SoundContext.h | 15 +++- src/crepe/facade/SoundHandle.h | 13 ++++ src/crepe/system/AudioSystem.cpp | 44 +++++------ src/crepe/system/AudioSystem.h | 29 ++----- src/crepe/util/CMakeLists.txt | 3 - src/crepe/util/Private.cpp | 27 ------- src/crepe/util/Private.h | 89 ---------------------- src/crepe/util/Private.hpp | 31 -------- src/test/AudioTest.cpp | 8 +- src/test/CMakeLists.txt | 1 - src/test/PrivateTest.cpp | 155 -------------------------------------- 14 files changed, 78 insertions(+), 381 deletions(-) create mode 100644 src/crepe/facade/SoundHandle.h delete mode 100644 src/crepe/util/Private.cpp delete mode 100644 src/crepe/util/Private.h delete mode 100644 src/crepe/util/Private.hpp delete mode 100644 src/test/PrivateTest.cpp (limited to 'src/test') diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 330e8e1..7c1f161 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -2,7 +2,7 @@ #include "../Component.h" #include "../types.h" -#include "../util/Private.h" +#include "../facade/SoundHandle.h" #include "Asset.h" #include "GameObject.h" @@ -59,10 +59,17 @@ private: //! Stop this sample bool oneshot_stop = false; //! \} + /** + * \name State diffing variables + * \{ + */ + typeof(active) last_active = false; + typeof(volume) last_volume = volume; + typeof(loop) last_loop = loop; + //! \} + //! This source's voice handle + SoundHandle voice{}; -private: - //! AudioSystem::ComponentPrivate - Private private_data; }; } // namespace crepe diff --git a/src/crepe/facade/Sound.h b/src/crepe/facade/Sound.h index a78a2a7..85d141b 100644 --- a/src/crepe/facade/Sound.h +++ b/src/crepe/facade/Sound.h @@ -20,12 +20,6 @@ public: Sound(const Asset & src); ~Sound(); // dbg_trace - //! Voice handle wrapper - struct Handle { - //! Voice handle (soloud), used by SoundContext - SoLoud::handle handle; - }; - private: //! Deserialized resource (soloud) SoLoud::Wav sample; diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index 8bd7e74..d18afc6 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -15,18 +15,23 @@ SoundContext::~SoundContext() { this->engine.deinit(); } -Sound::Handle SoundContext::play(Sound & resource) { - return { - .handle = this->engine.play(resource.sample, 1.0f), - }; +SoundHandle SoundContext::play(Sound & resource) { + SoLoud::handle real_handle = this->engine.play(resource.sample, 1.0f); + SoundHandle handle = this->next_handle; + this->registry[handle] = real_handle; + this->next_handle++; + return handle; } -void SoundContext::stop(Sound::Handle & handle) { this->engine.stop(handle.handle); } +void SoundContext::stop(const SoundHandle & handle) { + this->engine.stop(this->registry[handle]); +} -void SoundContext::set_volume(Sound::Handle & handle, float volume) { - this->engine.setVolume(handle.handle, volume); +void SoundContext::set_volume(const SoundHandle & handle, float volume) { + this->engine.setVolume(this->registry[handle], volume); } -void SoundContext::set_loop(Sound::Handle & handle, bool loop) { - this->engine.setLooping(handle.handle, loop); +void SoundContext::set_loop(const SoundHandle & handle, bool loop) { + this->engine.setLooping(this->registry[handle], loop); } + diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index 3bc8be5..102f928 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -4,6 +4,7 @@ #include "../api/Config.h" +#include "SoundHandle.h" #include "Sound.h" namespace crepe { @@ -33,7 +34,7 @@ public: * * \returns Handle to control this voice */ - virtual Sound::Handle play(Sound & resource); + virtual SoundHandle play(Sound & resource); /** * \brief Stop a voice immediately if it is still playing * @@ -42,7 +43,7 @@ public: * * \param handle Voice handle returned by SoundContext::play */ - virtual void stop(Sound::Handle & handle); + virtual void stop(const SoundHandle & handle); /** * \brief Change the volume of a voice * @@ -52,7 +53,7 @@ public: * \param handle Voice handle returned by SoundContext::play * \param volume New gain value (0=silent, 1=default) */ - virtual void set_volume(Sound::Handle & handle, float volume); + virtual void set_volume(const SoundHandle & handle, float volume); /** * \brief Set the looping behavior of a voice * @@ -62,7 +63,7 @@ public: * \param handle Voice handle returned by SoundContext::play * \param loop Looping behavior (false=oneshot, true=loop) */ - virtual void set_loop(Sound::Handle & handle, bool loop); + virtual void set_loop(const SoundHandle & handle, bool loop); private: //! Abstracted class @@ -70,6 +71,12 @@ private: //! Config reference Config & config = Config::get_instance(); + + //! Sound handle registry + std::unordered_map registry; + //! Unique handle counter + SoundHandle next_handle = 0; + }; } // namespace crepe diff --git a/src/crepe/facade/SoundHandle.h b/src/crepe/facade/SoundHandle.h new file mode 100644 index 0000000..131d28c --- /dev/null +++ b/src/crepe/facade/SoundHandle.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace crepe { + +/** + * \brief Voice handle returned by + */ +typedef size_t SoundHandle; + +} + diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index 26913c0..c1cde8b 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -16,56 +16,50 @@ void AudioSystem::update() { for (AudioSource & component : components) { Sound & resource = resource_manager.get(component.source); - if (component.private_data.empty()) { - auto & data = component.private_data.set(); - this->update_last(component, data); - data.last_active = false; - } - auto & data = component.private_data.get(); - - this->diff_update(component, data, resource); + this->diff_update(component, resource); - this->update_last(component, data); + this->update_last(component); } } -void AudioSystem::diff_update(AudioSource & component, ComponentPrivate & data, - Sound & resource) { +void AudioSystem::diff_update(AudioSource & component, Sound & resource) { SoundContext & context = this->get_context(); - if (component.active != data.last_active) { + if (component.active != component.last_active) { if (component.active) { component.oneshot_play = component.play_on_awake; } else { - context.stop(data.handle); + context.stop(component.voice); return; } } if (!component.active) return; if (component.oneshot_play) { - data.handle = context.play(resource); + component.voice = context.play(resource); component.oneshot_play = false; } if (component.oneshot_stop) { - context.stop(data.handle); + context.stop(component.voice); component.oneshot_stop = false; } - if (component.volume != data.last_volume) { - context.set_volume(data.handle, component.volume); + if (component.volume != component.last_volume) { + context.set_volume(component.voice, component.volume); } - if (component.loop != data.last_loop) { - context.set_loop(data.handle, component.loop); + if (component.loop != component.last_loop) { + context.set_loop(component.voice, component.loop); } } -void AudioSystem::update_last(const AudioSource & component, ComponentPrivate & data) { - data.last_active = component.active; - data.last_loop = component.loop; - data.last_volume = component.volume; +void AudioSystem::update_last(AudioSource & component) { + component.last_active = component.active; + component.last_loop = component.loop; + component.last_volume = component.volume; } SoundContext & AudioSystem::get_context() { - if (this->context.empty()) this->context.set(); - return this->context.get(); + if (this->context == nullptr) + this->context = make_unique(); + return *this->context.get(); } + diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 4d21883..2ddc443 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -14,30 +14,14 @@ public: void update() override; private: - //! Private data stored by AudioSystem on AudioSource component - struct ComponentPrivate { - //! This sample's voice handle - Sound::Handle handle; - - /** - * \name State diffing variables - * \{ - */ - typeof(AudioSource::active) last_active; - typeof(AudioSource::volume) last_volume; - typeof(AudioSource::loop) last_loop; - //! \} - }; - /** - * \brief Update `last_*` members of \c data + * \brief Update `last_*` members of \c component * * Copies all component properties stored for comparison between AudioSystem::update() calls * - * \param component Source properties - * \param data Destination properties + * \param component AudioSource component to update */ - void update_last(const AudioSource & component, ComponentPrivate & data); + void update_last(AudioSource & component); /** * \brief Compare update component @@ -46,10 +30,9 @@ private: * applicable. * * \param component AudioSource component to update - * \param data AudioSource's private data * \param resource Sound instance for AudioSource's Asset */ - void diff_update(AudioSource & component, ComponentPrivate & data, Sound & resource); + void diff_update(AudioSource & component, Sound & resource); protected: /** @@ -61,8 +44,8 @@ protected: virtual SoundContext & get_context(); private: - //! Actually stores SoundContext if the base AudioSystem::get_context implementation is used - Private context; + //! SoundContext + std::unique_ptr context = nullptr; }; } // namespace crepe diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index f49d851..94ed906 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,7 +1,6 @@ target_sources(crepe PUBLIC LogColor.cpp Log.cpp - Private.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -12,7 +11,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Proxy.hpp OptionalRef.h OptionalRef.hpp - Private.h - Private.hpp ) diff --git a/src/crepe/util/Private.cpp b/src/crepe/util/Private.cpp deleted file mode 100644 index 262620d..0000000 --- a/src/crepe/util/Private.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Private.h" - -using namespace crepe; - -bool Private::empty() const noexcept { return this->instance == nullptr; } - -Private::~Private() { - if (this->instance == nullptr) return; - this->destructor(this->instance); -} - -Private::Private(Private && other) { *this = std::move(other); } - -Private & Private::operator=(Private && other) { - // TODO: ideally this function checks for self-assignment - this->instance = other.instance; - this->destructor = other.destructor; - this->type = other.type; - - other.instance = nullptr; - other.destructor = [](void *) {}; - - return *this; -} - -Private::Private(const Private & other) {} -Private & Private::operator=(const Private & other) { return *this; } diff --git a/src/crepe/util/Private.h b/src/crepe/util/Private.h deleted file mode 100644 index d725a5e..0000000 --- a/src/crepe/util/Private.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include -#include - -namespace crepe { - -/** - * \brief Utility for storing type hidden from user - * - * This class can be used to store types which cannot be used in the API directly due to header - * distribution limitations. This class is similar to `std::any`, but provides a method for - * retrieving a mutable reference to the stored object. - */ -class Private { -public: - Private() = default; - ~Private(); - /** - * \name Copy - * - * \note These functions do not do anything, resulting in `*this` being an empty (default) - * instance. - * - * \{ - */ - Private(const Private &); - Private & operator=(const Private &); - //! \} - /** - * \name Move - * - * These functions actually move the stored type if present. - * - * \{ - */ - Private(Private &&); - Private & operator=(Private &&); - //! \} - - /** - * \brief Get the stored object - * - * \tparam T Type of stored object - * - * \returns Mutable reference to stored object - * - * \throws std::out_of_range if this instance does not contain any object - * \throws std::logic_error if the stored type and requested type differ - */ - template - T & get() const; - - /** - * \brief Create and store an arbitrary object - * - * \tparam T Type of object - * \tparam Args Perfect forwarding arguments - * \param args Perfect forwarding arguments - * - * All arguments to this function are forwarded using `std::forward` to the constructor of T. - * - * \returns Mutable reference to stored object - * - * \note If this instance already contained an object, this function implicitly destroys the - * previous object. - */ - template - T & set(Args &&... args); - - /** - * \brief Check if this instance contains an object - * - * \returns `true` if this instance is empty, `false` if it contains an object - */ - bool empty() const noexcept; - -private: - //! Wrapper for destructor call of stored object type - std::function destructor; - //! Stored object's type - std::type_index type = typeid(void); - //! Stored object - void * instance = nullptr; -}; - -} // namespace crepe - -#include "Private.hpp" diff --git a/src/crepe/util/Private.hpp b/src/crepe/util/Private.hpp deleted file mode 100644 index b2174c0..0000000 --- a/src/crepe/util/Private.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -#include "Private.h" - -namespace crepe { - -template -T & Private::set(Args &&... args) { - if (!this->empty()) this->destructor(this->instance); - T * instance = new T(std::forward(args)...); - this->instance = static_cast(instance); - this->destructor = [](void * instance) { delete static_cast(instance); }; - this->type = typeid(T); - return *instance; -} - -template -T & Private::get() const { - using namespace std; - if (this->empty()) throw out_of_range("Private: get() called on empty object"); - type_index requested_type = typeid(T); - if (this->type != requested_type) - throw logic_error(format("Private: get() called with [T = {}] (actual is [T = {}])", - requested_type.name(), this->type.name())); - return *static_cast(this->instance); -} - -} // namespace crepe diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 7644ab7..774fdb8 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -16,10 +16,10 @@ class AudioTest : public Test { private: class TestSoundContext : public SoundContext { public: - MOCK_METHOD(Sound::Handle, play, (Sound & resource), (override)); - MOCK_METHOD(void, stop, (Sound::Handle &), (override)); - MOCK_METHOD(void, set_volume, (Sound::Handle &, float), (override)); - MOCK_METHOD(void, set_loop, (Sound::Handle &, bool), (override)); + MOCK_METHOD(SoundHandle, play, (Sound & resource), (override)); + MOCK_METHOD(void, stop, (const SoundHandle &), (override)); + MOCK_METHOD(void, set_volume, (const SoundHandle &, float), (override)); + MOCK_METHOD(void, set_loop, (const SoundHandle &, bool), (override)); }; class TestAudioSystem : public AudioSystem { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 8c4b855..4174926 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -16,5 +16,4 @@ target_sources(test_main PUBLIC Vector2Test.cpp ScriptEventTest.cpp ScriptSceneTest.cpp - PrivateTest.cpp ) diff --git a/src/test/PrivateTest.cpp b/src/test/PrivateTest.cpp deleted file mode 100644 index 454789e..0000000 --- a/src/test/PrivateTest.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include - -#include - -using namespace std; -using namespace crepe; -using namespace testing; - -class PrivateTest : public Test { -public: - static unsigned constructors; - static unsigned destructors; - - void SetUp() override { - PrivateTest::constructors = 0; - PrivateTest::destructors = 0; - } - - class TestClass { - public: - TestClass() { PrivateTest::constructors++; } - ~TestClass() { PrivateTest::destructors++; } - }; - class Unrelated {}; -}; -unsigned PrivateTest::constructors; -unsigned PrivateTest::destructors; - -TEST_F(PrivateTest, Empty) { - { Private foo; } - - EXPECT_EQ(PrivateTest::constructors, 0); - EXPECT_EQ(PrivateTest::destructors, 0); -} - -TEST_F(PrivateTest, WithObject) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - } - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 1); -} - -TEST_F(PrivateTest, EmptyException) { - Private foo; - EXPECT_THROW(foo.get(), std::out_of_range); - - foo.set(); - EXPECT_NO_THROW(foo.get()); -} - -TEST_F(PrivateTest, IncorrectTypeException) { - Private foo; - foo.set(); - - EXPECT_THROW(foo.get(), std::logic_error); - EXPECT_NO_THROW(foo.get()); -} - -TEST_F(PrivateTest, MoveConstructor) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - - Private bar(std::move(foo)); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - } - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 1); -} - -TEST_F(PrivateTest, MoveOperator) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - - Private bar = std::move(foo); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - } - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 1); -} - -TEST_F(PrivateTest, CopyConstructor) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - - Private bar(foo); - - EXPECT_TRUE(bar.empty()); - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - } - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 1); -} - -TEST_F(PrivateTest, CopyOperator) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - - Private bar = foo; - - EXPECT_TRUE(bar.empty()); - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - } - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 1); -} - -TEST_F(PrivateTest, DoubleAssignment) { - { - Private foo; - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 1); - EXPECT_EQ(PrivateTest::destructors, 0); - - foo.set(); - - EXPECT_EQ(PrivateTest::constructors, 2); - EXPECT_EQ(PrivateTest::destructors, 1); - } - - EXPECT_EQ(PrivateTest::constructors, 2); - EXPECT_EQ(PrivateTest::destructors, 2); -} -- cgit v1.2.3 From 803771dfc4fb5b9144d551a91b77a5a4ec8f21b6 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 5 Dec 2024 09:51:36 +0100 Subject: add unit test --- src/test/ResourceManagerTest.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'src/test') diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index b6be3c0..0789ef0 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -17,6 +17,10 @@ class ResourceManagerTest : public Test { public: ResourceManager resource_manager{mediator}; + class Unrelated : public Resource { + using Resource::Resource; + }; + Asset asset_a{"asset/texture/img.png"}; Asset asset_b{"asset/texture/ERROR.png"}; @@ -69,3 +73,14 @@ TEST_F(ResourceManagerTest, Persistent) { resource_manager.clear_all(); EXPECT_EQ(TestResource::instances, 0); } + +TEST_F(ResourceManagerTest, UnmatchedType) { + EXPECT_NO_THROW({ + resource_manager.get(asset_a); + }); + + EXPECT_THROW({ + resource_manager.get(asset_a); + }, runtime_error); +} + -- cgit v1.2.3 From 1e9e564f3806d07c7b0dc445c4ae2e738350fc83 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 5 Dec 2024 17:12:56 +0100 Subject: `make format` --- src/crepe/api/AudioSource.h | 3 +-- src/crepe/facade/SoundContext.cpp | 1 - src/crepe/facade/SoundContext.h | 3 +-- src/crepe/facade/SoundHandle.h | 3 +-- src/crepe/system/AudioSystem.cpp | 4 +--- src/test/ResourceManagerTest.cpp | 9 ++------- 6 files changed, 6 insertions(+), 17 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/AudioSource.h b/src/crepe/api/AudioSource.h index 7c1f161..b20e490 100644 --- a/src/crepe/api/AudioSource.h +++ b/src/crepe/api/AudioSource.h @@ -1,8 +1,8 @@ #pragma once #include "../Component.h" -#include "../types.h" #include "../facade/SoundHandle.h" +#include "../types.h" #include "Asset.h" #include "GameObject.h" @@ -69,7 +69,6 @@ private: //! \} //! This source's voice handle SoundHandle voice{}; - }; } // namespace crepe diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index d18afc6..b1f8cb3 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -34,4 +34,3 @@ void SoundContext::set_volume(const SoundHandle & handle, float volume) { void SoundContext::set_loop(const SoundHandle & handle, bool loop) { this->engine.setLooping(this->registry[handle], loop); } - diff --git a/src/crepe/facade/SoundContext.h b/src/crepe/facade/SoundContext.h index 102f928..d986c59 100644 --- a/src/crepe/facade/SoundContext.h +++ b/src/crepe/facade/SoundContext.h @@ -4,8 +4,8 @@ #include "../api/Config.h" -#include "SoundHandle.h" #include "Sound.h" +#include "SoundHandle.h" namespace crepe { @@ -76,7 +76,6 @@ private: std::unordered_map registry; //! Unique handle counter SoundHandle next_handle = 0; - }; } // namespace crepe diff --git a/src/crepe/facade/SoundHandle.h b/src/crepe/facade/SoundHandle.h index 131d28c..b7925fc 100644 --- a/src/crepe/facade/SoundHandle.h +++ b/src/crepe/facade/SoundHandle.h @@ -9,5 +9,4 @@ namespace crepe { */ typedef size_t SoundHandle; -} - +} // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index c1cde8b..b2c1dc6 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -58,8 +58,6 @@ void AudioSystem::update_last(AudioSource & component) { } SoundContext & AudioSystem::get_context() { - if (this->context == nullptr) - this->context = make_unique(); + if (this->context == nullptr) this->context = make_unique(); return *this->context.get(); } - diff --git a/src/test/ResourceManagerTest.cpp b/src/test/ResourceManagerTest.cpp index 0789ef0..44a5921 100644 --- a/src/test/ResourceManagerTest.cpp +++ b/src/test/ResourceManagerTest.cpp @@ -75,12 +75,7 @@ TEST_F(ResourceManagerTest, Persistent) { } TEST_F(ResourceManagerTest, UnmatchedType) { - EXPECT_NO_THROW({ - resource_manager.get(asset_a); - }); + EXPECT_NO_THROW({ resource_manager.get(asset_a); }); - EXPECT_THROW({ - resource_manager.get(asset_a); - }, runtime_error); + EXPECT_THROW({ resource_manager.get(asset_a); }, runtime_error); } - -- cgit v1.2.3 From f4824f5e7e6cee12bec602f3240770945a73d043 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 6 Dec 2024 17:13:59 +0100 Subject: add more fetching functions to ComponentManager --- src/crepe/api/Script.h | 7 +++++ src/crepe/manager/ComponentManager.cpp | 14 ++++++++++ src/crepe/manager/ComponentManager.h | 18 ++++++++++-- src/crepe/manager/ComponentManager.hpp | 51 ++++++++++++++++++++-------------- src/test/ECSTest.cpp | 34 +++++++++++++++++++++++ 5 files changed, 101 insertions(+), 23 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 1b339b0..5862bae 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -84,6 +84,13 @@ protected: template RefVector get_components() const; + /** + * \copydoc ComponentManager::get_components_by_id + * \see ComponentManager::get_components_by_id + */ + template + RefVector get_components_by_id(game_object_id_t id) const; + /** * \brief Log a message using Log::logf * diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 80cf8b4..44774d9 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,16 @@ 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 ComponentManager::get_objects_by_name(const string & name) const { + return this->get_objects_by_predicate([name](const Metadata & data) { + return data.name == name; + }); +} + +set ComponentManager::get_objects_by_tag(const string & tag) const { + return this->get_objects_by_predicate([tag](const Metadata & data) { + return data.tag == tag; + }); +} + diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index ad37586..5f2cf3c 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "../Component.h" #include "../types.h" @@ -136,6 +137,17 @@ public: RefVector get_components_by_type() const; private: + template + std::set get_objects_by_predicate(const std::function & pred) const; + + std::set get_objects_by_name(const std::string & name) const; + std::set get_objects_by_tag(const std::string & tag) const; + +private: + template + using by_type = std::unordered_map; + template + using by_id_index = std::vector; /** * \brief The components * @@ -146,8 +158,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>>> - components; + by_type>>> components; //! Persistent flag for each GameObject std::unordered_map persistent; @@ -156,6 +167,9 @@ private: game_object_id_t next_id = 0; }; +// template <> +// RefVector ComponentManager::get_components_by_id(game_object_id_t id) const; + } // namespace crepe #include "ComponentManager.hpp" diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index ffb38ec..25c2747 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -95,32 +95,24 @@ template RefVector 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 component_vector; - - if (this->components.find(type) == this->components.end()) return component_vector; - - // Get the correct vector<> - const vector>> & 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; + static_assert(is_base_of::value, + "get_components_by_id must recieve a derivative class of Component"); - // Loop trough the whole vector<> - for (const unique_ptr & component_ptr : component_array[id]) { - // Cast the unique_ptr to a raw pointer - T * casted_component = static_cast(component_ptr.get()); + type_index type = typeid(T); + if (!this->components.contains(type)) return {}; - if (casted_component == nullptr) continue; + const by_id_index>> & components_by_id = this->components.at(type); + if (id >= components_by_id.size()) return {}; - // Add the dereferenced raw pointer to the vector<> - component_vector.push_back(*casted_component); + RefVector out = {}; + const vector> & 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(component)); } - return component_vector; + return out; } template @@ -158,4 +150,21 @@ RefVector ComponentManager::get_components_by_type() const { return component_vector; } +template +std::set ComponentManager::get_objects_by_predicate(const std::function & pred) const { + using namespace std; + + set objects = {}; + RefVector components = this->get_components_by_type(); + + for (const T & component : components) { + game_object_id_t id = dynamic_cast(component).game_object_id; + if (objects.contains(id)) continue; + if (!pred(component)) continue; + objects.insert(id); + } + + return objects; +} + } // namespace crepe diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index 3e6c61c..a169b3b 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -1,6 +1,7 @@ #include #define protected public +#define private public #include #include @@ -387,3 +388,36 @@ TEST_F(ECSTest, resetPersistent) { EXPECT_EQ(metadata.size(), 0); EXPECT_EQ(transform.size(), 0); } + +TEST_F(ECSTest, GetByName) { + GameObject foo = mgr.new_object("foo"); + GameObject bar = mgr.new_object("bar"); + + { + auto objects = mgr.get_objects_by_name(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_name("foo"); + EXPECT_EQ(objects.size(), 1); + EXPECT_TRUE(objects.contains(foo.id)); + } +} + +TEST_F(ECSTest, GetByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + GameObject bar = mgr.new_object("bar", "common tag"); + + { + auto objects = mgr.get_objects_by_tag(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_objects_by_tag("common tag"); + EXPECT_EQ(objects.size(), 2); + EXPECT_TRUE(objects.contains(foo.id)); + EXPECT_TRUE(objects.contains(bar.id)); + } +} -- cgit v1.2.3 From 90c6bf03e59fdec64f850310bcbff45ae86f69e3 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 14:18:54 +0100 Subject: more script utilities --- src/crepe/api/Script.h | 12 ++++++++ src/crepe/api/Script.hpp | 27 +++++++++++++++--- src/crepe/manager/ComponentManager.h | 20 ++++++++++++++ src/crepe/manager/ComponentManager.hpp | 23 ++++++++++++++++ src/test/CMakeLists.txt | 1 + src/test/ECSTest.cpp | 50 ++++++++++++++++++++++++++++++++-- src/test/ScriptECSTest.cpp | 42 ++++++++++++++++++++++++++++ src/test/ScriptEventTest.cpp | 2 +- src/test/ScriptSceneTest.cpp | 3 +- src/test/ScriptTest.cpp | 3 +- src/test/ScriptTest.h | 2 ++ 11 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 src/test/ScriptECSTest.cpp (limited to 'src/test') diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 5862bae..a040608 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -90,6 +90,18 @@ protected: */ template RefVector get_components_by_id(game_object_id_t id) const; + /** + * \copydoc ComponentManager::get_components_by_name + * \see ComponentManager::get_components_by_name + */ + template + RefVector get_components_by_name(const std::string & name) const; + /** + * \copydoc ComponentManager::get_components_by_tag + * \see ComponentManager::get_components_by_tag + */ + template + RefVector get_components_by_tag(const std::string & tag) const; /** * \brief Log a message using Log::logf diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 45f1ff1..16e0dc5 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,10 +20,7 @@ T & Script::get_component() const { template RefVector Script::get_components() const { - Mediator & mediator = this->mediator; - ComponentManager & mgr = mediator.component_manager; - - return mgr.get_components_by_id(this->game_object_id); + return this->get_components_by_id(this->game_object_id); } template @@ -56,4 +53,26 @@ void Script::subscribe(const EventHandler & callback) { this->subscribe_internal(callback, EventManager::CHANNEL_ALL); } +template +RefVector Script::get_components_by_id(game_object_id_t id) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_id(id); +} +template +RefVector Script::get_components_by_name(const std::string & name) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_name(name); +} +template +RefVector Script::get_components_by_tag(const std::string & tag) const { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + return mgr.get_components_by_tag(tag); +} + } // namespace crepe diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 5f2cf3c..4e53954 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -135,10 +135,30 @@ public: */ template RefVector 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 + RefVector 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 + RefVector get_components_by_tag(const std::string & tag) const; private: template std::set get_objects_by_predicate(const std::function & pred) const; + template + RefVector get_components_by_ids(const std::set & ids) const; std::set get_objects_by_name(const std::string & name) const; std::set get_objects_by_tag(const std::string & tag) const; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index 25c2747..52df368 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -167,4 +167,27 @@ std::set ComponentManager::get_objects_by_predicate(const std: return objects; } +template +RefVector ComponentManager::get_components_by_ids(const std::set & ids) const { + using namespace std; + + RefVector out = {}; + for (game_object_id_t id : ids) { + RefVector components = get_components_by_id(id); + out.insert(out.end(), components.begin(), components.end()); + } + + return out; +} + +template +RefVector ComponentManager::get_components_by_name(const std::string & name) const { + return this->get_components_by_ids(this->get_objects_by_name(name)); +} + +template +RefVector ComponentManager::get_components_by_tag(const std::string & tag) const { + return this->get_components_by_ids(this->get_objects_by_tag(tag)); +} + } // namespace crepe diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index e19d7de..43f564a 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,4 +15,5 @@ target_sources(test_main PUBLIC InputTest.cpp ScriptEventTest.cpp ScriptSceneTest.cpp + ScriptECSTest.cpp ) diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index a169b3b..ed5341e 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -17,6 +17,10 @@ class ECSTest : public ::testing::Test { public: ComponentManager mgr{m}; + + class TestComponent : public Component { + using Component::Component; + }; }; TEST_F(ECSTest, createGameObject) { @@ -389,7 +393,7 @@ TEST_F(ECSTest, resetPersistent) { EXPECT_EQ(transform.size(), 0); } -TEST_F(ECSTest, GetByName) { +TEST_F(ECSTest, IDByName) { GameObject foo = mgr.new_object("foo"); GameObject bar = mgr.new_object("bar"); @@ -405,7 +409,7 @@ TEST_F(ECSTest, GetByName) { } } -TEST_F(ECSTest, GetByTag) { +TEST_F(ECSTest, IDByTag) { GameObject foo = mgr.new_object("foo", "common tag"); GameObject bar = mgr.new_object("bar", "common tag"); @@ -421,3 +425,45 @@ TEST_F(ECSTest, GetByTag) { EXPECT_TRUE(objects.contains(bar.id)); } } + +TEST_F(ECSTest, ComponentsByName) { + GameObject foo = mgr.new_object("foo"); + foo.add_component(); + GameObject bar = mgr.new_object("bar"); + bar.add_component(); + bar.add_component(); + + { + auto objects = mgr.get_components_by_name(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_name("foo"); + EXPECT_EQ(objects.size(), 1); + } + + { + auto objects = mgr.get_components_by_name("bar"); + EXPECT_EQ(objects.size(), 2); + } +} + +TEST_F(ECSTest, ComponentsByTag) { + GameObject foo = mgr.new_object("foo", "common tag"); + foo.add_component(); + GameObject bar = mgr.new_object("bar", "common tag"); + bar.add_component(); + bar.add_component(); + + { + auto objects = mgr.get_components_by_tag(""); + EXPECT_EQ(objects.size(), 0); + } + + { + auto objects = mgr.get_components_by_tag("common tag"); + EXPECT_EQ(objects.size(), 3); + } +} + diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp new file mode 100644 index 0000000..4477e55 --- /dev/null +++ b/src/test/ScriptECSTest.cpp @@ -0,0 +1,42 @@ +#include + +#define protected public + +#include +#include +#include +#include +#include +#include + +#include "ScriptTest.h" + +using namespace std; +using namespace crepe; +using namespace testing; + +class ScriptECSTest : public ScriptTest { +public: + class TestComponent : public Component { + using Component::Component; + }; +}; + +TEST_F(ScriptECSTest, GetOwnComponent) { + MyScript & script = this->script; + Metadata & metadata = script.get_component(); + + EXPECT_EQ(metadata.name, OBJ_NAME); +} + +TEST_F(ScriptECSTest, GetOwnComponents) { + const unsigned COUNT = 4; + + for (unsigned i = 0; i < COUNT; i++) + entity.add_component(); + + MyScript & script = this->script; + RefVector components = script.get_components(); + + EXPECT_EQ(components.size(), COUNT); +} diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index 5da31e7..c1b4028 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -26,7 +26,7 @@ public: class MyEvent : public Event {}; }; -TEST_F(ScriptEventTest, Inactive) { +TEST_F(ScriptEventTest, Default) { BehaviorScript & behaviorscript = this->behaviorscript; MyScript & script = this->script; EventManager & evmgr = this->event_manager; diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 9ee1e52..8e849c1 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -18,7 +18,7 @@ public: class MyScene : public Scene {}; }; -TEST_F(ScriptSceneTest, Inactive) { +TEST_F(ScriptSceneTest, Default) { BehaviorScript & behaviorscript = this->behaviorscript; MyScript & script = this->script; @@ -28,3 +28,4 @@ TEST_F(ScriptSceneTest, Inactive) { script.set_next_scene(non_default_value); EXPECT_EQ(non_default_value, scene_manager.next_scene); } + diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 1d2d6dd..b0b2546 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -6,7 +6,6 @@ #define protected public #include "ScriptTest.h" -#include using namespace std; using namespace crepe; @@ -14,7 +13,6 @@ using namespace testing; void ScriptTest::SetUp() { auto & mgr = this->component_manager; - GameObject entity = mgr.new_object("name"); BehaviorScript & component = entity.add_component(); this->behaviorscript = component; @@ -75,3 +73,4 @@ TEST_F(ScriptTest, UpdateInactive) { system.update(); } } + diff --git a/src/test/ScriptTest.h b/src/test/ScriptTest.h index 1bbfdd3..309e016 100644 --- a/src/test/ScriptTest.h +++ b/src/test/ScriptTest.h @@ -11,10 +11,12 @@ class ScriptTest : public testing::Test { protected: crepe::Mediator mediator; + static constexpr const char * OBJ_NAME = "foo"; public: crepe::ComponentManager component_manager{mediator}; crepe::ScriptSystem system{mediator}; + crepe::GameObject entity = component_manager.new_object(OBJ_NAME); class MyScript : public crepe::Script { // NOTE: explicitly stating `public:` is not required on actual scripts -- cgit v1.2.3 From f3009ab8f0785a54d3fd83c0d758c8ebd901adda Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 14:19:26 +0100 Subject: `make format` --- src/crepe/manager/ComponentManager.cpp | 11 ++++------- src/crepe/manager/ComponentManager.h | 5 +++-- src/crepe/manager/ComponentManager.hpp | 11 +++++++---- src/test/ECSTest.cpp | 1 - src/test/ScriptECSTest.cpp | 5 ++--- src/test/ScriptSceneTest.cpp | 1 - src/test/ScriptTest.cpp | 1 - 7 files changed, 16 insertions(+), 19 deletions(-) (limited to 'src/test') diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index 44774d9..df30d27 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -64,14 +64,11 @@ void ComponentManager::set_persistent(game_object_id_t id, bool persistent) { } set ComponentManager::get_objects_by_name(const string & name) const { - return this->get_objects_by_predicate([name](const Metadata & data) { - return data.name == name; - }); + return this->get_objects_by_predicate( + [name](const Metadata & data) { return data.name == name; }); } set ComponentManager::get_objects_by_tag(const string & tag) const { - return this->get_objects_by_predicate([tag](const Metadata & data) { - return data.tag == tag; - }); + return this->get_objects_by_predicate( + [tag](const Metadata & data) { return data.tag == tag; }); } - diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 4e53954..685cae5 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include #include -#include #include "../Component.h" #include "../types.h" @@ -156,7 +156,8 @@ public: private: template - std::set get_objects_by_predicate(const std::function & pred) const; + std::set + get_objects_by_predicate(const std::function & pred) const; template RefVector get_components_by_ids(const std::set & ids) const; diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp index 52df368..9e70865 100644 --- a/src/crepe/manager/ComponentManager.hpp +++ b/src/crepe/manager/ComponentManager.hpp @@ -101,7 +101,8 @@ RefVector ComponentManager::get_components_by_id(game_object_id_t id) const { type_index type = typeid(T); if (!this->components.contains(type)) return {}; - const by_id_index>> & components_by_id = this->components.at(type); + const by_id_index>> & components_by_id + = this->components.at(type); if (id >= components_by_id.size()) return {}; RefVector out = {}; @@ -151,7 +152,8 @@ RefVector ComponentManager::get_components_by_type() const { } template -std::set ComponentManager::get_objects_by_predicate(const std::function & pred) const { +std::set +ComponentManager::get_objects_by_predicate(const std::function & pred) const { using namespace std; set objects = {}; @@ -168,9 +170,10 @@ std::set ComponentManager::get_objects_by_predicate(const std: } template -RefVector ComponentManager::get_components_by_ids(const std::set & ids) const { +RefVector +ComponentManager::get_components_by_ids(const std::set & ids) const { using namespace std; - + RefVector out = {}; for (game_object_id_t id : ids) { RefVector components = get_components_by_id(id); diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index ed5341e..af2b7b0 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -466,4 +466,3 @@ TEST_F(ECSTest, ComponentsByTag) { EXPECT_EQ(objects.size(), 3); } } - diff --git a/src/test/ScriptECSTest.cpp b/src/test/ScriptECSTest.cpp index 4477e55..1ec33ba 100644 --- a/src/test/ScriptECSTest.cpp +++ b/src/test/ScriptECSTest.cpp @@ -4,8 +4,8 @@ #include #include -#include #include +#include #include #include @@ -32,8 +32,7 @@ TEST_F(ScriptECSTest, GetOwnComponent) { TEST_F(ScriptECSTest, GetOwnComponents) { const unsigned COUNT = 4; - for (unsigned i = 0; i < COUNT; i++) - entity.add_component(); + for (unsigned i = 0; i < COUNT; i++) entity.add_component(); MyScript & script = this->script; RefVector components = script.get_components(); diff --git a/src/test/ScriptSceneTest.cpp b/src/test/ScriptSceneTest.cpp index 8e849c1..2568049 100644 --- a/src/test/ScriptSceneTest.cpp +++ b/src/test/ScriptSceneTest.cpp @@ -28,4 +28,3 @@ TEST_F(ScriptSceneTest, Default) { script.set_next_scene(non_default_value); EXPECT_EQ(non_default_value, scene_manager.next_scene); } - diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index b0b2546..acdae70 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -73,4 +73,3 @@ TEST_F(ScriptTest, UpdateInactive) { system.update(); } } - -- cgit v1.2.3 From f19f37ae3eff84161f86e62a26fbd8b68f8f91a9 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 15:29:33 +0100 Subject: make SaveManager no longer a singleton --- src/crepe/manager/Mediator.h | 4 ++-- src/crepe/manager/SaveManager.cpp | 32 +++++++++++++--------------- src/crepe/manager/SaveManager.h | 27 ++++++++++-------------- src/example/CMakeLists.txt | 2 -- src/example/asset_manager.cpp | 36 -------------------------------- src/example/savemgr.cpp | 44 --------------------------------------- src/test/CMakeLists.txt | 1 + src/test/SaveManagerTest.cpp | 41 ++++++++++++++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 118 deletions(-) delete mode 100644 src/example/asset_manager.cpp delete mode 100644 src/example/savemgr.cpp create mode 100644 src/test/SaveManagerTest.cpp (limited to 'src/test') diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index 8094d80..6507a74 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; /** * Struct to pass references to classes that would otherwise need to be singletons down to @@ -28,7 +28,7 @@ class SceneManager; struct Mediator { OptionalRef component_manager; OptionalRef scene_manager; - OptionalRef save_manager = SaveManager::get_instance(); + OptionalRef save_manager; OptionalRef event_manager = EventManager::get_instance(); OptionalRef sdl_context = SDLContext::get_instance(); OptionalRef timer = LoopTimer::get_instance(); diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index d4ed1c1..292e8fd 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -1,13 +1,24 @@ #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 = make_unique(cfg.savemgr.location); + } + return *this->db; +} + template <> string SaveManager::serialize(const string & value) const noexcept { return value; @@ -90,22 +101,6 @@ int32_t SaveManager::deserialize(const string & value) const noexcept { return deserialize(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 +150,8 @@ ValueBroker SaveManager::get(const string & key) { return { [this, key](const T & target) { this->set(key, target); }, [this, key, value]() mutable -> const T & { - value = this->deserialize(this->get_db().get(key)); + DB & db = this->get_db(); + value = this->deserialize(db.get(key)); return value; }, }; diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 3d8c852..d13a97a 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -4,6 +4,8 @@ #include "../ValueBroker.h" +#include "Manager.h" + namespace crepe { class DB; @@ -18,7 +20,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 +65,8 @@ public: */ bool has(const std::string & key); -private: - SaveManager(); +public: + SaveManager(Mediator & mediator); virtual ~SaveManager() = default; private: @@ -90,25 +92,18 @@ private: 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 db = nullptr; }; } // namespace crepe diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 8ef71bb..5a93b1f 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -16,8 +16,6 @@ function(add_example target_name) add_dependencies(examples ${target_name}) endfunction() -add_example(asset_manager) -add_example(savemgr) add_example(rendering_particle) add_example(game) add_example(button) diff --git a/src/example/asset_manager.cpp b/src/example/asset_manager.cpp deleted file mode 100644 index 917b547..0000000 --- a/src/example/asset_manager.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include - -using namespace crepe; - -int main() { - - // this needs to be called before the asset manager otherwise the destructor of sdl is not in - // the right order - { Texture test("../asset/texture/img.png"); } - // FIXME: make it so the issue described by the above comment is not possible (i.e. the order - // in which internal classes are instantiated should not impact the way the engine works). - - auto & mgr = AssetManager::get_instance(); - - { - // TODO: [design] the Sound class can't be directly included by the user as it includes - // SoLoud headers. - auto bgm = mgr.cache("../mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("../mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("../mwe/audio/sfx2.wav"); - - auto img = mgr.cache("../asset/texture/img.png"); - auto img1 = mgr.cache("../asset/texture/second.png"); - } - - { - auto bgm = mgr.cache("../mwe/audio/bgm.ogg"); - auto sfx1 = mgr.cache("../mwe/audio/sfx1.wav"); - auto sfx2 = mgr.cache("../mwe/audio/sfx2.wav"); - - auto img = mgr.cache("../asset/texture/img.png"); - auto img1 = mgr.cache("../asset/texture/second.png"); - } -} 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 -#include -#include -#include -#include - -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 prop = mgr.get(key, 0); - Proxy val = mgr.get(key, 0); - - dbg_logf("val = {}", mgr.get(key).get()); - prop.set(1); - dbg_logf("val = {}", mgr.get(key).get()); - val = 2; - dbg_logf("val = {}", mgr.get(key).get()); - mgr.set(key, 3); - dbg_logf("val = {}", mgr.get(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 c9cbac5..734e3ee 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -17,4 +17,5 @@ target_sources(test_main PUBLIC ScriptEventTest.cpp ScriptSceneTest.cpp Profiling.cpp + SaveManagerTest.cpp ) diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp new file mode 100644 index 0000000..a1efc33 --- /dev/null +++ b/src/test/SaveManagerTest.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include +#include + +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("foo", "bar"); + ASSERT_TRUE(mgr.has("foo")); + + ValueBroker value = mgr.get("foo"); + EXPECT_EQ(value.get(), "bar"); +} + +TEST_F(SaveManagerTest, DefaultValue) { + ValueBroker value = mgr.get("foo", 3); + + ASSERT_EQ(value.get(), 3); + value.set(5); + ASSERT_EQ(value.get(), 5); +} + -- cgit v1.2.3 From 2e6fcb1d048edd13a2ec69ddd226fc8ebabc2389 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 16:01:19 +0100 Subject: add SaveManager to Script --- src/crepe/api/Script.cpp | 11 +++++++---- src/crepe/api/Script.h | 4 ++++ src/crepe/api/Script.hpp | 6 ++---- src/crepe/util/OptionalRef.h | 10 +++++++++- src/crepe/util/OptionalRef.hpp | 7 +++++++ src/test/CMakeLists.txt | 1 + src/test/ScriptSaveManagerTest.cpp | 36 ++++++++++++++++++++++++++++++++++++ 7 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/test/ScriptSaveManagerTest.cpp (limited to 'src/test') diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 4091fd4..961e6e7 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,11 @@ void Script::subscribe(const EventHandler & 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 d99ab0e..024f1d7 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -7,6 +7,7 @@ #include "../system/CollisionSystem.h" #include "../types.h" #include "../util/OptionalRef.h" +#include "../ValueBroker.h" namespace crepe { @@ -113,6 +114,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 45f1ff1..23d69d9 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,8 +20,7 @@ T & Script::get_component() const { template RefVector Script::get_components() const { - Mediator & mediator = this->mediator; - ComponentManager & mgr = mediator.component_manager; + ComponentManager & mgr = this->mediator->component_manager; return mgr.get_components_by_id(this->game_object_id); } @@ -34,8 +33,7 @@ void Script::logf(Args &&... args) { template void Script::subscribe_internal(const EventHandler & 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( [this, callback](const EventType & data) -> bool { bool & active = this->active; 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,13 +25,21 @@ public: */ OptionalRef & operator=(T & ref); /** - * \brief Retrieve this reference + * \brief Retrieve this reference (cast) * * \returns Internal reference if it is set * * \throws std::runtime_error if this function is called while the reference it not set */ 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 * 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 @@ -18,6 +18,13 @@ OptionalRef::operator T &() const { return *this->ref; } +template +T * OptionalRef::operator->() const { + if (this->ref == nullptr) + throw std::runtime_error("OptionalRef: attempt to dereference nullptr"); + return this->ref; +} + template OptionalRef & OptionalRef::operator=(T & ref) { this->ref = &ref; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 734e3ee..2cb7c7a 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -18,4 +18,5 @@ target_sources(test_main PUBLIC ScriptSceneTest.cpp Profiling.cpp SaveManagerTest.cpp + ScriptSaveManagerTest.cpp ) diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp new file mode 100644 index 0000000..098afa0 --- /dev/null +++ b/src/test/ScriptSaveManagerTest.cpp @@ -0,0 +1,36 @@ +#include + +// stupid hack to allow access to private/protected members under test +#define private public +#define protected public + +#include +#include + +#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); +} + -- cgit v1.2.3 From dd8bbe2fde97786ab29490bc7ba9962deb08fb4c Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 7 Dec 2024 16:01:59 +0100 Subject: `make format` --- src/crepe/api/Script.cpp | 5 +---- src/crepe/api/Script.h | 2 +- src/test/SaveManagerTest.cpp | 5 ++--- src/test/ScriptSaveManagerTest.cpp | 3 +-- 4 files changed, 5 insertions(+), 10 deletions(-) (limited to 'src/test') diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 961e6e7..753a9e3 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -24,7 +24,4 @@ void Script::set_next_scene(const string & name) { mgr.set_next_scene(name); } -SaveManager & Script::get_save_manager() const { - return this->mediator->save_manager; -} - +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 024f1d7..0d59ab6 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -2,12 +2,12 @@ #include +#include "../ValueBroker.h" #include "../manager/EventManager.h" #include "../manager/Mediator.h" #include "../system/CollisionSystem.h" #include "../types.h" #include "../util/OptionalRef.h" -#include "../ValueBroker.h" namespace crepe { diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp index a1efc33..e9b0c29 100644 --- a/src/test/SaveManagerTest.cpp +++ b/src/test/SaveManagerTest.cpp @@ -1,8 +1,8 @@ #include -#include -#include #include +#include +#include using namespace std; using namespace crepe; @@ -38,4 +38,3 @@ TEST_F(SaveManagerTest, DefaultValue) { value.set(5); ASSERT_EQ(value.get(), 5); } - diff --git a/src/test/ScriptSaveManagerTest.cpp b/src/test/ScriptSaveManagerTest.cpp index 098afa0..64403c4 100644 --- a/src/test/ScriptSaveManagerTest.cpp +++ b/src/test/ScriptSaveManagerTest.cpp @@ -4,8 +4,8 @@ #define private public #define protected public -#include #include +#include #include "ScriptTest.h" @@ -33,4 +33,3 @@ TEST_F(ScriptSaveManagerTest, GetSaveManager) { EXPECT_EQ(&mgr, &save_mgr); } - -- cgit v1.2.3 From 7cbc577e94ed048f2a8146fab6972ae6ff290be7 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Tue, 10 Dec 2024 18:57:03 +0100 Subject: fix AudioSystem bug + add regression test --- src/crepe/system/AudioSystem.cpp | 6 +++--- src/test/AudioTest.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src/test') diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b2c1dc6..ddba268 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -26,12 +26,12 @@ void AudioSystem::diff_update(AudioSource & component, Sound & resource) { SoundContext & context = this->get_context(); if (component.active != component.last_active) { - if (component.active) { - component.oneshot_play = component.play_on_awake; - } else { + if (!component.active) { context.stop(component.voice); return; } + if (component.play_on_awake) + component.oneshot_play = true; } if (!component.active) return; diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 774fdb8..48bba1b 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -150,3 +150,12 @@ TEST_F(AudioTest, PlayOnActive) { system.update(); } } + +TEST_F(AudioTest, PlayImmediately) { + component.play_on_awake = false; + component.play(); + + EXPECT_CALL(context, play(_)).Times(1); + + system.update(); +} -- cgit v1.2.3