Avoid duplication of mod metadata in memory (#12562)

Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
Jude Melton-Houghton 2022-09-26 17:03:43 -04:00 committed by GitHub
parent 03428d9825
commit f4a01f3a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 527 additions and 272 deletions

@ -25,6 +25,7 @@ core.features = {
dynamic_add_media_table = true, dynamic_add_media_table = true,
get_sky_as_table = true, get_sky_as_table = true,
get_light_data_buffer = true, get_light_data_buffer = true,
mod_storage_on_disk = true,
} }
function core.has_feature(arg) function core.has_feature(arg)

@ -4904,6 +4904,10 @@ Utilities
get_sky_as_table = true, get_sky_as_table = true,
-- VoxelManip:get_light_data accepts an optional buffer argument (5.7.0) -- VoxelManip:get_light_data accepts an optional buffer argument (5.7.0)
get_light_data_buffer = true, get_light_data_buffer = true,
-- When using a mod storage backend that is not "files" or "dummy",
-- the amount of data in mod storage is not constrained by
-- the amount of RAM available. (5.7.0)
mod_storage_on_disk = true,
} }
* `minetest.has_feature(arg)`: returns `boolean, missing_features` * `minetest.has_feature(arg)`: returns `boolean, missing_features`

@ -179,6 +179,7 @@ dofile(modpath .. "/itemdescription.lua")
dofile(modpath .. "/async_env.lua") dofile(modpath .. "/async_env.lua")
dofile(modpath .. "/entity.lua") dofile(modpath .. "/entity.lua")
dofile(modpath .. "/content_ids.lua") dofile(modpath .. "/content_ids.lua")
dofile(modpath .. "/metadata.lua")
-------------- --------------

@ -0,0 +1,79 @@
-- Tests of generic and specific metadata functionality
local compare_meta = ItemStack("unittests:iron_lump"):get_meta()
compare_meta:from_table({
fields = {
a = "1",
b = "2",
c = "3",
d = "4",
e = "e",
},
})
local function test_metadata(meta)
meta:from_table({fields = {a = 1, b = "2"}})
meta:set_string("c", "3")
meta:set_int("d", 4)
meta:set_string("e", "e")
meta:set_string("", "!")
meta:set_string("", "")
assert(meta:equals(compare_meta))
local tab = meta:to_table()
assert(tab.fields.a == "1")
assert(tab.fields.b == "2")
assert(tab.fields.c == "3")
assert(tab.fields.d == "4")
assert(tab.fields.e == "e")
assert(not meta:contains(""))
assert(meta:contains("a"))
assert(meta:contains("b"))
assert(meta:contains("c"))
assert(meta:contains("d"))
assert(meta:contains("e"))
assert(meta:get("") == nil)
assert(meta:get_string("") == "")
assert(meta:get_int("") == 0)
assert(meta:get_float("") == 0.0)
assert(meta:get("a") == "1")
assert(meta:get_string("a") == "1")
assert(meta:get_int("a") == 1)
assert(meta:get_float("a") == 1.0)
assert(meta:get_int("e") == 0)
assert(meta:get_float("e") == 0.0)
meta:set_float("f", 1.1)
meta:set_string("g", "${f}")
meta:set_string("h", "${g}")
meta:set_string("i", "${h}")
assert(meta:get_float("h") > 1)
assert(meta:get_string("i") == "${f}")
meta:from_table()
assert(next(meta:to_table().fields) == nil)
assert(not meta:equals(compare_meta))
end
local storage_a = core.get_mod_storage()
local storage_b = core.get_mod_storage()
local function test_mod_storage()
assert(rawequal(storage_a, storage_b))
test_metadata(storage_a)
end
unittests.register("test_mod_storage", test_mod_storage)
local function test_item_metadata()
test_metadata(ItemStack("unittest:coal_lump"):get_meta())
end
unittests.register("test_item_metadata", test_item_metadata)
local function test_node_metadata(player, pos)
test_metadata(minetest.get_meta(pos))
end
unittests.register("test_node_metadata", test_node_metadata, {map=true})

@ -1991,26 +1991,6 @@ const std::string* Client::getModFile(std::string filename)
return &it->second; return &it->second;
} }
bool Client::registerModStorage(ModMetadata *storage)
{
if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
errorstream << "Unable to register same mod storage twice. Storage name: "
<< storage->getModName() << std::endl;
return false;
}
m_mod_storages[storage->getModName()] = storage;
return true;
}
void Client::unregisterModStorage(const std::string &name)
{
std::unordered_map<std::string, ModMetadata *>::const_iterator it =
m_mod_storages.find(name);
if (it != m_mod_storages.end())
m_mod_storages.erase(name);
}
/* /*
* Mod channels * Mod channels
*/ */

@ -383,9 +383,6 @@ public:
const std::string* getModFile(std::string filename); const std::string* getModFile(std::string filename);
ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; } ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; }
bool registerModStorage(ModMetadata *meta) override;
void unregisterModStorage(const std::string &name) override;
// Migrates away old files-based mod storage if necessary // Migrates away old files-based mod storage if necessary
void migrateModStorage(); void migrateModStorage();
@ -593,7 +590,6 @@ private:
// Client modding // Client modding
ClientScripting *m_script = nullptr; ClientScripting *m_script = nullptr;
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
ModMetadataDatabase *m_mod_storage_database = nullptr; ModMetadataDatabase *m_mod_storage_database = nullptr;
float m_mod_storage_save_timer = 10.0f; float m_mod_storage_save_timer = 10.0f;
std::vector<ModSpec> m_mods; std::vector<ModSpec> m_mods;

@ -220,26 +220,34 @@ std::vector<ModSpec> flattenMods(const std::map<std::string, ModSpec> &mods)
ModMetadata::ModMetadata(const std::string &mod_name, ModMetadataDatabase *database): ModMetadata::ModMetadata(const std::string &mod_name, ModMetadataDatabase *database):
m_mod_name(mod_name), m_database(database) m_mod_name(mod_name), m_database(database)
{ {
m_database->getModEntries(m_mod_name, &m_stringvars);
} }
void ModMetadata::clear() void ModMetadata::clear()
{ {
for (const auto &pair : m_stringvars) { m_database->removeModEntries(m_mod_name);
m_database->removeModEntry(m_mod_name, pair.first); }
}
Metadata::clear(); bool ModMetadata::contains(const std::string &name) const
{
return m_database->hasModEntry(m_mod_name, name);
} }
bool ModMetadata::setString(const std::string &name, const std::string &var) bool ModMetadata::setString(const std::string &name, const std::string &var)
{ {
if (Metadata::setString(name, var)) { if (var.empty())
if (var.empty()) { return m_database->removeModEntry(m_mod_name, name);
m_database->removeModEntry(m_mod_name, name); else
} else { return m_database->setModEntry(m_mod_name, name, var);
m_database->setModEntry(m_mod_name, name, var); }
}
return true; const StringMap &ModMetadata::getStrings(StringMap *place) const
} {
return false; place->clear();
m_database->getModEntries(m_mod_name, place);
return *place;
}
const std::string *ModMetadata::getStringRaw(const std::string &name, std::string *place) const
{
return m_database->getModEntry(m_mod_name, name, place) ? place : nullptr;
} }

