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/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/test/CMakeLists.txt') 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 ) -- 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/CMakeLists.txt') 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 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/CMakeLists.txt') 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 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/CMakeLists.txt') 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 d9e46281c1e24a5f23d779d314e5df87fa3317a3 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Wed, 4 Dec 2024 21:33:38 +0100 Subject: tests --- src/crepe/api/LoopManager.cpp | 13 +++++------ src/crepe/api/LoopManager.h | 16 ++++++-------- src/example/CMakeLists.txt | 1 - src/test/CMakeLists.txt | 1 + src/test/loopManagerTest.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++ src/test/loopTimerTest.cpp | 36 ++++++++++++++++++++++++++----- 6 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 src/test/loopManagerTest.cpp (limited to 'src/test/CMakeLists.txt') diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index e584ba7..9bedbcc 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,17 +1,17 @@ #include #include "../facade/SDLContext.h" - #include "../system/AnimatorSystem.h" #include "../system/CollisionSystem.h" #include "../system/ParticleSystem.h" #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" + #include "../api/EventManager.h" #include "LoopManager.h" #include "LoopTimer.h" -#include + using namespace crepe; using namespace std; @@ -37,7 +37,6 @@ void LoopManager::start() { this->setup(); this->loop(); } -void LoopManager::set_running(bool running) { this->game_running = running; } void LoopManager::fixed_update() {} @@ -46,7 +45,7 @@ void LoopManager::loop() { while (game_running) { this->loop_timer->update(); - + while (this->loop_timer->get_lag() >= this->loop_timer->get_fixed_delta_time()) { this->process_input(); this->fixed_update(); @@ -67,7 +66,7 @@ void LoopManager::setup() { void LoopManager::render() { if (this->game_running) { - //this->get_system().update(); + this->get_system().update(); } } bool LoopManager::on_shutdown(const ShutDownEvent & e){ @@ -75,6 +74,4 @@ bool LoopManager::on_shutdown(const ShutDownEvent & e){ return false; } -void LoopManager::update() { - std::cout << this->loop_timer->get_fps() << std::endl; -} +void LoopManager::update() {} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index 3bf54b9..eb2b525 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -16,6 +16,12 @@ namespace crepe { */ class LoopManager { public: + /** + * \brief Start the gameloop + * + * This is the start of the engine where the setup is called and then the loop keeps running until the game stops running. + * Developers need to call this function to run the game. + */ void start(); LoopManager(); @@ -68,14 +74,6 @@ private: * This function updates physics and game logic based on LoopTimer's fixed_delta_time. */ void fixed_update(); - - /** - * \brief Set game running variable - * - * \param running running (false = game shutdown, true = game running) - */ - void set_running(bool running); - /** * \brief Function for executing render-related systems. * @@ -100,7 +98,7 @@ private: * \brief Collection of System instances * * This map holds System instances indexed by the system's class typeid. It is filled in the - * constructor of \c LoopManager using LoopManager::load_system. + * constructor of LoopManager using LoopManager::load_system. */ std::unordered_map> systems; /** diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 54100cf..6f92d45 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -19,4 +19,3 @@ endfunction() add_example(asset_manager) add_example(savemgr) add_example(rendering_particle) -add_example(gameloop) diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d310f6a..b126add 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -12,4 +12,5 @@ target_sources(test_main PUBLIC ValueBrokerTest.cpp DBTest.cpp Vector2Test.cpp + loopTimerTest.cpp ) diff --git a/src/test/loopManagerTest.cpp b/src/test/loopManagerTest.cpp new file mode 100644 index 0000000..6e66ce7 --- /dev/null +++ b/src/test/loopManagerTest.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#define private public +#define protected public +#include "api/LoopTimer.h" +#include "api/LoopManager.h" + +using namespace std::chrono; +using namespace crepe; + +class LoopTimerTest : public ::testing::Test { +protected: + LoopManager loop_manager; + + void SetUp() override { + // Setting up loop manager and start the loop + loop_manager.setup(); + loop_manager.loop_timer->set_target_fps(60); + } +}; + +// Test to check if exactly 5 fixed updates are done every second (50Hz) +TEST_F(LoopTimerTest, FixedUpdateCalledAt50Hz) { + // Set target fixed delta time to 20ms (50Hz fixed updates) + loop_manager.loop_timer->set_fixed_delta_time(milliseconds(20)); + + int fixed_update_count = 0; + + // We want to simulate the game loop for about 1 second + auto start_time = steady_clock::now(); + + // Simulate the game loop for 1 second + while (duration_cast(steady_clock::now() - start_time).count() < 1) { + loop_manager.loop_timer->update(); + + // Simulate processing fixed updates while there's lag to advance + while (loop_manager.loop_timer->get_lag() >= loop_manager.loop_timer->get_fixed_delta_time()) { + loop_manager.fixed_update(); // Process fixed update + fixed_update_count++; // Count the number of fixed updates + loop_manager.loop_timer->advance_fixed_update(); + } + + // We do not need to call render or update for this test + loop_manager.loop_timer->enforce_frame_rate(); // Enforce the frame rate (this would normally go to the display) + } + + // We expect 5 fixed updates to occur in 1 second at 50Hz + ASSERT_EQ(fixed_update_count, 5); +} diff --git a/src/test/loopTimerTest.cpp b/src/test/loopTimerTest.cpp index d2f7d9b..9bbbff3 100644 --- a/src/test/loopTimerTest.cpp +++ b/src/test/loopTimerTest.cpp @@ -1,6 +1,8 @@ #include #include #include +#define private public +#define protected public #include "api/LoopTimer.h" using namespace std::chrono; @@ -11,23 +13,47 @@ protected: LoopTimer loop_timer; void SetUp() override { - loop_timer.start(); // Reset loop timer before each test. + loop_timer.start(); } }; TEST_F(LoopTimerTest, EnforcesTargetFrameRate) { // Set the target FPS to 60 (which gives a target time per frame of ~16.67 ms) loop_timer.set_target_fps(60); - // Simulate a short update (frame duration less than the target frame time) auto start_time = steady_clock::now(); - loop_timer.enforce_frame_rate(); // Enforce the frame rate + loop_timer.enforce_frame_rate(); - // Check that the loop timer's current time is greater than or equal to the target frame time auto elapsed_time = steady_clock::now() - start_time; auto elapsed_ms = duration_cast(elapsed_time).count(); - // Assert that the elapsed time is close to the target frame time // For 60 FPS, the target frame time is around 16.67ms ASSERT_GE(elapsed_ms, 16); // Make sure it's at least 16 ms (could be slightly more) ASSERT_LE(elapsed_ms, 18); // Ensure it's not too much longer } +TEST_F(LoopTimerTest, SetTargetFps) { + // Set the target FPS to 120 + loop_timer.set_target_fps(120); + + // Calculate the expected frame time (~8.33ms per frame) + auto expected_frame_time = std::chrono::duration(1.0 / 120.0); + + ASSERT_NEAR(loop_timer.frame_target_time.count(), expected_frame_time.count(), 0.001); +} +TEST_F(LoopTimerTest, DeltaTimeCalculation) { + // Set the target FPS to 60 (16.67 ms per frame) + loop_timer.set_target_fps(60); + + auto start_time = steady_clock::now(); + loop_timer.update(); + auto end_time = steady_clock::now(); + + // Check the delta time + double delta_time = loop_timer.get_delta_time(); + + auto elapsed_time = duration_cast(end_time - start_time).count(); + + // Assert that delta_time is close to the elapsed time + ASSERT_GE(delta_time, elapsed_time / 1000.0); + ASSERT_LE(delta_time, (elapsed_time + 2) / 1000.0); +} + -- 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/CMakeLists.txt') 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 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/CMakeLists.txt') 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/CMakeLists.txt') 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 7893342e1ba5fd01be7448244db967fda3c5cfe9 Mon Sep 17 00:00:00 2001 From: WBoerenkamps Date: Mon, 9 Dec 2024 17:35:20 +0100 Subject: checked for 0 division --- src/crepe/manager/LoopTimerManager.cpp | 7 +++++++ src/test/CMakeLists.txt | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/test/CMakeLists.txt') diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index 37b9c44..70a57d2 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -31,6 +31,9 @@ void LoopTimerManager::update() { if (this->delta_time > this->maximum_delta_time) { this->delta_time = this->maximum_delta_time; } + if(this->delta_time.count() <= 0){ + this->delta_time = std::chrono::duration(0.0); + } this->actual_fps = 1.0 / this->delta_time.count(); this->elapsed_time += this->delta_time; @@ -49,6 +52,10 @@ void LoopTimerManager::advance_fixed_update() { void LoopTimerManager::set_target_fps(int fps) { this->target_fps = fps; + //check if fps is lower or equals 0 + if(fps <= 0){ + return; + } // target time per frame in seconds this->frame_target_time = std::chrono::duration(1.0) / this->target_fps; } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 1c963d8..a6d3387 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,7 +15,6 @@ target_sources(test_main PUBLIC Vector2Test.cpp LoopManagerTest.cpp LoopTimerTest.cpp - InputTest.cpp ScriptEventTest.cpp ScriptSceneTest.cpp -- cgit v1.2.3