summaryrefslogtreecommitdiff
path: root/src/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/database')
-rw-r--r--src/database/database-dummy.cpp38
-rw-r--r--src/database/database-dummy.h9
-rw-r--r--src/database/database-files.cpp125
-rw-r--r--src/database/database-files.h26
-rw-r--r--src/database/database-leveldb.cpp16
-rw-r--r--src/database/database-postgresql.cpp41
-rw-r--r--src/database/database-postgresql.h2
-rw-r--r--src/database/database-redis.cpp6
-rw-r--r--src/database/database-sqlite3.cpp135
-rw-r--r--src/database/database-sqlite3.h25
-rw-r--r--src/database/database.h13
11 files changed, 383 insertions, 53 deletions
diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp
index b56f341c5..629b2fb04 100644
--- a/src/database/database-dummy.cpp
+++ b/src/database/database-dummy.cpp
@@ -80,3 +80,41 @@ void Database_Dummy::listPlayers(std::vector<std::string> &res)
res.emplace_back(player);
}
}
+
+bool Database_Dummy::getModEntries(const std::string &modname, StringMap *storage)
+{
+ const auto mod_pair = m_mod_meta_database.find(modname);
+ if (mod_pair != m_mod_meta_database.cend()) {
+ for (const auto &pair : mod_pair->second) {
+ (*storage)[pair.first] = pair.second;
+ }
+ }
+ return true;
+}
+
+bool Database_Dummy::setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value)
+{
+ auto mod_pair = m_mod_meta_database.find(modname);
+ if (mod_pair == m_mod_meta_database.end()) {
+ m_mod_meta_database[modname] = StringMap({{key, value}});
+ } else {
+ mod_pair->second[key] = value;
+ }
+ return true;
+}
+
+bool Database_Dummy::removeModEntry(const std::string &modname, const std::string &key)
+{
+ auto mod_pair = m_mod_meta_database.find(modname);
+ if (mod_pair != m_mod_meta_database.end())
+ return mod_pair->second.erase(key) > 0;
+ return false;
+}
+
+void Database_Dummy::listMods(std::vector<std::string> *res)
+{
+ for (const auto &pair : m_mod_meta_database) {
+ res->push_back(pair.first);
+ }
+}
diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h
index b69919f84..44b9e8d68 100644
--- a/src/database/database-dummy.h
+++ b/src/database/database-dummy.h
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include "irrlichttypes.h"
-class Database_Dummy : public MapDatabase, public PlayerDatabase
+class Database_Dummy : public MapDatabase, public PlayerDatabase, public ModMetadataDatabase
{
public:
bool saveBlock(const v3s16 &pos, const std::string &data);
@@ -37,10 +37,17 @@ public:
bool removePlayer(const std::string &name);
void listPlayers(std::vector<std::string> &res);
+ bool getModEntries(const std::string &modname, StringMap *storage);
+ bool setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value);
+ bool removeModEntry(const std::string &modname, const std::string &key);
+ void listMods(std::vector<std::string> *res);
+
void beginSave() {}
void endSave() {}
private:
std::map<s64, std::string> m_database;
std::set<std::string> m_player_database;
+ std::unordered_map<std::string, StringMap> m_mod_meta_database;
};
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
index d9d113b4e..7c0dbac28 100644
--- a/src/database/database-files.cpp
+++ b/src/database/database-files.cpp
@@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <cassert>
-#include <json/json.h>
#include "convert_json.h"
#include "database-files.h"
#include "remoteplayer.h"
@@ -376,3 +375,127 @@ bool AuthDatabaseFiles::writeAuthFile()
}
return true;
}
+
+ModMetadataDatabaseFiles::ModMetadataDatabaseFiles(const std::string &savedir):
+ m_storage_dir(savedir + DIR_DELIM + "mod_storage")
+{
+}
+
+bool ModMetadataDatabaseFiles::getModEntries(const std::string &modname, StringMap *storage)
+{
+ Json::Value *meta = getOrCreateJson(modname);
+ if (!meta)
+ return false;
+
+ const Json::Value::Members attr_list = meta->getMemberNames();
+ for (const auto &it : attr_list) {
+ Json::Value attr_value = (*meta)[it];
+ (*storage)[it] = attr_value.asString();
+ }
+
+ return true;
+}
+
+bool ModMetadataDatabaseFiles::setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value)
+{
+ Json::Value *meta = getOrCreateJson(modname);
+ if (!meta)
+ return false;
+
+ (*meta)[key] = Json::Value(value);
+ m_modified.insert(modname);
+
+ return true;
+}
+
+bool ModMetadataDatabaseFiles::removeModEntry(const std::string &modname,
+ const std::string &key)
+{
+ Json::Value *meta = getOrCreateJson(modname);
+ if (!meta)
+ return false;
+
+ Json::Value removed;
+ if (meta->removeMember(key, &removed)) {
+ m_modified.insert(modname);
+ return true;
+ }
+ return false;
+}
+
+void ModMetadataDatabaseFiles::beginSave()
+{
+}
+
+void ModMetadataDatabaseFiles::endSave()
+{
+ if (m_modified.empty())
+ return;
+
+ if (!fs::CreateAllDirs(m_storage_dir)) {
+ errorstream << "ModMetadataDatabaseFiles: Unable to save. '"
+ << m_storage_dir << "' cannot be created." << std::endl;
+ return;
+ }
+ if (!fs::IsDir(m_storage_dir)) {
+ errorstream << "ModMetadataDatabaseFiles: Unable to save. '"
+ << m_storage_dir << "' is not a directory." << std::endl;
+ return;
+ }
+
+ for (auto it = m_modified.begin(); it != m_modified.end();) {
+ const std::string &modname = *it;
+
+ const Json::Value &json = m_mod_meta[modname];
+
+ if (!fs::safeWriteToFile(m_storage_dir + DIR_DELIM + modname, fastWriteJson(json))) {
+ errorstream << "ModMetadataDatabaseFiles[" << modname
+ << "]: failed to write file." << std::endl;
+ ++it;
+ continue;
+ }
+
+ it = m_modified.erase(it);
+ }
+}
+
+void ModMetadataDatabaseFiles::listMods(std::vector<std::string> *res)
+{
+ // List in-memory metadata first.
+ for (const auto &pair : m_mod_meta) {
+ res->push_back(pair.first);
+ }
+
+ // List other metadata present in the filesystem.
+ for (const auto &entry : fs::GetDirListing(m_storage_dir)) {
+ if (!entry.dir && m_mod_meta.count(entry.name) == 0)
+ res->push_back(entry.name);
+ }
+}
+
+Json::Value *ModMetadataDatabaseFiles::getOrCreateJson(const std::string &modname)
+{
+ auto found = m_mod_meta.find(modname);
+ if (found != m_mod_meta.end())
+ return &found->second;
+
+ Json::Value meta(Json::objectValue);
+
+ std::string path = m_storage_dir + DIR_DELIM + modname;
+ if (fs::PathExists(path)) {
+ std::ifstream is(path.c_str(), std::ios_base::binary);
+
+ Json::CharReaderBuilder builder;
+ builder.settings_["collectComments"] = false;
+ std::string errs;
+
+ if (!Json::parseFromStream(builder, is, &meta, &errs)) {
+ errorstream << "ModMetadataDatabaseFiles[" << modname
+ << "]: failed to decode data: " << errs << std::endl;
+ return nullptr;
+ }
+ }
+
+ return &(m_mod_meta[modname] = meta);
+}
diff --git a/src/database/database-files.h b/src/database/database-files.h
index e647a2e24..962e4d7bb 100644
--- a/src/database/database-files.h
+++ b/src/database/database-files.h
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include <unordered_map>
+#include <unordered_set>
+#include <json/json.h>
class PlayerDatabaseFiles : public PlayerDatabase
{
@@ -69,3 +71,27 @@ private:
bool readAuthFile();
bool writeAuthFile();
};
+
+class ModMetadataDatabaseFiles : public ModMetadataDatabase
+{
+public:
+ ModMetadataDatabaseFiles(const std::string &savedir);
+ virtual ~ModMetadataDatabaseFiles() = default;
+
+ virtual bool getModEntries(const std::string &modname, StringMap *storage);
+ virtual bool setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value);
+ virtual bool removeModEntry(const std::string &modname, const std::string &key);
+ virtual void listMods(std::vector<std::string> *res);
+
+ virtual void beginSave();
+ virtual void endSave();
+
+private:
+ Json::Value *getOrCreateJson(const std::string &modname);
+ bool writeJson(const std::string &modname, const Json::Value &json);
+
+ std::string m_storage_dir;
+ std::unordered_map<std::string, Json::Value> m_mod_meta;
+ std::unordered_set<std::string> m_modified;
+};
diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp
index 73cd63f6d..39f4c8442 100644
--- a/src/database/database-leveldb.cpp
+++ b/src/database/database-leveldb.cpp
@@ -70,11 +70,11 @@ bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block)
{
- std::string datastr;
leveldb::Status status = m_database->Get(leveldb::ReadOptions(),
- i64tos(getBlockAsInteger(pos)), &datastr);
+ i64tos(getBlockAsInteger(pos)), block);
- *block = (status.ok()) ? datastr : "";
+ if (!status.ok())
+ block->clear();
}
bool Database_LevelDB::deleteBlock(const v3s16 &pos)
@@ -131,7 +131,7 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player)
std::string (long) serialized_inventory
*/
- std::ostringstream os;
+ std::ostringstream os(std::ios_base::binary);
writeU8(os, 1);
PlayerSAO *sao = player->getPlayerSAO();
@@ -142,7 +142,7 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player)
writeF32(os, sao->getRotation().Y);
writeU16(os, sao->getBreath());
- StringMap stringvars = sao->getMeta().getStrings();
+ const auto &stringvars = sao->getMeta().getStrings();
writeU32(os, stringvars.size());
for (const auto &it : stringvars) {
os << serializeString16(it.first);
@@ -170,7 +170,7 @@ bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
player->getName(), &raw);
if (!s.ok())
return false;
- std::istringstream is(raw);
+ std::istringstream is(raw, std::ios_base::binary);
if (readU8(is) > 1)
return false;
@@ -230,7 +230,7 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res)
leveldb::Status s = m_database->Get(leveldb::ReadOptions(), name, &raw);
if (!s.ok())
return false;
- std::istringstream is(raw);
+ std::istringstream is(raw, std::ios_base::binary);
/*
u8 version = 1
@@ -262,7 +262,7 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res)
bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry)
{
- std::ostringstream os;
+ std::ostringstream os(std::ios_base::binary);
writeU8(os, 1);
os << serializeString16(authEntry.password);
diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp
index e1bb39928..3469f4242 100644
--- a/src/database/database-postgresql.cpp
+++ b/src/database/database-postgresql.cpp
@@ -39,20 +39,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "remoteplayer.h"
#include "server/player_sao.h"
-Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
+Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string,
+ const char *type) :
m_connect_string(connect_string)
{
if (m_connect_string.empty()) {
- throw SettingNotFoundException(
- "Set pgsql_connection string in world.mt to "
+ // Use given type to reference the exact setting in the error message
+ std::string s = type;
+ std::string msg =
+ "Set pgsql" + s + "_connection string in world.mt to "
"use the postgresql backend\n"
"Notes:\n"
- "pgsql_connection has the following form: \n"
- "\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user "
- "password=mt_password dbname=minetest_world\n"
+ "pgsql" + s + "_connection has the following form: \n"
+ "\tpgsql" + s + "_connection = host=127.0.0.1 port=5432 "
+ "user=mt_user password=mt_password dbname=minetest" + s + "\n"
"mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and "
- "DELETE rights on the database.\n"
- "Don't create mt_user as a SUPERUSER!");
+ "DELETE rights on the database. "
+ "Don't create mt_user as a SUPERUSER!";
+ throw SettingNotFoundException(msg);
}
}
@@ -166,7 +170,7 @@ void Database_PostgreSQL::rollback()
}
MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
- Database_PostgreSQL(connect_string),
+ Database_PostgreSQL(connect_string, ""),
MapDatabase()
{
connectToDatabase();
@@ -270,10 +274,10 @@ void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
PGresult *results = execPrepared("read_block", ARRLEN(args), args,
argLen, argFmt, false);
- *block = "";
-
if (PQntuples(results))
- *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
+ block->assign(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
+ else
+ block->clear();
PQclear(results);
}
@@ -315,7 +319,7 @@ void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
* Player Database
*/
PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
- Database_PostgreSQL(connect_string),
+ Database_PostgreSQL(connect_string, "_player"),
PlayerDatabase()
{
connectToDatabase();
@@ -492,6 +496,7 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
execPrepared("remove_player_inventory_items", 1, rmvalues);
std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+ std::ostringstream oss;
for (u16 i = 0; i < inventory_lists.size(); i++) {
const InventoryList* list = inventory_lists[i];
const std::string &name = list->getName();
@@ -508,9 +513,10 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
execPrepared("add_player_inventory", 5, inv_values);
for (u32 j = 0; j < list->getSize(); j++) {
- std::ostringstream os;
- list->getItem(j).serialize(os);
- std::string itemStr = os.str(), slotId = itos(j);
+ oss.str("");
+ oss.clear();
+ list->getItem(j).serialize(oss);
+ std::string itemStr = oss.str(), slotId = itos(j);
const char* invitem_values[] = {
player->getName(),
@@ -637,7 +643,8 @@ void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
}
AuthDatabasePostgreSQL::AuthDatabasePostgreSQL(const std::string &connect_string) :
- Database_PostgreSQL(connect_string), AuthDatabase()
+ Database_PostgreSQL(connect_string, "_auth"),
+ AuthDatabase()
{
connectToDatabase();
}
diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h
index f47deda33..81b4a2b10 100644
--- a/src/database/database-postgresql.h
+++ b/src/database/database-postgresql.h
@@ -29,7 +29,7 @@ class Settings;
class Database_PostgreSQL: public Database
{
public:
- Database_PostgreSQL(const std::string &connect_string);
+ Database_PostgreSQL(const std::string &connect_string, const char *type);
~Database_PostgreSQL();
void beginSave();
diff --git a/src/database/database-redis.cpp b/src/database/database-redis.cpp
index 096ea504d..5ffff67b7 100644
--- a/src/database/database-redis.cpp
+++ b/src/database/database-redis.cpp
@@ -127,8 +127,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block)
switch (reply->type) {
case REDIS_REPLY_STRING: {
- *block = std::string(reply->str, reply->len);
- // std::string copies the memory so this won't cause any problems
+ block->assign(reply->str, reply->len);
freeReplyObject(reply);
return;
}
@@ -141,8 +140,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block)
"Redis command 'HGET %s %s' errored: ") + errstr);
}
case REDIS_REPLY_NIL: {
- *block = "";
- // block not found in database
+ block->clear();
freeReplyObject(reply);
return;
}
diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp
index 4560743b9..1e63ae9d8 100644
--- a/src/database/database-sqlite3.cpp
+++ b/src/database/database-sqlite3.cpp
@@ -228,11 +228,7 @@ void MapDatabaseSQLite3::createDatabase()
void MapDatabaseSQLite3::initStatements()
{
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
-#ifdef __ANDROID__
- PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
-#else
PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
-#endif
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
@@ -265,19 +261,6 @@ bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data)
{
verifyDatabase();
-#ifdef __ANDROID__
- /**
- * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
- * deleting them and then inserting works.
- */
- bindPos(m_stmt_read, pos);
-
- if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
- deleteBlock(pos);
- }
- sqlite3_reset(m_stmt_read);
-#endif
-
bindPos(m_stmt_write, pos);
SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL),
"Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
@@ -302,7 +285,10 @@ void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block)
const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
size_t len = sqlite3_column_bytes(m_stmt_read, 0);
- *block = (data) ? std::string(data, len) : "";
+ if (data)
+ block->assign(data, len);
+ else
+ block->clear();
sqlite3_step(m_stmt_read);
// We should never get more than 1 row, so ok to reset
@@ -491,6 +477,7 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player)
sqlite3_reset(m_stmt_player_remove_inventory_items);
std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+ std::ostringstream oss;
for (u16 i = 0; i < inventory_lists.size(); i++) {
const InventoryList* list = inventory_lists[i];
@@ -503,9 +490,10 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player)
sqlite3_reset(m_stmt_player_add_inventory);
for (u32 j = 0; j < list->getSize(); j++) {
- std::ostringstream os;
- list->getItem(j).serialize(os);
- std::string itemStr = os.str();
+ oss.str("");
+ oss.clear();
+ list->getItem(j).serialize(oss);
+ std::string itemStr = oss.str();
str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName());
int_to_sqlite(m_stmt_player_add_inventory_items, 2, i);
@@ -774,3 +762,108 @@ void AuthDatabaseSQLite3::writePrivileges(const AuthEntry &authEntry)
sqlite3_reset(m_stmt_write_privs);
}
}
+
+ModMetadataDatabaseSQLite3::ModMetadataDatabaseSQLite3(const std::string &savedir):
+ Database_SQLite3(savedir, "mod_storage"), ModMetadataDatabase()
+{
+}
+
+ModMetadataDatabaseSQLite3::~ModMetadataDatabaseSQLite3()
+{
+ FINALIZE_STATEMENT(m_stmt_remove)
+ FINALIZE_STATEMENT(m_stmt_set)
+ FINALIZE_STATEMENT(m_stmt_get)
+}
+
+void ModMetadataDatabaseSQLite3::createDatabase()
+{
+ assert(m_database); // Pre-condition
+
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `entries` (\n"
+ " `modname` TEXT NOT NULL,\n"
+ " `key` BLOB NOT NULL,\n"
+ " `value` BLOB NOT NULL,\n"
+ " PRIMARY KEY (`modname`, `key`)\n"
+ ");\n",
+ NULL, NULL, NULL),
+ "Failed to create database table");
+}
+
+void ModMetadataDatabaseSQLite3::initStatements()
+{
+ PREPARE_STATEMENT(get, "SELECT `key`, `value` FROM `entries` WHERE `modname` = ?");
+ PREPARE_STATEMENT(set,
+ "REPLACE INTO `entries` (`modname`, `key`, `value`) VALUES (?, ?, ?)");
+ PREPARE_STATEMENT(remove, "DELETE FROM `entries` WHERE `modname` = ? AND `key` = ?");
+}
+
+bool ModMetadataDatabaseSQLite3::getModEntries(const std::string &modname, StringMap *storage)
+{
+ verifyDatabase();
+
+ str_to_sqlite(m_stmt_get, 1, modname);
+ while (sqlite3_step(m_stmt_get) == SQLITE_ROW) {
+ const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get, 0);
+ size_t key_len = sqlite3_column_bytes(m_stmt_get, 0);
+ const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get, 1);
+ size_t value_len = sqlite3_column_bytes(m_stmt_get, 1);
+ (*storage)[std::string(key_data, key_len)] = std::string(value_data, value_len);
+ }
+ sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE);
+
+ sqlite3_reset(m_stmt_get);
+
+ return true;
+}
+
+bool ModMetadataDatabaseSQLite3::setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value)
+{
+ verifyDatabase();
+
+ str_to_sqlite(m_stmt_set, 1, modname);
+ SQLOK(sqlite3_bind_blob(m_stmt_set, 2, key.data(), key.size(), NULL),
+ "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
+ SQLOK(sqlite3_bind_blob(m_stmt_set, 3, value.data(), value.size(), NULL),
+ "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
+ SQLRES(sqlite3_step(m_stmt_set), SQLITE_DONE, "Failed to set mod entry")
+
+ sqlite3_reset(m_stmt_set);
+
+ return true;
+}
+
+bool ModMetadataDatabaseSQLite3::removeModEntry(const std::string &modname,
+ const std::string &key)
+{
+ verifyDatabase();
+
+ str_to_sqlite(m_stmt_remove, 1, modname);
+ SQLOK(sqlite3_bind_blob(m_stmt_remove, 2, key.data(), key.size(), NULL),
+ "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
+ sqlite3_vrfy(sqlite3_step(m_stmt_remove), SQLITE_DONE);
+ int changes = sqlite3_changes(m_database);
+
+ sqlite3_reset(m_stmt_remove);
+
+ return changes > 0;
+}
+
+void ModMetadataDatabaseSQLite3::listMods(std::vector<std::string> *res)
+{
+ verifyDatabase();
+
+ char *errmsg;
+ int status = sqlite3_exec(m_database,
+ "SELECT `modname` FROM `entries` GROUP BY `modname`;",
+ [](void *res_vp, int n_col, char **cols, char **col_names) -> int {
+ ((decltype(res)) res_vp)->emplace_back(cols[0]);
+ return 0;
+ }, (void *) res, &errmsg);
+ if (status != SQLITE_OK) {
+ DatabaseException e(std::string("Error trying to list mods with metadata: ") + errmsg);
+ sqlite3_free(errmsg);
+ throw e;
+ }
+}
diff --git a/src/database/database-sqlite3.h b/src/database/database-sqlite3.h
index d7202a918..5e3d7c96c 100644
--- a/src/database/database-sqlite3.h
+++ b/src/database/database-sqlite3.h
@@ -232,3 +232,28 @@ private:
sqlite3_stmt *m_stmt_delete_privs = nullptr;
sqlite3_stmt *m_stmt_last_insert_rowid = nullptr;
};
+
+class ModMetadataDatabaseSQLite3 : private Database_SQLite3, public ModMetadataDatabase
+{
+public:
+ ModMetadataDatabaseSQLite3(const std::string &savedir);
+ virtual ~ModMetadataDatabaseSQLite3();
+
+ virtual bool getModEntries(const std::string &modname, StringMap *storage);
+ virtual bool setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value);
+ virtual bool removeModEntry(const std::string &modname, const std::string &key);
+ virtual void listMods(std::vector<std::string> *res);
+
+ virtual void beginSave() { Database_SQLite3::beginSave(); }
+ virtual void endSave() { Database_SQLite3::endSave(); }
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ sqlite3_stmt *m_stmt_get = nullptr;
+ sqlite3_stmt *m_stmt_set = nullptr;
+ sqlite3_stmt *m_stmt_remove = nullptr;
+};
diff --git a/src/database/database.h b/src/database/database.h
index b7d551935..fbb5befea 100644
--- a/src/database/database.h
+++ b/src/database/database.h
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h"
#include "irrlichttypes.h"
#include "util/basic_macros.h"
+#include "util/string.h"
class Database
{
@@ -84,3 +85,15 @@ public:
virtual void listNames(std::vector<std::string> &res) = 0;
virtual void reload() = 0;
};
+
+class ModMetadataDatabase : public Database
+{
+public:
+ virtual ~ModMetadataDatabase() = default;
+
+ virtual bool getModEntries(const std::string &modname, StringMap *storage) = 0;
+ virtual bool setModEntry(const std::string &modname,
+ const std::string &key, const std::string &value) = 0;
+ virtual bool removeModEntry(const std::string &modname, const std::string &key) = 0;
+ virtual void listMods(std::vector<std::string> *res) = 0;
+};