@ -110,18 +110,26 @@ std::map<std::string, ModSpec> getModsInPath(const std::string &path,
std::vector<ModSpec> flattenMods(const std::map<std::string, ModSpec> &mods); std::vector<ModSpec> flattenMods(const std::map<std::string, ModSpec> &mods);
class ModMetadata : public Metadata class ModMetadata : public IMetadata
{ {
public: public:
ModMetadata() = delete; ModMetadata() = delete;
ModMetadata(const std::string &mod_name, ModMetadataDatabase *database); ModMetadata(const std::string &mod_name, ModMetadataDatabase *database);
~ModMetadata() = default; ~ModMetadata() = default;
virtual void clear();
const std::string &getModName() const { return m_mod_name; } const std::string &getModName() const { return m_mod_name; }
virtual bool setString(const std::string &name, const std::string &var); void clear() override;
bool contains(const std::string &name) const override;
bool setString(const std::string &name, const std::string &var) override;
const StringMap &getStrings(StringMap *place) const override;
protected:
const std::string *getStringRaw(const std::string &name,
std::string *place) const override;
private: private:
std::string m_mod_name; std::string m_mod_name;

@ -92,6 +92,32 @@ bool Database_Dummy::getModEntries(const std::string &modname, StringMap *storag
return true; return true;
} }
bool Database_Dummy::getModEntry(const std::string &modname,
const std::string &key, std::string *value)
{
auto mod_pair = m_mod_meta_database.find(modname);
if (mod_pair == m_mod_meta_database.end())
return false;
const StringMap &meta = mod_pair->second;
auto pair = meta.find(key);
if (pair != meta.end()) {
*value = pair->second;
return true;
}
return false;
}
bool Database_Dummy::hasModEntry(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 false;
const StringMap &meta = mod_pair->second;
return meta.find(key) != meta.cend();
}
bool Database_Dummy::setModEntry(const std::string &modname, bool Database_Dummy::setModEntry(const std::string &modname,
const std::string &key, const std::string &value) const std::string &key, const std::string &value)
{ {
@ -112,6 +138,16 @@ bool Database_Dummy::removeModEntry(const std::string &modname, const std::strin
return false; return false;
} }
bool Database_Dummy::removeModEntries(const std::string &modname)
{
auto mod_pair = m_mod_meta_database.find(modname);
if (mod_pair != m_mod_meta_database.end() && !mod_pair->second.empty()) {
mod_pair->second.clear();
return true;
}
return false;
}
void Database_Dummy::listMods(std::vector<std::string> *res) void Database_Dummy::listMods(std::vector<std::string> *res)
{ {
for (const auto &pair : m_mod_meta_database) { for (const auto &pair : m_mod_meta_database) {

@ -38,9 +38,13 @@ public:
void listPlayers(std::vector<std::string> &res); void listPlayers(std::vector<std::string> &res);
bool getModEntries(const std::string &modname, StringMap *storage); bool getModEntries(const std::string &modname, StringMap *storage);
bool getModEntry(const std::string &modname,
const std::string &key, std::string *value);
bool hasModEntry(const std::string &modname, const std::string &key);
bool setModEntry(const std::string &modname, bool setModEntry(const std::string &modname,
const std::string &key, const std::string &value); const std::string &key, const std::string &value);
bool removeModEntry(const std::string &modname, const std::string &key); bool removeModEntry(const std::string &modname, const std::string &key);
bool removeModEntries(const std::string &modname);
void listMods(std::vector<std::string> *res); void listMods(std::vector<std::string> *res);
void beginSave() {} void beginSave() {}

@ -396,6 +396,26 @@ bool ModMetadataDatabaseFiles::getModEntries(const std::string &modname, StringM
return true; return true;
} }
bool ModMetadataDatabaseFiles::getModEntry(const std::string &modname,
const std::string &key, std::string *value)
{
Json::Value *meta = getOrCreateJson(modname);
if (!meta)
return false;
if (meta->isMember(key)) {
*value = (*meta)[key].asString();
return true;
}
return false;
}
bool ModMetadataDatabaseFiles::hasModEntry(const std::string &modname, const std::string &key)
{
Json::Value *meta = getOrCreateJson(modname);
return meta && meta->isMember(key);
}
bool ModMetadataDatabaseFiles::setModEntry(const std::string &modname, bool ModMetadataDatabaseFiles::setModEntry(const std::string &modname,
const std::string &key, const std::string &value) const std::string &key, const std::string &value)
{ {
@ -424,6 +444,17 @@ bool ModMetadataDatabaseFiles::removeModEntry(const std::string &modname,
return false; return false;
} }
bool ModMetadataDatabaseFiles::removeModEntries(const std::string &modname)
{
Json::Value *meta = getOrCreateJson(modname);
if (!meta || meta->empty())
return false;
meta->clear();
m_modified.insert(modname);
return true;
}
void ModMetadataDatabaseFiles::beginSave() void ModMetadataDatabaseFiles::beginSave()
{ {
} }

@ -79,9 +79,13 @@ public:
virtual ~ModMetadataDatabaseFiles() = default; virtual ~ModMetadataDatabaseFiles() = default;
virtual bool getModEntries(const std::string &modname, StringMap *storage); virtual bool getModEntries(const std::string &modname, StringMap *storage);
virtual bool getModEntry(const std::string &modname,
const std::string &key, std::string *value);
virtual bool hasModEntry(const std::string &modname, const std::string &key);
virtual bool setModEntry(const std::string &modname, virtual bool setModEntry(const std::string &modname,
const std::string &key, const std::string &value); const std::string &key, const std::string &value);
virtual bool removeModEntry(const std::string &modname, const std::string &key); virtual bool removeModEntry(const std::string &modname, const std::string &key);
virtual bool removeModEntries(const std::string &modname);
virtual void listMods(std::vector<std::string> *res); virtual void listMods(std::vector<std::string> *res);
virtual void beginSave(); virtual void beginSave();

@ -770,9 +770,12 @@ ModMetadataDatabaseSQLite3::ModMetadataDatabaseSQLite3(const std::string &savedi
ModMetadataDatabaseSQLite3::~ModMetadataDatabaseSQLite3() ModMetadataDatabaseSQLite3::~ModMetadataDatabaseSQLite3()
{ {
FINALIZE_STATEMENT(m_stmt_remove_all)
FINALIZE_STATEMENT(m_stmt_remove) FINALIZE_STATEMENT(m_stmt_remove)
FINALIZE_STATEMENT(m_stmt_set) FINALIZE_STATEMENT(m_stmt_set)
FINALIZE_STATEMENT(m_stmt_has)
FINALIZE_STATEMENT(m_stmt_get) FINALIZE_STATEMENT(m_stmt_get)
FINALIZE_STATEMENT(m_stmt_get_all)
} }
void ModMetadataDatabaseSQLite3::createDatabase() void ModMetadataDatabaseSQLite3::createDatabase()
@ -792,31 +795,74 @@ void ModMetadataDatabaseSQLite3::createDatabase()
void ModMetadataDatabaseSQLite3::initStatements() void ModMetadataDatabaseSQLite3::initStatements()
{ {
PREPARE_STATEMENT(get, "SELECT `key`, `value` FROM `entries` WHERE `modname` = ?"); PREPARE_STATEMENT(get_all, "SELECT `key`, `value` FROM `entries` WHERE `modname` = ?");
PREPARE_STATEMENT(get,
"SELECT `value` FROM `entries` WHERE `modname` = ? AND `key` = ? LIMIT 1");
PREPARE_STATEMENT(has,
"SELECT 1 FROM `entries` WHERE `modname` = ? AND `key` = ? LIMIT 1");
PREPARE_STATEMENT(set, PREPARE_STATEMENT(set,
"REPLACE INTO `entries` (`modname`, `key`, `value`) VALUES (?, ?, ?)"); "REPLACE INTO `entries` (`modname`, `key`, `value`) VALUES (?, ?, ?)");
PREPARE_STATEMENT(remove, "DELETE FROM `entries` WHERE `modname` = ? AND `key` = ?"); PREPARE_STATEMENT(remove, "DELETE FROM `entries` WHERE `modname` = ? AND `key` = ?");
PREPARE_STATEMENT(remove_all, "DELETE FROM `entries` WHERE `modname` = ?");
} }
bool ModMetadataDatabaseSQLite3::getModEntries(const std::string &modname, StringMap *storage) bool ModMetadataDatabaseSQLite3::getModEntries(const std::string &modname, StringMap *storage)
{ {
verifyDatabase(); verifyDatabase();
str_to_sqlite(m_stmt_get, 1, modname); str_to_sqlite(m_stmt_get_all, 1, modname);
while (sqlite3_step(m_stmt_get) == SQLITE_ROW) { while (sqlite3_step(m_stmt_get_all) == SQLITE_ROW) {
const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get, 0); const char *key_data = (const char *) sqlite3_column_blob(m_stmt_get_all, 0);
size_t key_len = sqlite3_column_bytes(m_stmt_get, 0); size_t key_len = sqlite3_column_bytes(m_stmt_get_all, 0);
const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get, 1); const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get_all, 1);
size_t value_len = sqlite3_column_bytes(m_stmt_get, 1); size_t value_len = sqlite3_column_bytes(m_stmt_get_all, 1);
(*storage)[std::string(key_data, key_len)] = std::string(value_data, value_len); (*storage)[std::string(key_data, key_len)] = std::string(value_data, value_len);
} }
sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE); sqlite3_vrfy(sqlite3_errcode(m_database), SQLITE_DONE);
sqlite3_reset(m_stmt_get); sqlite3_reset(m_stmt_get_all);
return true; return true;
} }
bool ModMetadataDatabaseSQLite3::getModEntry(const std::string &modname,
const std::string &key, std::string *value)
{
verifyDatabase();
str_to_sqlite(m_stmt_get, 1, modname);
SQLOK(sqlite3_bind_blob(m_stmt_get, 2, key.data(), key.size(), NULL),
"Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
bool found = sqlite3_step(m_stmt_get) == SQLITE_ROW;
if (found) {
const char *value_data = (const char *) sqlite3_column_blob(m_stmt_get, 0);
size_t value_len = sqlite3_column_bytes(m_stmt_get, 0);
value->assign(value_data, value_len);
sqlite3_step(m_stmt_get);
}
sqlite3_reset(m_stmt_get);
return found;
}
bool ModMetadataDatabaseSQLite3::hasModEntry(const std::string &modname,
const std::string &key)
{
verifyDatabase();
str_to_sqlite(m_stmt_has, 1, modname);
SQLOK(sqlite3_bind_blob(m_stmt_has, 2, key.data(), key.size(), NULL),
"Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
bool found = sqlite3_step(m_stmt_has) == SQLITE_ROW;
if (found)
sqlite3_step(m_stmt_has);
sqlite3_reset(m_stmt_has);
return found;
}
bool ModMetadataDatabaseSQLite3::setModEntry(const std::string &modname, bool ModMetadataDatabaseSQLite3::setModEntry(const std::string &modname,
const std::string &key, const std::string &value) const std::string &key, const std::string &value)
{ {
@ -850,6 +896,19 @@ bool ModMetadataDatabaseSQLite3::removeModEntry(const std::string &modname,
return changes > 0; return changes > 0;
} }
bool ModMetadataDatabaseSQLite3::removeModEntries(const std::string &modname)
{
verifyDatabase();
str_to_sqlite(m_stmt_remove_all, 1, modname);
sqlite3_vrfy(sqlite3_step(m_stmt_remove_all), SQLITE_DONE);
int changes = sqlite3_changes(m_database);
sqlite3_reset(m_stmt_remove_all);
return changes > 0;
}
void ModMetadataDatabaseSQLite3::listMods(std::vector<std::string> *res) void ModMetadataDatabaseSQLite3::listMods(std::vector<std::string> *res)
{ {
verifyDatabase(); verifyDatabase();

@ -240,9 +240,13 @@ public:
virtual ~ModMetadataDatabaseSQLite3(); virtual ~ModMetadataDatabaseSQLite3();
virtual bool getModEntries(const std::string &modname, StringMap *storage); virtual bool getModEntries(const std::string &modname, StringMap *storage);
virtual bool getModEntry(const std::string &modname,
const std::string &key, std::string *value);
virtual bool hasModEntry(const std::string &modname, const std::string &key);
virtual bool setModEntry(const std::string &modname, virtual bool setModEntry(const std::string &modname,
const std::string &key, const std::string &value); const std::string &key, const std::string &value);
virtual bool removeModEntry(const std::string &modname, const std::string &key); virtual bool removeModEntry(const std::string &modname, const std::string &key);
virtual bool removeModEntries(const std::string &modname);
virtual void listMods(std::vector<std::string> *res); virtual void listMods(std::vector<std::string> *res);
virtual void beginSave() { Database_SQLite3::beginSave(); } virtual void beginSave() { Database_SQLite3::beginSave(); }
@ -253,7 +257,10 @@ protected:
virtual void initStatements(); virtual void initStatements();
private: private:
sqlite3_stmt *m_stmt_get_all = nullptr;
sqlite3_stmt *m_stmt_get = nullptr; sqlite3_stmt *m_stmt_get = nullptr;
sqlite3_stmt *m_stmt_has = nullptr;
sqlite3_stmt *m_stmt_set = nullptr; sqlite3_stmt *m_stmt_set = nullptr;
sqlite3_stmt *m_stmt_remove = nullptr; sqlite3_stmt *m_stmt_remove = nullptr;
sqlite3_stmt *m_stmt_remove_all = nullptr;
}; };

@ -92,8 +92,12 @@ public:
virtual ~ModMetadataDatabase() = default; virtual ~ModMetadataDatabase() = default;
virtual bool getModEntries(const std::string &modname, StringMap *storage) = 0; virtual bool getModEntries(const std::string &modname, StringMap *storage) = 0;
virtual bool hasModEntry(const std::string &modname, const std::string &key) = 0;
virtual bool getModEntry(const std::string &modname,
const std::string &key, std::string *value) = 0;
virtual bool setModEntry(const std::string &modname, virtual bool setModEntry(const std::string &modname,
const std::string &key, const std::string &value) = 0; const std::string &key, const std::string &value) = 0;
virtual bool removeModEntry(const std::string &modname, const std::string &key) = 0; virtual bool removeModEntry(const std::string &modname, const std::string &key) = 0;
virtual bool removeModEntries(const std::string &modname) = 0;
virtual void listMods(std::vector<std::string> *res) = 0; virtual void listMods(std::vector<std::string> *res) = 0;
}; };

@ -61,8 +61,6 @@ public:
return emptymodspec; return emptymodspec;
} }
const ModSpec* getModSpec(const std::string &modname) const override { return nullptr; } const ModSpec* getModSpec(const std::string &modname) const override { return nullptr; }
bool registerModStorage(ModMetadata *meta) override { return true; }
void unregisterModStorage(const std::string &name) override {}
ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; } ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; }
bool joinModChannel(const std::string &channel) override { return false; } bool joinModChannel(const std::string &channel) override { return false; }

@ -73,8 +73,6 @@ public:
virtual const std::vector<ModSpec> &getMods() const = 0; virtual const std::vector<ModSpec> &getMods() const = 0;
virtual const ModSpec* getModSpec(const std::string &modname) const = 0; virtual const ModSpec* getModSpec(const std::string &modname) const = 0;
virtual std::string getWorldPath() const { return ""; } virtual std::string getWorldPath() const { return ""; }
virtual bool registerModStorage(ModMetadata *storage) = 0;
virtual void unregisterModStorage(const std::string &name) = 0;
virtual ModMetadataDatabase *getModStorageDatabase() = 0; virtual ModMetadataDatabase *getModStorageDatabase() = 0;
virtual bool joinModChannel(const std::string &channel) = 0; virtual bool joinModChannel(const std::string &channel) = 0;

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
void ItemStackMetadata::clear() void ItemStackMetadata::clear()
{ {
Metadata::clear(); SimpleMetadata::clear();
updateToolCapabilities(); updateToolCapabilities();
} }
@ -52,7 +52,7 @@ bool ItemStackMetadata::setString(const std::string &name, const std::string &va
sanitize_string(clean_name); sanitize_string(clean_name);
sanitize_string(clean_var); sanitize_string(clean_var);
bool result = Metadata::setString(clean_name, clean_var); bool result = SimpleMetadata::setString(clean_name, clean_var);
if (clean_name == TOOLCAP_KEY) if (clean_name == TOOLCAP_KEY)
updateToolCapabilities(); updateToolCapabilities();
return result; return result;

@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Inventory; class Inventory;
class IItemDefManager; class IItemDefManager;
class ItemStackMetadata : public Metadata class ItemStackMetadata : public SimpleMetadata
{ {
public: public:
ItemStackMetadata() : toolcaps_overridden(false) {} ItemStackMetadata() : toolcaps_overridden(false) {}

@ -21,95 +21,110 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
/* /*
Metadata IMetadata
*/ */
void Metadata::clear() bool IMetadata::operator==(const IMetadata &other) const
{ {
m_stringvars.clear(); StringMap this_map_, other_map_;
m_modified = true; const StringMap &this_map = getStrings(&this_map_);
} const StringMap &other_map = other.getStrings(&other_map_);
bool Metadata::empty() const if (this_map.size() != other_map.size())
{
return m_stringvars.empty();
}
size_t Metadata::size() const
{
return m_stringvars.size();
}
bool Metadata::contains(const std::string &name) const
{
return m_stringvars.find(name) != m_stringvars.end();
}
bool Metadata::operator==(const Metadata &other) const
{
if (size() != other.size())
return false; return false;
for (const auto &sv : m_stringvars) { for (const auto &this_pair : this_map) {
if (!other.contains(sv.first) || other.getString(sv.first) != sv.second) const auto &other_pair = other_map.find(this_pair.first);
if (other_pair == other_map.cend() || other_pair->second != this_pair.second)
return false; return false;
} }
return true; return true;
} }
const std::string &Metadata::getString(const std::string &name, u16 recursion) const const std::string &IMetadata::getString(const std::string &name, std::string *place,
u16 recursion) const
{ {
StringMap::const_iterator it = m_stringvars.find(name); const std::string *raw = getStringRaw(name, place);
if (it == m_stringvars.end()) { if (!raw) {
static const std::string empty_string = std::string(""); static const std::string empty_string = std::string("");
return empty_string; return empty_string;
} }
return resolveString(it->second, recursion); return resolveString(*raw, place, recursion);
} }
bool Metadata::getStringToRef( bool IMetadata::getStringToRef(const std::string &name,
const std::string &name, std::string &str, u16 recursion) const std::string &str, u16 recursion) const
{ {
StringMap::const_iterator it = m_stringvars.find(name); const std::string *raw = getStringRaw(name, &str);
if (it == m_stringvars.end()) { if (!raw)
return false; return false;
}
str = resolveString(it->second, recursion); const std::string &resolved = resolveString(*raw, &str, recursion);
if (&resolved != &str)
str = resolved;
return true; return true;
} }
/** const std::string &IMetadata::resolveString(const std::string &str, std::string *place,
* Sets var to name key in the metadata storage u16 recursion) const
*
* @param name
* @param var
* @return true if key-value pair is created or changed
*/
bool Metadata::setString(const std::string &name, const std::string &var)
{
if (var.empty()) {
m_stringvars.erase(name);
return true;
}
StringMap::iterator it = m_stringvars.find(name);
if (it != m_stringvars.end() && it->second == var) {
return false;
}
m_stringvars[name] = var;
m_modified = true;
return true;
}
const std::string &Metadata::resolveString(const std::string &str, u16 recursion) const
{ {
if (recursion <= 1 && str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { if (recursion <= 1 && str.substr(0, 2) == "${" && str[str.length() - 1] == '}') {
return getString(str.substr(2, str.length() - 3), recursion + 1); // It may be the case that &str == place, but that's fine.
return getString(str.substr(2, str.length() - 3), place, recursion + 1);
} }
return str; return str;
} }
/*
SimpleMetadata
*/
void SimpleMetadata::clear()
{
m_stringvars.clear();
m_modified = true;
}
bool SimpleMetadata::empty() const
{
return m_stringvars.empty();
}
size_t SimpleMetadata::size() const
{
return m_stringvars.size();
}
bool SimpleMetadata::contains(const std::string &name) const
{
return m_stringvars.find(name) != m_stringvars.end();
}
const StringMap &SimpleMetadata::getStrings(StringMap *) const
{
return m_stringvars;
}
const std::string *SimpleMetadata::getStringRaw(const std::string &name, std::string *) const
{
const auto found = m_stringvars.find(name);
return found != m_stringvars.cend() ? &found->second : nullptr;
}
bool SimpleMetadata::setString(const std::string &name, const std::string &var)
{
if (var.empty()) {
if (m_stringvars.erase(name) == 0)
return false;
} else {
StringMap::iterator it = m_stringvars.find(name);
if (it != m_stringvars.end() && it->second == var)
return false;
m_stringvars[name] = var;
}
m_modified = true;
return true;
}

@ -24,17 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector> #include <vector>
#include "util/string.h" #include "util/string.h"
class Metadata // Basic metadata interface
class IMetadata
{ {
bool m_modified = false;
public: public:
virtual ~Metadata() = default; virtual ~IMetadata() = default;
virtual void clear(); virtual void clear() = 0;
virtual bool empty() const;
bool operator==(const Metadata &other) const; bool operator==(const IMetadata &other) const;
inline bool operator!=(const Metadata &other) const inline bool operator!=(const IMetadata &other) const
{ {
return !(*this == other); return !(*this == other);
} }
@ -43,21 +42,76 @@ public:
// Key-value related // Key-value related
// //
size_t size() const; virtual bool contains(const std::string &name) const = 0;
bool contains(const std::string &name) const;
const std::string &getString(const std::string &name, u16 recursion = 0) const; // May (not must!) put a string in `place` and return a reference to that string.
const std::string &getString(const std::string &name, std::string *place,
u16 recursion = 0) const;
// If the entry is present, puts the value in str and returns true;
// otherwise just returns false.
bool getStringToRef(const std::string &name, std::string &str, u16 recursion = 0) const; bool getStringToRef(const std::string &name, std::string &str, u16 recursion = 0) const;
virtual bool setString(const std::string &name, const std::string &var);
// Returns whether the metadata was (potentially) changed.
virtual bool setString(const std::string &name, const std::string &var) = 0;
inline bool removeString(const std::string &name) { return setString(name, ""); } inline bool removeString(const std::string &name) { return setString(name, ""); }
const StringMap &getStrings() const
// May (not must!) put strings in `place` and return a reference to these strings.
virtual const StringMap &getStrings(StringMap *place) const = 0;
// Add support for variable names in values. Uses place like getString.
const std::string &resolveString(const std::string &str, std::string *place,
u16 recursion = 0) const;
protected:
// Returns nullptr to indicate absence of value. Uses place like getString.
virtual const std::string *getStringRaw(const std::string &name,
std::string *place) const = 0;
};
// Simple metadata parent class (in-memory storage)
class SimpleMetadata: public virtual IMetadata
{
bool m_modified = false;
public:
virtual ~SimpleMetadata() = default;
virtual void clear() override;
virtual bool empty() const;
//
// Key-value related
//
size_t size() const;
bool contains(const std::string &name) const override;
virtual bool setString(const std::string &name, const std::string &var) override;
const StringMap &getStrings(StringMap *) const override final;
// Simple version of getters, possible due to in-memory storage:
inline const std::string &getString(const std::string &name, u16 recursion = 0) const
{ {
return m_stringvars; return IMetadata::getString(name, nullptr, recursion);
}
inline const std::string &resolveString(const std::string &str, u16 recursion = 0) const
{
return IMetadata::resolveString(str, nullptr, recursion);
}
inline const StringMap &getStrings() const
{
return SimpleMetadata::getStrings(nullptr);
} }
// Add support for variable names in values
const std::string &resolveString(const std::string &str, u16 recursion = 0) const;
inline bool isModified() const { return m_modified; } inline bool isModified() const { return m_modified; }
inline void setModified(bool v) { m_modified = v; } inline void setModified(bool v) { m_modified = v; }
protected: protected:
StringMap m_stringvars; StringMap m_stringvars;
const std::string *getStringRaw(const std::string &name,
std::string *) const override final;
}; };

@ -77,14 +77,14 @@ void NodeMetadata::deSerialize(std::istream &is, u8 version)
void NodeMetadata::clear() void NodeMetadata::clear()
{ {
Metadata::clear(); SimpleMetadata::clear();
m_privatevars.clear(); m_privatevars.clear();
m_inventory->clear(); m_inventory->clear();
} }
bool NodeMetadata::empty() const bool NodeMetadata::empty() const
{ {
return Metadata::empty() && m_inventory->getLists().empty(); return SimpleMetadata::empty() && m_inventory->getLists().empty();
} }

@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Inventory; class Inventory;
class IItemDefManager; class IItemDefManager;
class NodeMetadata : public Metadata class NodeMetadata : public SimpleMetadata
{ {
public: public:
NodeMetadata(IItemDefManager *item_def_mgr); NodeMetadata(IItemDefManager *item_def_mgr);

@ -36,7 +36,7 @@ ItemStackMetaRef* ItemStackMetaRef::checkobject(lua_State *L, int narg)
return *(ItemStackMetaRef**)ud; // unbox pointer return *(ItemStackMetaRef**)ud; // unbox pointer
} }
Metadata* ItemStackMetaRef::getmeta(bool auto_create) IMetadata* ItemStackMetaRef::getmeta(bool auto_create)
{ {
return &istack->getItem().metadata; return &istack->getItem().metadata;
} }

@ -36,7 +36,7 @@ private:
static ItemStackMetaRef *checkobject(lua_State *L, int narg); static ItemStackMetaRef *checkobject(lua_State *L, int narg);
virtual Metadata* getmeta(bool auto_create); virtual IMetadata* getmeta(bool auto_create);
virtual void clearMeta(); virtual void clearMeta();

@ -59,7 +59,7 @@ int MetaDataRef::l_contains(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2); std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false); IMetadata *meta = ref->getmeta(false);
if (meta == NULL) if (meta == NULL)
return 0; return 0;
@ -75,7 +75,7 @@ int MetaDataRef::l_get(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2); std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false); IMetadata *meta = ref->getmeta(false);
if (meta == NULL) if (meta == NULL)
return 0; return 0;
@ -96,13 +96,14 @@ int MetaDataRef::l_get_string(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2); std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false); IMetadata *meta = ref->getmeta(false);
if (meta == NULL) { if (meta == NULL) {
lua_pushlstring(L, "", 0); lua_pushlstring(L, "", 0);
return 1; return 1;
} }
const std::string &str = meta->getString(name); std::string str_;
const std::string &str = meta->getString(name, &str_);
lua_pushlstring(L, str.c_str(), str.size()); lua_pushlstring(L, str.c_str(), str.size());
return 1; return 1;
} }
@ -118,12 +119,9 @@ int MetaDataRef::l_set_string(lua_State *L)
const char *s = lua_tolstring(L, 3, &len); const char *s = lua_tolstring(L, 3, &len);
std::string str(s, len); std::string str(s, len);
Metadata *meta = ref->getmeta(!str.empty()); IMetadata *meta = ref->getmeta(!str.empty());
if (meta == NULL || str == meta->getString(name)) if (meta != NULL && meta->setString(name, str))
return 0; ref->reportMetadataChange(&name);
meta->setString(name, str);
ref->reportMetadataChange(&name);
return 0; return 0;
} }
@ -135,13 +133,14 @@ int MetaDataRef::l_get_int(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2); std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false); IMetadata *meta = ref->getmeta(false);
if (meta == NULL) { if (meta == NULL) {
lua_pushnumber(L, 0); lua_pushnumber(L, 0);
return 1; return 1;
} }
const std::string &str = meta->getString(name); std::string str_;
const std::string &str = meta->getString(name, &str_);
lua_pushnumber(L, stoi(str)); lua_pushnumber(L, stoi(str));
return 1; return 1;
} }
@ -156,12 +155,9 @@ int MetaDataRef::l_set_int(lua_State *L)
int a = luaL_checkint(L, 3); int a = luaL_checkint(L, 3);
std::string str = itos(a); std::string str = itos(a);
Metadata *meta = ref->getmeta(true); IMetadata *meta = ref->getmeta(true);
if (meta == NULL || str == meta->getString(name)) if (meta != NULL && meta->setString(name, str))
return 0; ref->reportMetadataChange(&name);
meta->setString(name, str);
ref->reportMetadataChange(&name);
return 0; return 0;
} }
@ -173,13 +169,14 @@ int MetaDataRef::l_get_float(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
std::string name = luaL_checkstring(L, 2); std::string name = luaL_checkstring(L, 2);
Metadata *meta = ref->getmeta(false); IMetadata *meta = ref->getmeta(false);
if (meta == NULL) { if (meta == NULL) {
lua_pushnumber(L, 0); lua_pushnumber(L, 0);
return 1; return 1;
} }
const std::string &str = meta->getString(name); std::string str_;
const std::string &str = meta->getString(name, &str_);
lua_pushnumber(L, stof(str)); lua_pushnumber(L, stof(str));
return 1; return 1;
} }
@ -194,12 +191,9 @@ int MetaDataRef::l_set_float(lua_State *L)
float a = readParam<float>(L, 3); float a = readParam<float>(L, 3);
std::string str = ftos(a); std::string str = ftos(a);
Metadata *meta = ref->getmeta(true); IMetadata *meta = ref->getmeta(true);
if (meta == NULL || str == meta->getString(name)) if (meta != NULL && meta->setString(name, str))
return 0; ref->reportMetadataChange(&name);
meta->setString(name, str);
ref->reportMetadataChange(&name);
return 0; return 0;
} }
@ -210,7 +204,7 @@ int MetaDataRef::l_to_table(lua_State *L)
MetaDataRef *ref = checkobject(L, 1); MetaDataRef *ref = checkobject(L, 1);
Metadata *meta = ref->getmeta(true); IMetadata *meta = ref->getmeta(true);
if (meta == NULL) { if (meta == NULL) {
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
@ -239,7 +233,7 @@ int MetaDataRef::l_from_table(lua_State *L)
} }
// Create new metadata // Create new metadata
Metadata *meta = ref->getmeta(true); IMetadata *meta = ref->getmeta(true);
if (meta == NULL) { if (meta == NULL) {
lua_pushboolean(L, false); lua_pushboolean(L, false);
return 1; return 1;
@ -251,11 +245,12 @@ int MetaDataRef::l_from_table(lua_State *L)
return 1; return 1;
} }
void MetaDataRef::handleToTable(lua_State *L, Metadata *meta) void MetaDataRef::handleToTable(lua_State *L, IMetadata *meta)
{ {
lua_newtable(L); lua_newtable(L);
{ {
const StringMap &fields = meta->getStrings(); StringMap fields_;
const StringMap &fields = meta->getStrings(&fields_);
for (const auto &field : fields) { for (const auto &field : fields) {
const std::string &name = field.first; const std::string &name = field.first;
const std::string &value = field.second; const std::string &value = field.second;
@ -267,7 +262,7 @@ void MetaDataRef::handleToTable(lua_State *L, Metadata *meta)
lua_setfield(L, -2, "fields"); lua_setfield(L, -2, "fields");
} }
bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta) bool MetaDataRef::handleFromTable(lua_State *L, int table, IMetadata *meta)
{ {
// Set fields // Set fields
lua_getfield(L, table, "fields"); lua_getfield(L, table, "fields");
@ -292,9 +287,9 @@ bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta)
int MetaDataRef::l_equals(lua_State *L) int MetaDataRef::l_equals(lua_State *L)
{ {
MetaDataRef *ref1 = checkobject(L, 1); MetaDataRef *ref1 = checkobject(L, 1);
Metadata *data1 = ref1->getmeta(false); IMetadata *data1 = ref1->getmeta(false);
MetaDataRef *ref2 = checkobject(L, 2); MetaDataRef *ref2 = checkobject(L, 2);
Metadata *data2 = ref2->getmeta(false); IMetadata *data2 = ref2->getmeta(false);
if (data1 == NULL || data2 == NULL) if (data1 == NULL || data2 == NULL)
lua_pushboolean(L, data1 == data2); lua_pushboolean(L, data1 == data2);
else else

@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h" #include "irrlichttypes_bloated.h"
#include "lua_api/l_base.h" #include "lua_api/l_base.h"
class Metadata; class IMetadata;
/* /*
NodeMetaRef NodeMetaRef
@ -38,11 +38,11 @@ protected:
static MetaDataRef *checkobject(lua_State *L, int narg); static MetaDataRef *checkobject(lua_State *L, int narg);
virtual void reportMetadataChange(const std::string *name = nullptr) {} virtual void reportMetadataChange(const std::string *name = nullptr) {}
virtual Metadata *getmeta(bool auto_create) = 0; virtual IMetadata *getmeta(bool auto_create) = 0;
virtual void clearMeta() = 0; virtual void clearMeta() = 0;
virtual void handleToTable(lua_State *L, Metadata *meta); virtual void handleToTable(lua_State *L, IMetadata *meta);
virtual bool handleFromTable(lua_State *L, int table, Metadata *meta); virtual bool handleFromTable(lua_State *L, int table, IMetadata *meta);
// Exported functions // Exported functions

@ -37,7 +37,7 @@ NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg)
return *(NodeMetaRef**)ud; // unbox pointer return *(NodeMetaRef**)ud; // unbox pointer
} }
Metadata* NodeMetaRef::getmeta(bool auto_create) IMetadata* NodeMetaRef::getmeta(bool auto_create)
{ {
if (m_is_local) if (m_is_local)
return m_local_meta; return m_local_meta;
@ -127,12 +127,13 @@ int NodeMetaRef::l_mark_as_private(lua_State *L)
return 0; return 0;
} }
void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta) void NodeMetaRef::handleToTable(lua_State *L, IMetadata *_meta)
{ {
// fields // fields
MetaDataRef::handleToTable(L, _meta); MetaDataRef::handleToTable(L, _meta);
NodeMetadata *meta = (NodeMetadata *) _meta; NodeMetadata *meta = dynamic_cast<NodeMetadata*>(_meta);
assert(meta);
// inventory // inventory
Inventory *inv = meta->getInventory(); Inventory *inv = meta->getInventory();
@ -145,13 +146,14 @@ void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta)
} }
// from_table(self, table) // from_table(self, table)
bool NodeMetaRef::handleFromTable(lua_State *L, int table, Metadata *_meta) bool NodeMetaRef::handleFromTable(lua_State *L, int table, IMetadata *_meta)
{ {
// fields // fields
if (!MetaDataRef::handleFromTable(L, table, _meta)) if (!MetaDataRef::handleFromTable(L, table, _meta))
return false; return false;
NodeMetadata *meta = (NodeMetadata*) _meta; NodeMetadata *meta = dynamic_cast<NodeMetadata*>(_meta);
assert(meta);
// inventory // inventory
Inventory *inv = meta->getInventory(); Inventory *inv = meta->getInventory();
@ -178,7 +180,7 @@ NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env):
{ {
} }
NodeMetaRef::NodeMetaRef(Metadata *meta): NodeMetaRef::NodeMetaRef(IMetadata *meta):
m_is_local(true), m_is_local(true),
m_local_meta(meta) m_local_meta(meta)
{ {
@ -196,7 +198,7 @@ void NodeMetaRef::create(lua_State *L, v3s16 p, ServerEnvironment *env)
} }
// Client-sided version of the above // Client-sided version of the above
void NodeMetaRef::createClient(lua_State *L, Metadata *meta) void NodeMetaRef::createClient(lua_State *L, IMetadata *meta)
{ {
NodeMetaRef *o = new NodeMetaRef(meta); NodeMetaRef *o = new NodeMetaRef(meta);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o; *(void **)(lua_newuserdata(L, sizeof(void *))) = o;

@ -38,7 +38,7 @@ private:
v3s16 m_p; v3s16 m_p;
ServerEnvironment *m_env = nullptr; ServerEnvironment *m_env = nullptr;
// Set for client metadata // Set for client metadata
Metadata *m_local_meta = nullptr; IMetadata *m_local_meta = nullptr;
static const char className[]; static const char className[];
static const luaL_Reg methodsServer[]; static const luaL_Reg methodsServer[];
@ -59,13 +59,13 @@ private:
* @param auto_create when true, try to create metadata information for the node if it has none. * @param auto_create when true, try to create metadata information for the node if it has none.
* @return pointer to a @c NodeMetadata object or @c NULL in case of error. * @return pointer to a @c NodeMetadata object or @c NULL in case of error.
*/ */
virtual Metadata* getmeta(bool auto_create); virtual IMetadata* getmeta(bool auto_create);
virtual void clearMeta(); virtual void clearMeta();
virtual void reportMetadataChange(const std::string *name = nullptr); virtual void reportMetadataChange(const std::string *name = nullptr);
virtual void handleToTable(lua_State *L, Metadata *_meta); virtual void handleToTable(lua_State *L, IMetadata *_meta);
virtual bool handleFromTable(lua_State *L, int table, Metadata *_meta); virtual bool handleFromTable(lua_State *L, int table, IMetadata *_meta);
// Exported functions // Exported functions
@ -80,7 +80,7 @@ private:
public: public:
NodeMetaRef(v3s16 p, ServerEnvironment *env); NodeMetaRef(v3s16 p, ServerEnvironment *env);
NodeMetaRef(Metadata *meta); NodeMetaRef(IMetadata *meta);
~NodeMetaRef() = default; ~NodeMetaRef() = default;
@ -89,7 +89,7 @@ public:
static void create(lua_State *L, v3s16 p, ServerEnvironment *env); static void create(lua_State *L, v3s16 p, ServerEnvironment *env);
// Client-sided version of the above // Client-sided version of the above
static void createClient(lua_State *L, Metadata *meta); static void createClient(lua_State *L, IMetadata *meta);
static void RegisterCommon(lua_State *L); static void RegisterCommon(lua_State *L);
static void Register(lua_State *L); static void Register(lua_State *L);

@ -35,7 +35,7 @@ PlayerMetaRef *PlayerMetaRef::checkobject(lua_State *L, int narg)
return *(PlayerMetaRef **)ud; // unbox pointer return *(PlayerMetaRef **)ud; // unbox pointer
} }
Metadata *PlayerMetaRef::getmeta(bool auto_create) IMetadata *PlayerMetaRef::getmeta(bool auto_create)
{ {
return metadata; return metadata;
} }
@ -60,7 +60,7 @@ int PlayerMetaRef::gc_object(lua_State *L)
// Creates an PlayerMetaRef and leaves it on top of stack // Creates an PlayerMetaRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side. // Not callable from Lua; all references are created on the C side.
void PlayerMetaRef::create(lua_State *L, Metadata *metadata) void PlayerMetaRef::create(lua_State *L, IMetadata *metadata)
{ {
PlayerMetaRef *o = new PlayerMetaRef(metadata); PlayerMetaRef *o = new PlayerMetaRef(metadata);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o; *(void **)(lua_newuserdata(L, sizeof(void *))) = o;

@ -29,14 +29,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class PlayerMetaRef : public MetaDataRef class PlayerMetaRef : public MetaDataRef
{ {
private: private:
Metadata *metadata = nullptr; IMetadata *metadata = nullptr;
static const char className[]; static const char className[];
static const luaL_Reg methods[]; static const luaL_Reg methods[];
static PlayerMetaRef *checkobject(lua_State *L, int narg); static PlayerMetaRef *checkobject(lua_State *L, int narg);
virtual Metadata *getmeta(bool auto_create); virtual IMetadata *getmeta(bool auto_create);
virtual void clearMeta(); virtual void clearMeta();
@ -46,12 +46,12 @@ private:
static int gc_object(lua_State *L); static int gc_object(lua_State *L);
public: public:
PlayerMetaRef(Metadata *metadata) : metadata(metadata) {} PlayerMetaRef(IMetadata *metadata) : metadata(metadata) {}
~PlayerMetaRef() = default; ~PlayerMetaRef() = default;
// Creates an ItemStackMetaRef and leaves it on top of stack // Creates an ItemStackMetaRef and leaves it on top of stack
// Not callable from Lua; all references are created on the C side. // Not callable from Lua; all references are created on the C side.
static void create(lua_State *L, Metadata *metadata); static void create(lua_State *L, IMetadata *metadata);
static void Register(lua_State *L); static void Register(lua_State *L);
}; };

@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_storage.h" #include "lua_api/l_storage.h"
#include "l_internal.h" #include "l_internal.h"
#include "content/mods.h"
#include "server.h" #include "server.h"
int ModApiStorage::l_get_mod_storage(lua_State *L) int ModApiStorage::l_get_mod_storage(lua_State *L)
@ -28,23 +27,12 @@ int ModApiStorage::l_get_mod_storage(lua_State *L)
// Note that this is wrapped in Lua, see builtin/common/mod_storage.lua // Note that this is wrapped in Lua, see builtin/common/mod_storage.lua
std::string mod_name = readParam<std::string>(L, 1); std::string mod_name = readParam<std::string>(L, 1);
ModMetadata *store = nullptr;
if (IGameDef *gamedef = getGameDef(L)) { if (IGameDef *gamedef = getGameDef(L)) {
store = new ModMetadata(mod_name, gamedef->getModStorageDatabase()); StorageRef::create(L, mod_name, gamedef->getModStorageDatabase());
if (gamedef->registerModStorage(store)) {
StorageRef::create(L, store);
int object = lua_gettop(L);
lua_pushvalue(L, object);
return 1;
}
} else { } else {
assert(false); // this should not happen assert(false); // this should not happen
lua_pushnil(L);
} }
delete store;
lua_pushnil(L);
return 1; return 1;
} }
@ -53,19 +41,9 @@ void ModApiStorage::Initialize(lua_State *L, int top)
API_FCT(get_mod_storage); API_FCT(get_mod_storage);
} }
StorageRef::StorageRef(ModMetadata *object): void StorageRef::create(lua_State *L, const std::string &mod_name, ModMetadataDatabase *db)
m_object(object)
{ {
} StorageRef *o = new StorageRef(mod_name, db);
StorageRef::~StorageRef()
{
delete m_object;
}
void StorageRef::create(lua_State *L, ModMetadata *object)
{
StorageRef *o = new StorageRef(object);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o; *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className); luaL_getmetatable(L, className);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
@ -74,9 +52,6 @@ void StorageRef::create(lua_State *L, ModMetadata *object)
int StorageRef::gc_object(lua_State *L) int StorageRef::gc_object(lua_State *L)
{ {
StorageRef *o = *(StorageRef **)(lua_touserdata(L, 1)); StorageRef *o = *(StorageRef **)(lua_touserdata(L, 1));
// Server side
if (IGameDef *gamedef = getGameDef(L))
gamedef->unregisterModStorage(getobject(o)->getModName());
delete o; delete o;
return 0; return 0;
} }
@ -122,20 +97,14 @@ StorageRef* StorageRef::checkobject(lua_State *L, int narg)
return *(StorageRef**)ud; // unbox pointer return *(StorageRef**)ud; // unbox pointer
} }
ModMetadata* StorageRef::getobject(StorageRef *ref) IMetadata* StorageRef::getmeta(bool auto_create)
{ {
ModMetadata *co = ref->m_object; return &m_object;
return co;
}
Metadata* StorageRef::getmeta(bool auto_create)
{
return m_object;
} }
void StorageRef::clearMeta() void StorageRef::clearMeta()
{ {
m_object->clear(); m_object.clear();
} }
const char StorageRef::className[] = "StorageRef"; const char StorageRef::className[] = "StorageRef";

@ -22,8 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "l_metadata.h" #include "l_metadata.h"
#include "lua_api/l_base.h" #include "lua_api/l_base.h"
#include "content/mods.h"
class ModMetadata;
class ModApiStorage : public ModApiBase class ModApiStorage : public ModApiBase
{ {
@ -37,24 +36,23 @@ public:
class StorageRef : public MetaDataRef class StorageRef : public MetaDataRef
{ {
private: private:
ModMetadata *m_object = nullptr; ModMetadata m_object;
static const char className[]; static const char className[];
static const luaL_Reg methods[]; static const luaL_Reg methods[];
virtual Metadata *getmeta(bool auto_create); virtual IMetadata *getmeta(bool auto_create);
virtual void clearMeta(); virtual void clearMeta();
// garbage collector // garbage collector
static int gc_object(lua_State *L); static int gc_object(lua_State *L);
public: public:
StorageRef(ModMetadata *object); StorageRef(const std::string &mod_name, ModMetadataDatabase *db): m_object(mod_name, db) {}
~StorageRef(); ~StorageRef() = default;
static void Register(lua_State *L); static void Register(lua_State *L);
static void create(lua_State *L, ModMetadata *object); static void create(lua_State *L, const std::string &mod_name, ModMetadataDatabase *db);
static StorageRef *checkobject(lua_State *L, int narg); static StorageRef *checkobject(lua_State *L, int narg);
static ModMetadata *getobject(StorageRef *ref);
}; };

@ -3882,25 +3882,6 @@ PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_v
return playersao; return playersao;
} }
bool Server::registerModStorage(ModMetadata *storage)
{
if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
errorstream << "Unable to register same mod storage twice. Storage name: "
<< storage->getModName() << std::endl;
return false;
}
m_mod_storages[storage->getModName()] = storage;
return true;
}
void Server::unregisterModStorage(const std::string &name)
{
std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
if (it != m_mod_storages.end())
m_mod_storages.erase(name);
}
void dedicated_server_loop(Server &server, bool &kill) void dedicated_server_loop(Server &server, bool &kill)
{ {
verbosestream<<"dedicated_server_loop()"<<std::endl; verbosestream<<"dedicated_server_loop()"<<std::endl;

@ -357,9 +357,6 @@ public:
void sendDetachedInventories(session_t peer_id, bool incremental); void sendDetachedInventories(session_t peer_id, bool incremental);
virtual bool registerModStorage(ModMetadata *storage);
virtual void unregisterModStorage(const std::string &name);
bool joinModChannel(const std::string &channel); bool joinModChannel(const std::string &channel);
bool leaveModChannel(const std::string &channel); bool leaveModChannel(const std::string &channel);
bool sendModChannelMessage(const std::string &channel, const std::string &message); bool sendModChannelMessage(const std::string &channel, const std::string &message);
@ -695,7 +692,6 @@ private:
s32 m_next_sound_id = 0; // positive values only s32 m_next_sound_id = 0; // positive values only
s32 nextSoundId(); s32 nextSoundId();
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
ModMetadataDatabase *m_mod_storage_database = nullptr; ModMetadataDatabase *m_mod_storage_database = nullptr;
float m_mod_storage_save_timer = 10.0f; float m_mod_storage_save_timer = 10.0f;

@ -181,7 +181,7 @@ public:
v3f getEyeOffset() const; v3f getEyeOffset() const;
float getZoomFOV() const; float getZoomFOV() const;
inline Metadata &getMeta() { return m_meta; } inline SimpleMetadata &getMeta() { return m_meta; }
private: private:
std::string getPropertyPacket(); std::string getPropertyPacket();
@ -218,7 +218,7 @@ private:
f32 m_fov = 0.0f; f32 m_fov = 0.0f;
s16 m_wanted_range = 0.0f; s16 m_wanted_range = 0.0f;
Metadata m_meta; SimpleMetadata m_meta;
public: public:
bool m_physics_override_sent = false; bool m_physics_override_sent = false;

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h" #include "test.h"
#include <algorithm> #include <algorithm>
#include "database/database-dummy.h"
#include "database/database-files.h" #include "database/database-files.h"
#include "database/database-sqlite3.h" #include "database/database-sqlite3.h"
#include "filesys.h" #include "filesys.h"
@ -133,14 +134,27 @@ void TestModMetadataDatabase::runTests(IGameDef *gamedef)
// fixed directory, for persistence // fixed directory, for persistence
thread_local const std::string test_dir = getTestTempDirectory(); thread_local const std::string test_dir = getTestTempDirectory();
// Each set of tests is run twice for each database type: // Each set of tests is run twice for each database type except dummy:
// one where we reuse the same ModMetadataDatabase object (to test local caching), // one where we reuse the same ModMetadataDatabase object (to test local caching),
// and one where we create a new ModMetadataDatabase object for each call // and one where we create a new ModMetadataDatabase object for each call
// (to test actual persistence). // (to test actual persistence).
// Since the dummy database is only in-memory, it has no persistence to test.
ModMetadataDatabase *mod_meta_db;
rawstream << "-------- Dummy database (same object only)" << std::endl;
mod_meta_db = new Database_Dummy();
mod_meta_provider = new FixedProvider(mod_meta_db);
runTestsForCurrentDB();
delete mod_meta_db;
delete mod_meta_provider;
rawstream << "-------- Files database (same object)" << std::endl; rawstream << "-------- Files database (same object)" << std::endl;
ModMetadataDatabase *mod_meta_db = new ModMetadataDatabaseFiles(test_dir); mod_meta_db = new ModMetadataDatabaseFiles(test_dir);
mod_meta_provider = new FixedProvider(mod_meta_db); mod_meta_provider = new FixedProvider(mod_meta_db);
runTestsForCurrentDB(); runTestsForCurrentDB();
@ -201,6 +215,9 @@ void TestModMetadataDatabase::testRecallFail()
StringMap recalled; StringMap recalled;
mod_meta_db->getModEntries("mod1", &recalled); mod_meta_db->getModEntries("mod1", &recalled);
UASSERT(recalled.empty()); UASSERT(recalled.empty());
std::string key1_value;
UASSERT(!mod_meta_db->getModEntry("mod1", "key1", &key1_value));
UASSERT(!mod_meta_db->hasModEntry("mod1", "key1"));
} }
void TestModMetadataDatabase::testCreate() void TestModMetadataDatabase::testCreate()
@ -214,8 +231,12 @@ void TestModMetadataDatabase::testRecall()
ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase();
StringMap recalled; StringMap recalled;
mod_meta_db->getModEntries("mod1", &recalled); mod_meta_db->getModEntries("mod1", &recalled);
UASSERT(recalled.size() == 1); UASSERTCMP(std::size_t, ==, recalled.size(), 1);
UASSERT(recalled["key1"] == "value1"); UASSERTCMP(std::string, ==, recalled["key1"], "value1");
std::string key1_value;
UASSERT(mod_meta_db->getModEntry("mod1", "key1", &key1_value));
UASSERTCMP(std::string, ==, key1_value, "value1");
UASSERT(mod_meta_db->hasModEntry("mod1", "key1"));
} }
void TestModMetadataDatabase::testChange() void TestModMetadataDatabase::testChange()
@ -229,8 +250,12 @@ void TestModMetadataDatabase::testRecallChanged()
ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase();
StringMap recalled; StringMap recalled;
mod_meta_db->getModEntries("mod1", &recalled); mod_meta_db->getModEntries("mod1", &recalled);
UASSERT(recalled.size() == 1); UASSERTCMP(std::size_t, ==, recalled.size(), 1);
UASSERT(recalled["key1"] == "value2"); UASSERTCMP(std::string, ==, recalled["key1"], "value2");
std::string key1_value;
UASSERT(mod_meta_db->getModEntry("mod1", "key1", &key1_value));
UASSERTCMP(std::string, ==, key1_value, "value2");
UASSERT(mod_meta_db->hasModEntry("mod1", "key1"));
} }
void TestModMetadataDatabase::testListMods() void TestModMetadataDatabase::testListMods()
@ -239,7 +264,7 @@ void TestModMetadataDatabase::testListMods()
UASSERT(mod_meta_db->setModEntry("mod2", "key1", "value1")); UASSERT(mod_meta_db->setModEntry("mod2", "key1", "value1"));
std::vector<std::string> mod_list; std::vector<std::string> mod_list;
mod_meta_db->listMods(&mod_list); mod_meta_db->listMods(&mod_list);
UASSERT(mod_list.size() == 2); UASSERTCMP(size_t, ==, mod_list.size(), 2);
UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod1") != mod_list.cend()); UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod1") != mod_list.cend());
UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod2") != mod_list.cend()); UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod2") != mod_list.cend());
} }
@ -248,4 +273,6 @@ void TestModMetadataDatabase::testRemove()
{ {
ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase();
UASSERT(mod_meta_db->removeModEntry("mod1", "key1")); UASSERT(mod_meta_db->removeModEntry("mod1", "key1"));
UASSERT(!mod_meta_db->removeModEntries("mod1"));
UASSERT(mod_meta_db->removeModEntries("mod2"));
} }