From b845376e270c060730d4f8b9b0946a63908871da Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Mon, 9 Dec 2024 11:22:35 +0100 Subject: feature_config --- src/crepe/api/Config.h | 50 ++++++++++++++--------------------------- src/crepe/facade/SDLContext.cpp | 6 ++--- 2 files changed, 20 insertions(+), 36 deletions(-) (limited to 'src/crepe') diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index a9745c3..e762d89 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -3,34 +3,21 @@ #include #include "../util/Log.h" - -#include "types.h" +#include "../types.h" namespace crepe { /** * \brief Global configuration interface * - * This class stores engine default settings. Properties on this class are only supposed to be - * modified *before* execution is handed over from the game programmer to the engine (i.e. the - * main loop is started). + * This struct stores both engine default settings and global configuration parameters. */ -class Config final { -public: +struct Config { //! 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 { + struct log { // NOLINT /** * \brief Log level * @@ -38,7 +25,7 @@ public: */ Log::Level level = Log::Level::INFO; /** - * \brief Colored log output + * \brief Enable colored log output * * Enables log coloring using ANSI escape codes. */ @@ -46,7 +33,7 @@ public: } log; //! Save manager - struct { + struct savemgr { // NOLINT /** * \brief Save file location * @@ -56,25 +43,22 @@ public: std::string location = "save.crepe.db"; } savemgr; - //! physics-related settings - struct { - /** - * \brief gravity value of physics system - * - * Gravity value of game. - */ + //! Physics-related settings + struct physics { // NOLINT + //! Gravity value of physics system double gravity = 1; } physics; - //! default window settings - struct { - //! default screen size in pixels - ivec2 default_size = {1280, 720}; - std::string window_title = "Jetpack joyride clone"; - } window_settings; + //! Default window settings + struct window { // NOLINT + //! Default window size (in pixels) + ivec2 size = {1280, 720}; + //! Default window title + std::string title = "Jetpack joyride clone"; + } window; //! Asset loading options - struct { + struct asset { // NOLINT /** * \brief Pattern to match for Asset base directory * diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 4cc2206..223dd11 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -39,10 +39,10 @@ SDLContext::SDLContext() { throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError())); } - auto & cfg = Config::get_instance().window_settings; + auto & cfg = Config::get_instance().window; SDL_Window * tmp_window - = SDL_CreateWindow(cfg.window_title.c_str(), SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, cfg.default_size.x, cfg.default_size.y, 0); + = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, cfg.size.x, cfg.size.y, 0); if (!tmp_window) { throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError())); } -- cgit v1.2.3 From e8e5412965f19d3966635a2c207d55ae1b12db51 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Mon, 9 Dec 2024 12:42:58 +0100 Subject: feature_savemgr --- src/crepe/manager/SaveManager.cpp | 48 +++++++++++------------ src/crepe/manager/SaveManager.h | 6 +-- src/doc/feature/proxy.dox | 38 +++++++++++++++++++ src/doc/feature/savemgr.dox | 80 +++++++++++++++++++++++++++++++++++++++ src/doc/features.dox | 9 ++++- 5 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 src/doc/feature/proxy.dox create mode 100644 src/doc/feature/savemgr.dox (limited to 'src/crepe') diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index d4ed1c1..034a283 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -132,10 +132,33 @@ template void SaveManager::set(const string &, const int64_t &); template void SaveManager::set(const string &, const float &); template void SaveManager::set(const string &, const double &); +template +T SaveManager::get(const string & key) { + return this->deserialize(this->get_db().get(key)); +} +template uint8_t SaveManager::get(const string &); +template int8_t SaveManager::get(const string &); +template uint16_t SaveManager::get(const string &); +template int16_t SaveManager::get(const string &); +template uint32_t SaveManager::get(const string &); +template int32_t SaveManager::get(const string &); +template uint64_t SaveManager::get(const string &); +template int64_t SaveManager::get(const string &); +template float SaveManager::get(const string &); +template double SaveManager::get(const string &); +template string SaveManager::get(const string &); + template ValueBroker SaveManager::get(const string & key, const T & default_value) { if (!this->has(key)) this->set(key, default_value); - return this->get(key); + T value; + return { + [this, key](const T & target) { this->set(key, target); }, + [this, key, value]() mutable -> const T & { + value = this->get(key); + return value; + }, + }; } template ValueBroker SaveManager::get(const string &, const uint8_t &); template ValueBroker SaveManager::get(const string &, const int8_t &); @@ -148,26 +171,3 @@ template ValueBroker SaveManager::get(const string &, const int64_t &); template ValueBroker SaveManager::get(const string &, const float &); template ValueBroker SaveManager::get(const string &, const double &); template ValueBroker SaveManager::get(const string &, const string &); - -template -ValueBroker SaveManager::get(const string & key) { - T value; - return { - [this, key](const T & target) { this->set(key, target); }, - [this, key, value]() mutable -> const T & { - value = this->deserialize(this->get_db().get(key)); - return value; - }, - }; -} -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); -template ValueBroker SaveManager::get(const string &); diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 3d8c852..e2ef005 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -33,17 +33,17 @@ public: ValueBroker get(const std::string & key, const T & default_value); /** - * \brief Get a read/write reference to a value + * \brief Get a value directly * * \param key The value key * - * \return Read/write reference to the value + * \return The value * * \note Attempting to read this value before it is initialized (i.e. set) will result in an * exception */ template - ValueBroker get(const std::string & key); + T get(const std::string & key); /** * \brief Set a value directly diff --git a/src/doc/feature/proxy.dox b/src/doc/feature/proxy.dox new file mode 100644 index 0000000..02ed4a5 --- /dev/null +++ b/src/doc/feature/proxy.dox @@ -0,0 +1,38 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_proxy Proxy utility +\ingroup feature +\brief Use ValueBroker as if it were a regular variable + +\todo Long description + +\see ValueBroker +\see Proxy + +\par Example + +```cpp +#include +#include + +int calculation(int value) { + return 3 * value; +} + +void anywhere() { + crepe::ValueBroker foo_handle; + crepe::Proxy foo = foo_handle; + + // implicitly calls .set() + foo += 10; + + // implicitly calls .get() + int out = calculation(foo); +} + +``` + +*/ +} diff --git a/src/doc/feature/savemgr.dox b/src/doc/feature/savemgr.dox new file mode 100644 index 0000000..6aeab03 --- /dev/null +++ b/src/doc/feature/savemgr.dox @@ -0,0 +1,80 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_savemgr Save data +\ingroup feature +\brief Functions to persistently store and retrieve arbitrary values + +The SaveManager may be used to persistently store game state such as player +progress, statistics, achievements, etc. It works like a key-value store, where +the key is a string and the value is an arbitrary type. + +SaveManager implements the following: + +- Storage and retrieval of primitive types and strings. +- Automatic initialization of the database using default values. +- The underlying database format is journaled, which reduces the likelihood of + players losing save data when an unexpected crash occurs while the SaveManager + is writing to disk. + +\see SaveManager + +\par Example + +The SaveManager instance reference may be retrieved by calling \c +get_save_manager(). This function is available--- + +- Within (derivatives of) Script + +- \todo Within (derivatives of) Scene + +- \todo As a public member function of LoopManager + +```cpp +// Retrieve save manager +crepe::SaveManager & save_manager = get_save_manager(); +``` + +SaveManager may be used *explicitly*, using the \ref SaveManager::set "set()", +\ref SaveManager::get "get()" and \ref SaveManager::has "has()" methods: +```cpp +// Check if the key "foo" exists, and initialize it to 3 if it doesn't +if (!save_manager.has("foo")) + save_manager.set("foo", 3); +// Get value of key "foo" +int foo = save_manager.get("foo"); + +// ~~~ arbitrary game code ~~~ +foo += 10; +// ~~~ arbitrary game code ~~~ + +// Save "foo" back to database +save_manager.set("foo", foo); +``` + +Alternatively, SaveManager::get may be called with a default value as second +parameter. This changes its return type to ValueBroker, which acts as a +read/write handle to the specific key requested, and remembers the key and its +value type for you: +```cpp +// Get a read/write handle to the value stored in key "foo", and initialize it +// to 3 if it doesn't exist yet +ValueBroker foo_handle = save_manager.get("foo", 3); +int foo = foo_handle.get(); + +// ~~~ arbitrary game code ~~~ +foo += 10; +// ~~~ arbitrary game code ~~~ + +// Save back to database +foo_handle.set(foo); +``` + +To further simplify game code, the return value of SaveManager::get may be +implicitly cast to Proxy instead of ValueBroker. This allows the database value +to be used as if it were a regular variable. This usage is detailed separately +in \"\ref feature_proxy\". + +*/ +} diff --git a/src/doc/features.dox b/src/doc/features.dox index 3bcbffe..6734137 100644 --- a/src/doc/features.dox +++ b/src/doc/features.dox @@ -41,7 +41,7 @@ feature. - \todo Particle effects - Save data - - \todo SaveManager usage + - \ref feature_savemgr \n\copybrief feature_savemgr - Audio - \todo Playing sound effects @@ -58,6 +58,13 @@ feature. - \todo Subscribing to *any* event inside Script + - \todo Creating and dispatching custom events + - \todo Replay +- Utilities + - \todo Logging + + - \ref feature_proxy \n\copybrief feature_proxy + */ -- cgit v1.2.3 From f3a30b93aa76f546144c744a08a4ed04bef95192 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Mon, 9 Dec 2024 15:46:30 +0100 Subject: more WIP doxygen --- src/crepe/api/Asset.h | 4 ++-- src/doc/feature/bgm.dox | 22 ++++++++++++++++++++++ src/doc/feature/gameobject.dox | 4 ++-- src/doc/feature/proxy.dox | 7 ++++++- src/doc/feature/sfx.dox | 24 ++++++++++++++++++++++++ src/doc/features.dox | 5 ++--- 6 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/doc/feature/bgm.dox create mode 100644 src/doc/feature/sfx.dox (limited to 'src/crepe') diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h index bfd0ac7..d802e83 100644 --- a/src/crepe/api/Asset.h +++ b/src/crepe/api/Asset.h @@ -43,13 +43,13 @@ private: /** * \brief Locate asset path, or throw exception if it cannot be found * - * This function resolves asset locations relative to crepe::Config::root_pattern if it is + * This function resolves asset locations relative to Config::asset::root_pattern if it is * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. * This function only returns if the file can be found. * * \param src Arbitrary path to resource file * - * \returns \p src if crepe::Config::root_pattern is empty + * \returns \p src if Config::asset::root_pattern is empty * \returns Canonical path to \p src * * \throws std::runtime_error if root_pattern cannot be found diff --git a/src/doc/feature/bgm.dox b/src/doc/feature/bgm.dox new file mode 100644 index 0000000..968abb8 --- /dev/null +++ b/src/doc/feature/bgm.dox @@ -0,0 +1,22 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_bgm Playing background music +\ingroup feature +\brief Add background music to a scene using the AudioSource component + +This page shows how to implement background music using the AudioSource +effects. + +\see AudioSource + +\par Example + +\note This example assumes you already have a GameObject. If not, read +\"\ref feature_gameobject\" first. + +\todo Merge #60 + +*/ +} diff --git a/src/doc/feature/gameobject.dox b/src/doc/feature/gameobject.dox index c561874..ac3927c 100644 --- a/src/doc/feature/gameobject.dox +++ b/src/doc/feature/gameobject.dox @@ -2,9 +2,9 @@ namespace crepe { /** -\defgroup feature_gameobject GameObjects +\defgroup feature_gameobject Entity basics \ingroup feature -\brief GameObject to create a Scene +\brief Building game entities using a GameObject GameObjects are the fundamental building blocks of a Scene. They represent entities in the game world and can have various components attached to them to define their diff --git a/src/doc/feature/proxy.dox b/src/doc/feature/proxy.dox index 02ed4a5..66bbd2f 100644 --- a/src/doc/feature/proxy.dox +++ b/src/doc/feature/proxy.dox @@ -6,7 +6,9 @@ namespace crepe { \ingroup feature \brief Use ValueBroker as if it were a regular variable -\todo Long description +Proxy provides operators that allow you to use a ValueBroker instance as if it +were a regular variable. Proxy implements a constructor that allows it to be +used as a substitute return type for any function that returns ValueBroker. \see ValueBroker \see Proxy @@ -30,6 +32,9 @@ void anywhere() { // implicitly calls .get() int out = calculation(foo); + + // explicitly cast (also calls .get()) + int casted = int(foo); } ``` diff --git a/src/doc/feature/sfx.dox b/src/doc/feature/sfx.dox new file mode 100644 index 0000000..2a5c9cc --- /dev/null +++ b/src/doc/feature/sfx.dox @@ -0,0 +1,24 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_sfx Playing sound effects +\ingroup feature +\brief Fire a sound effect using the AudioSource component + +This page shows how to implement one-shot sound effects using the AudioSource +component's 'fire and forget'-style API. + +\see AudioSource + +\par Example + +\note This example assumes you already have a GameObject to attach the +AudioSource component to, and uses a Script to control the AudioSource instance. +Separate pages describing these features in more detail can be found at \"\ref +feature_gameobject\" and \"\ref feature_script\" respectively. + +\todo Merge #60 + +*/ +} diff --git a/src/doc/features.dox b/src/doc/features.dox index 6734137..56d17c7 100644 --- a/src/doc/features.dox +++ b/src/doc/features.dox @@ -44,9 +44,8 @@ feature. - \ref feature_savemgr \n\copybrief feature_savemgr - Audio - - \todo Playing sound effects - - - \todo Adding background music to a scene + - \ref feature_sfx \n\copybrief feature_sfx + - \ref feature_bgm \n\copybrief feature_bgm - \todo AI -- cgit v1.2.3 From 6bc0025e4c24ed6659d993f3469c10615fb0e273 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Sat, 14 Dec 2024 16:20:49 +0100 Subject: fix DB facade + add regression tests --- src/crepe/facade/DB.cpp | 13 ++++--------- src/crepe/facade/DB.h | 2 -- src/test/DBTest.cpp | 9 +++++++++ src/test/SaveManagerTest.cpp | 13 ++++++++++++- 4 files changed, 25 insertions(+), 12 deletions(-) (limited to 'src/crepe') diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index 95cf606..2232a21 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -21,12 +21,6 @@ DB::DB(const string & path) { const char * file = path.empty() ? NULL : path.c_str(); ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0); if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret))); - - // create cursor - libdb::DBC * cursor; - ret = this->db->cursor(this->db.get(), NULL, &cursor, 0); - if (ret != 0) throw runtime_error(format("db->cursor: {}", libdb::db_strerror(ret))); - this->cursor = {cursor, [](libdb::DBC * cursor) { cursor->close(cursor); }}; } libdb::DBT DB::to_thing(const string & thing) const noexcept { @@ -42,10 +36,10 @@ string DB::get(const string & key) { libdb::DBT db_val; memset(&db_val, 0, sizeof(libdb::DBT)); - int ret = this->cursor->get(this->cursor.get(), &db_key, &db_val, DB_FIRST); + int ret = this->db->get(this->db.get(), NULL, &db_key, &db_val, 0); if (ret == 0) return {static_cast(db_val.data), db_val.size}; - string err = format("cursor->get: {}", libdb::db_strerror(ret)); + string err = format("db->get: {}", libdb::db_strerror(ret)); if (ret == DB_NOTFOUND) throw out_of_range(err); else throw runtime_error(err); } @@ -54,7 +48,7 @@ void DB::set(const string & key, const string & value) { libdb::DBT db_key = this->to_thing(key); libdb::DBT db_val = this->to_thing(value); int ret = this->db->put(this->db.get(), NULL, &db_key, &db_val, 0); - if (ret != 0) throw runtime_error(format("cursor->get: {}", libdb::db_strerror(ret))); + if (ret != 0) throw runtime_error(format("db->get: {}", libdb::db_strerror(ret))); } bool DB::has(const std::string & key) { @@ -65,3 +59,4 @@ bool DB::has(const std::string & key) { } return true; } + diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h index 115c0f1..84cdf19 100644 --- a/src/crepe/facade/DB.h +++ b/src/crepe/facade/DB.h @@ -61,8 +61,6 @@ public: private: //! RAII wrapper around \c DB struct std::unique_ptr> db; - //! RAII wrapper around \c DBC struct - std::unique_ptr> cursor; private: /** diff --git a/src/test/DBTest.cpp b/src/test/DBTest.cpp index 99dedff..0c98e10 100644 --- a/src/test/DBTest.cpp +++ b/src/test/DBTest.cpp @@ -27,3 +27,12 @@ TEST_F(DBTest, Has) { db.set("foo", "bar"); EXPECT_EQ(db.has("foo"), true); } + +TEST_F(DBTest, MultipleKeys) { + db.set("foo", "foo"); + db.set("bar", "bar"); + + EXPECT_EQ(db.get("foo"), "foo"); + EXPECT_EQ(db.get("bar"), "bar"); +} + diff --git a/src/test/SaveManagerTest.cpp b/src/test/SaveManagerTest.cpp index 0209f6a..7609e69 100644 --- a/src/test/SaveManagerTest.cpp +++ b/src/test/SaveManagerTest.cpp @@ -36,5 +36,16 @@ TEST_F(SaveManagerTest, DefaultValue) { ASSERT_EQ(value.get(), 3); value.set(5); - ASSERT_EQ(value.get(), 5); + EXPECT_EQ(value.get(), 5); +} + +TEST_F(SaveManagerTest, MultipleKeys) { + ValueBroker foo = mgr.get("foo", 1); + ValueBroker bar = mgr.get("bar", 2); + + EXPECT_EQ(foo.get(), 1); + EXPECT_EQ(bar.get(), 2); + + EXPECT_EQ(mgr.get("foo"), 1); + EXPECT_EQ(mgr.get("bar"), 2); } -- cgit v1.2.3