From bf22569019749e421e8ffe0a73cff988a9a9c846 Mon Sep 17 00:00:00 2001 From: Jude Melton-Houghton Date: Fri, 7 Jan 2022 13:28:49 -0500 Subject: Use a database for mod storage (#11763) --- src/database/database-sqlite3.cpp | 105 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) (limited to 'src/database/database-sqlite3.cpp') diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 898acc265..e9442118e 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -779,3 +779,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 *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; + } +} -- cgit v1.2.3 From 66e8aae9f2a28ee31ffe30694fdb61a8fdceb8d7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 19 Jan 2022 22:42:53 +0100 Subject: Get rid of legacy workaround in SQLite backend tested on Android 11, fixes #11937 --- src/database/database-sqlite3.cpp | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'src/database/database-sqlite3.cpp') diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index e9442118e..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__)); -- cgit v1.2.3 From 8d387433b14791db95e59127b5e6e30f58155c1e Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 29 Mar 2022 18:06:16 +0200 Subject: Fix the documentation of InvRef:get_lists() and clean up code (#12150) --- doc/lua_api.txt | 4 ++-- src/database/database-postgresql.cpp | 2 +- src/database/database-sqlite3.cpp | 4 ++-- src/inventory.cpp | 42 ------------------------------------ src/inventory.h | 26 +++++++++++++++------- src/script/common/c_content.cpp | 23 ++++++++++++-------- src/script/common/c_content.h | 6 ++++-- src/script/cpp_api/s_client.cpp | 10 +-------- src/script/lua_api/l_inventory.cpp | 23 +++++++++----------- src/script/lua_api/l_nodemeta.cpp | 12 ++++------- 10 files changed, 56 insertions(+), 96 deletions(-) (limited to 'src/database/database-sqlite3.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 029c53616..161bdd249 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -6355,9 +6355,9 @@ An `InvRef` is a reference to an inventory. * `set_width(listname, width)`: set width of list; currently used for crafting * `get_stack(listname, i)`: get a copy of stack index `i` in list * `set_stack(listname, i, stack)`: copy `stack` to index `i` in list -* `get_list(listname)`: return full list +* `get_list(listname)`: return full list (list of `ItemStack`s) * `set_list(listname, list)`: set full list (size will not change) -* `get_lists()`: returns list of inventory lists +* `get_lists()`: returns table that maps listnames to inventory lists * `set_lists(lists)`: sets inventory lists (size will not change) * `add_item(listname, stack)`: add item somewhere in list, returns leftover `ItemStack`. diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index 3469f4242..9d6501e68 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -495,7 +495,7 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) execPrepared("remove_player_inventories", 1, rmvalues); execPrepared("remove_player_inventory_items", 1, rmvalues); - std::vector inventory_lists = sao->getInventory()->getLists(); + const auto &inventory_lists = sao->getInventory()->getLists(); std::ostringstream oss; for (u16 i = 0; i < inventory_lists.size(); i++) { const InventoryList* list = inventory_lists[i]; diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 1e63ae9d8..9521085e9 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -476,10 +476,10 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory_items), SQLITE_DONE); sqlite3_reset(m_stmt_player_remove_inventory_items); - std::vector inventory_lists = sao->getInventory()->getLists(); + const auto &inventory_lists = sao->getInventory()->getLists(); std::ostringstream oss; for (u16 i = 0; i < inventory_lists.size(); i++) { - const InventoryList* list = inventory_lists[i]; + const InventoryList *list = inventory_lists[i]; str_to_sqlite(m_stmt_player_add_inventory, 1, player->getName()); int_to_sqlite(m_stmt_player_add_inventory, 2, i); diff --git a/src/inventory.cpp b/src/inventory.cpp index d14b12f9d..6d2b7fba3 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -503,11 +503,6 @@ void InventoryList::deSerialize(std::istream &is) throw SerializationError(ss.str()); } -InventoryList::InventoryList(const InventoryList &other) -{ - *this = other; -} - InventoryList & InventoryList::operator = (const InventoryList &other) { m_items = other.m_items; @@ -535,21 +530,6 @@ bool InventoryList::operator == (const InventoryList &other) const return true; } -const std::string &InventoryList::getName() const -{ - return m_name; -} - -u32 InventoryList::getSize() const -{ - return m_items.size(); -} - -u32 InventoryList::getWidth() const -{ - return m_width; -} - u32 InventoryList::getUsedSlots() const { u32 num = 0; @@ -560,18 +540,6 @@ u32 InventoryList::getUsedSlots() const return num; } -const ItemStack& InventoryList::getItem(u32 i) const -{ - assert(i < m_size); // Pre-condition - return m_items[i]; -} - -ItemStack& InventoryList::getItem(u32 i) -{ - assert(i < m_size); // Pre-condition - return m_items[i]; -} - ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem) { if(i >= m_items.size()) @@ -960,16 +928,6 @@ InventoryList * Inventory::getList(const std::string &name) return m_lists[i]; } -std::vector Inventory::getLists() -{ - std::vector lists; - lists.reserve(m_lists.size()); - for (auto list : m_lists) { - lists.push_back(list); - } - return lists; -} - bool Inventory::deleteList(const std::string &name) { s32 i = getListIndex(name); diff --git a/src/inventory.h b/src/inventory.h index eb063d4ad..8b31de3a8 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -198,7 +198,7 @@ public: void serialize(std::ostream &os, bool incremental) const; void deSerialize(std::istream &is); - InventoryList(const InventoryList &other); + InventoryList(const InventoryList &other) { *this = other; } InventoryList & operator = (const InventoryList &other); bool operator == (const InventoryList &other) const; bool operator != (const InventoryList &other) const @@ -206,15 +206,25 @@ public: return !(*this == other); } - const std::string &getName() const; - u32 getSize() const; - u32 getWidth() const; + const std::string &getName() const { return m_name; } + u32 getSize() const { return static_cast(m_items.size()); } + u32 getWidth() const { return m_width; } // Count used slots u32 getUsedSlots() const; // Get reference to item - const ItemStack& getItem(u32 i) const; - ItemStack& getItem(u32 i); + const ItemStack &getItem(u32 i) const + { + assert(i < m_size); // Pre-condition + return m_items[i]; + } + ItemStack &getItem(u32 i) + { + assert(i < m_size); // Pre-condition + return m_items[i]; + } + // Get reference to all items + const std::vector &getItems() const { return m_items; } // Returns old item. Parameter can be an empty item. ItemStack changeItem(u32 i, const ItemStack &newitem); // Delete item @@ -271,7 +281,7 @@ public: private: std::vector m_items; std::string m_name; - u32 m_size; + u32 m_size; // always the same as m_items.size() u32 m_width = 0; IItemDefManager *m_itemdef; bool m_dirty = true; @@ -301,7 +311,7 @@ public: InventoryList * addList(const std::string &name, u32 size); InventoryList * getList(const std::string &name); const InventoryList * getList(const std::string &name) const; - std::vector getLists(); + const std::vector &getLists() const { return m_lists; } bool deleteList(const std::string &name); // A shorthand for adding items. Returns leftover item (possibly empty). ItemStack addItem(const std::string &listname, const ItemStack &newitem) diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index b6eaa6b13..36f4316ee 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1351,17 +1351,22 @@ void push_tool_capabilities(lua_State *L, } /******************************************************************************/ -void push_inventory_list(lua_State *L, Inventory *inv, const char *name) +void push_inventory_list(lua_State *L, const InventoryList &invlist) { - InventoryList *invlist = inv->getList(name); - if(invlist == NULL){ - lua_pushnil(L); - return; + push_items(L, invlist.getItems()); +} + +/******************************************************************************/ +void push_inventory_lists(lua_State *L, const Inventory &inv) +{ + const auto &lists = inv.getLists(); + lua_createtable(L, 0, lists.size()); + for(const InventoryList *list : lists) { + const std::string &name = list->getName(); + lua_pushlstring(L, name.c_str(), name.size()); + push_inventory_list(L, *list); + lua_rawset(L, -3); } - std::vector items; - for(u32 i=0; igetSize(); i++) - items.push_back(invlist->getItem(i)); - push_items(L, items); } /******************************************************************************/ diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index e762604a4..11b39364f 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -55,6 +55,7 @@ struct ObjectProperties; struct SimpleSoundSpec; struct ServerSoundParams; class Inventory; +class InventoryList; struct NodeBox; struct ContentFeatures; struct TileDef; @@ -120,8 +121,9 @@ void push_object_properties (lua_State *L, ObjectProperties *prop); void push_inventory_list (lua_State *L, - Inventory *inv, - const char *name); + const InventoryList &invlist); +void push_inventory_lists (lua_State *L, + const Inventory &inv); void read_inventory_list (lua_State *L, int tableindex, Inventory *inv, const char *name, Server *srv, int forcesize=-1); diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index c889fffa0..b02a0c7be 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -281,15 +281,7 @@ bool ScriptApiClient::on_inventory_open(Inventory *inventory) lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_inventory_open"); - std::vector lists = inventory->getLists(); - std::vector::iterator iter = lists.begin(); - lua_createtable(L, 0, lists.size()); - for (; iter != lists.end(); iter++) { - const char* name = (*iter)->getName().c_str(); - lua_pushstring(L, name); - push_inventory_list(L, inventory, name); - lua_rawset(L, -3); - } + push_inventory_lists(L, *inventory); try { runCallbacks(1, RUN_CALLBACKS_MODE_OR); diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index b0a4ee194..175047e58 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -214,11 +214,16 @@ int InvRef::l_get_list(lua_State *L) InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); Inventory *inv = getinv(L, ref); - if(inv){ - push_inventory_list(L, inv, listname); - } else { + if (!inv) { lua_pushnil(L); + return 1; } + InventoryList *invlist = inv->getList(listname); + if (!invlist) { + lua_pushnil(L); + return 1; + } + push_inventory_list(L, *invlist); return 1; } @@ -242,7 +247,7 @@ int InvRef::l_set_list(lua_State *L) return 0; } -// get_lists(self) -> list of InventoryLists +// get_lists(self) -> table that maps listnames to InventoryLists int InvRef::l_get_lists(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -251,15 +256,7 @@ int InvRef::l_get_lists(lua_State *L) if (!inv) { return 0; } - std::vector lists = inv->getLists(); - std::vector::iterator iter = lists.begin(); - lua_createtable(L, 0, lists.size()); - for (; iter != lists.end(); iter++) { - const char* name = (*iter)->getName().c_str(); - lua_pushstring(L, name); - push_inventory_list(L, inv, name); - lua_rawset(L, -3); - } + push_inventory_lists(L, *inv); return 1; } diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 34760157d..1d052685e 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -127,18 +127,14 @@ void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta) // fields MetaDataRef::handleToTable(L, _meta); - NodeMetadata *meta = (NodeMetadata*) _meta; + NodeMetadata *meta = (NodeMetadata *) _meta; // inventory - lua_newtable(L); Inventory *inv = meta->getInventory(); if (inv) { - std::vector lists = inv->getLists(); - for(std::vector::const_iterator - i = lists.begin(); i != lists.end(); ++i) { - push_inventory_list(L, inv, (*i)->getName().c_str()); - lua_setfield(L, -2, (*i)->getName().c_str()); - } + push_inventory_lists(L, *inv); + } else { + lua_newtable(L); } lua_setfield(L, -2, "inventory"); } -- cgit v1.2.3