diff options
| author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-12-18 14:01:11 +0100 | 
|---|---|---|
| committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-12-18 14:01:11 +0100 | 
| commit | efcb4e88c7b921684379adce2a3d09c8f9aaf3a3 (patch) | |
| tree | 7ffc08549ddd6f20487304507a7474591f559cbb | |
| parent | a444da0b35123a9e51cde1f5be9aab0204eb5b66 (diff) | |
| parent | 0e800c6857122436e7a766c6934991aa1d8d31ff (diff) | |
Merge branch 'master' into loek/replay
| -rw-r--r-- | src/crepe/facade/DB.cpp | 12 | ||||
| -rw-r--r-- | src/crepe/facade/DB.h | 2 | ||||
| -rw-r--r-- | src/crepe/manager/SaveManager.cpp | 49 | ||||
| -rw-r--r-- | src/crepe/manager/SaveManager.h | 6 | ||||
| -rw-r--r-- | src/doc/feature/proxy.dox | 43 | ||||
| -rw-r--r-- | src/doc/feature/savemgr.dox | 80 | ||||
| -rw-r--r-- | src/test/DBTest.cpp | 8 | ||||
| -rw-r--r-- | src/test/SaveManagerTest.cpp | 17 | ||||
| -rw-r--r-- | src/test/main.cpp | 3 | 
9 files changed, 177 insertions, 43 deletions
diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index d45cd82..7a3e473 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<char *>(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) { 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<libdb::DB, std::function<void(libdb::DB *)>> db; -	//! RAII wrapper around \c DBC struct -	std::unique_ptr<libdb::DBC, std::function<void(libdb::DBC *)>> cursor;  private:  	/** diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp index 691ea2f..f313ed2 100644 --- a/src/crepe/manager/SaveManager.cpp +++ b/src/crepe/manager/SaveManager.cpp @@ -129,9 +129,32 @@ template void SaveManager::set(const string &, const float &);  template void SaveManager::set(const string &, const double &);  template <typename T> +T SaveManager::get(const string & key) { +	return this->deserialize<T>(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 <typename T>  ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {  	if (!this->has(key)) this->set<T>(key, default_value); -	return this->get<T>(key); +	T value; +	return { +		[this, key](const T & target) { this->set<T>(key, target); }, +		[this, key, value]() mutable -> const T & { +			value = this->get<T>(key); +			return value; +		}, +	};  }  template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);  template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &); @@ -144,27 +167,3 @@ template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);  template ValueBroker<float> SaveManager::get(const string &, const float &);  template ValueBroker<double> SaveManager::get(const string &, const double &);  template ValueBroker<string> SaveManager::get(const string &, const string &); - -template <typename T> -ValueBroker<T> SaveManager::get(const string & key) { -	T value; -	return { -		[this, key](const T & target) { this->set<T>(key, target); }, -		[this, key, value]() mutable -> const T & { -			DB & db = this->get_db(); -			value = this->deserialize<T>(db.get(key)); -			return value; -		}, -	}; -} -template ValueBroker<uint8_t> SaveManager::get(const string &); -template ValueBroker<int8_t> SaveManager::get(const string &); -template ValueBroker<uint16_t> SaveManager::get(const string &); -template ValueBroker<int16_t> SaveManager::get(const string &); -template ValueBroker<uint32_t> SaveManager::get(const string &); -template ValueBroker<int32_t> SaveManager::get(const string &); -template ValueBroker<uint64_t> SaveManager::get(const string &); -template ValueBroker<int64_t> SaveManager::get(const string &); -template ValueBroker<float> SaveManager::get(const string &); -template ValueBroker<double> SaveManager::get(const string &); -template ValueBroker<string> SaveManager::get(const string &); diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h index 61a978d..1e34bc0 100644 --- a/src/crepe/manager/SaveManager.h +++ b/src/crepe/manager/SaveManager.h @@ -36,17 +36,17 @@ public:  	ValueBroker<T> 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 <typename T> -	ValueBroker<T> 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..66bbd2f --- /dev/null +++ b/src/doc/feature/proxy.dox @@ -0,0 +1,43 @@ +// vim:ft=doxygen +namespace crepe { +/** + +\defgroup feature_proxy Proxy utility +\ingroup feature +\brief Use ValueBroker as if it were a regular variable + +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 + +\par Example + +```cpp +#include <crepe/util/Proxy.h> +#include <crepe/ValueBroker.h> + +int calculation(int value) { +	return 3 * value; +} + +void anywhere() { +	crepe::ValueBroker<int> foo_handle; +	crepe::Proxy foo = foo_handle; + +	// implicitly calls .set() +	foo += 10; + +	// implicitly calls .get() +	int out = calculation(foo); + +	// explicitly cast (also calls .get()) +	int casted = int(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<int>("foo", 3); +// Get value of key "foo" +int foo = save_manager.get<int>("foo"); + +// ~~~ arbitrary game code ~~~ +foo += 10; +// ~~~ arbitrary game code ~~~ + +// Save "foo" back to database +save_manager.set<int>("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<int>("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/test/DBTest.cpp b/src/test/DBTest.cpp index 99dedff..7f2c339 100644 --- a/src/test/DBTest.cpp +++ b/src/test/DBTest.cpp @@ -27,3 +27,11 @@ 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 e9b0c29..7609e69 100644 --- a/src/test/SaveManagerTest.cpp +++ b/src/test/SaveManagerTest.cpp @@ -27,8 +27,8 @@ TEST_F(SaveManagerTest, ReadWrite) {  	mgr.set<string>("foo", "bar");  	ASSERT_TRUE(mgr.has("foo")); -	ValueBroker value = mgr.get<string>("foo"); -	EXPECT_EQ(value.get(), "bar"); +	string value = mgr.get<string>("foo"); +	EXPECT_EQ(value, "bar");  }  TEST_F(SaveManagerTest, DefaultValue) { @@ -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<int>("foo", 1); +	ValueBroker bar = mgr.get<int>("bar", 2); + +	EXPECT_EQ(foo.get(), 1); +	EXPECT_EQ(bar.get(), 2); + +	EXPECT_EQ(mgr.get<int>("foo"), 1); +	EXPECT_EQ(mgr.get<int>("bar"), 2);  } diff --git a/src/test/main.cpp b/src/test/main.cpp index ed2aed5..0e1bc75 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -1,6 +1,7 @@ -#include <crepe/api/Config.h>  #include <gtest/gtest.h> +#include <crepe/api/Config.h> +  using namespace crepe;  using namespace testing;  |