Skip to content

Commit bf22569

Browse files
authoredJan 7, 2022
Use a database for mod storage (#11763)
1 parent b81948a commit bf22569

21 files changed

+797
-126
lines changed
 

‎doc/minetest.6

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ leveldb, and files.
112112
Migrate from current players backend to another. Possible values are sqlite3,
113113
leveldb, postgresql, dummy, and files.
114114
.TP
115+
.B \-\-migrate-mod-storage <value>
116+
Migrate from current mod storage backend to another. Possible values are
117+
sqlite3, dummy, and files.
118+
.TP
115119
.B \-\-terminal
116120
Display an interactive terminal over ncurses during execution.
117121

‎src/client/client.cpp

+14-19
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ Client::Client(
128128
// Add local player
129129
m_env.setLocalPlayer(new LocalPlayer(this, playername));
130130

131+
// Make the mod storage database and begin the save for later
132+
m_mod_storage_database =
133+
new ModMetadataDatabaseSQLite3(porting::path_user + DIR_DELIM + "client");
134+
m_mod_storage_database->beginSave();
135+
131136
if (g_settings->getBool("enable_minimap")) {
132137
m_minimap = new Minimap(this);
133138
}
@@ -305,6 +310,11 @@ Client::~Client()
305310
m_minimap = nullptr;
306311

307312
delete m_media_downloader;
313+
314+
// Write the changes and delete
315+
if (m_mod_storage_database)
316+
m_mod_storage_database->endSave();
317+
delete m_mod_storage_database;
308318
}
309319

310320
void Client::connect(Address address, bool is_local_server)
@@ -641,19 +651,12 @@ void Client::step(float dtime)
641651
}
642652
}
643653

654+
// Write changes to the mod storage
644655
m_mod_storage_save_timer -= dtime;
645656
if (m_mod_storage_save_timer <= 0.0f) {
646657
m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
647-
int n = 0;
648-
for (std::unordered_map<std::string, ModMetadata *>::const_iterator
649-
it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
650-
if (it->second->isModified()) {
651-
it->second->save(getModStoragePath());
652-
n++;
653-
}
654-
}
655-
if (n > 0)
656-
infostream << "Saved " << n << " modified mod storages." << std::endl;
658+
m_mod_storage_database->endSave();
659+
m_mod_storage_database->beginSave();
657660
}
658661

659662
// Write server map
@@ -1960,16 +1963,8 @@ void Client::unregisterModStorage(const std::string &name)
19601963
{
19611964
std::unordered_map<std::string, ModMetadata *>::const_iterator it =
19621965
m_mod_storages.find(name);
1963-
if (it != m_mod_storages.end()) {
1964-
// Save unconditionaly on unregistration
1965-
it->second->save(getModStoragePath());
1966+
if (it != m_mod_storages.end())
19661967
m_mod_storages.erase(name);
1967-
}
1968-
}
1969-
1970-
std::string Client::getModStoragePath() const
1971-
{
1972-
return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
19731968
}
19741969

19751970
/*

‎src/client/client.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
380380
{ return checkPrivilege(priv); }
381381
virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false);
382382
const std::string* getModFile(std::string filename);
383+
ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; }
383384

384-
std::string getModStoragePath() const override;
385385
bool registerModStorage(ModMetadata *meta) override;
386386
void unregisterModStorage(const std::string &name) override;
387387

@@ -590,6 +590,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
590590
// Client modding
591591
ClientScripting *m_script = nullptr;
592592
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
593+
ModMetadataDatabase *m_mod_storage_database = nullptr;
593594
float m_mod_storage_save_timer = 10.0f;
594595
std::vector<ModSpec> m_mods;
595596
StringMap m_mod_vfs;

‎src/content/mods.cpp

+15-68
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2222
#include <json/json.h>
2323
#include <algorithm>
2424
#include "content/mods.h"
25+
#include "database/database.h"
2526
#include "filesys.h"
2627
#include "log.h"
2728
#include "content/subgames.h"
@@ -422,83 +423,29 @@ ClientModConfiguration::ClientModConfiguration(const std::string &path) :
422423
}
423424
#endif
424425

425-
ModMetadata::ModMetadata(const std::string &mod_name) : m_mod_name(mod_name)
426+
ModMetadata::ModMetadata(const std::string &mod_name, ModMetadataDatabase *database):
427+
m_mod_name(mod_name), m_database(database)
426428
{
429+
m_database->getModEntries(m_mod_name, &m_stringvars);
427430
}
428431

429432
void ModMetadata::clear()
430433
{
434+
for (const auto &pair : m_stringvars) {
435+
m_database->removeModEntry(m_mod_name, pair.first);
436+
}
431437
Metadata::clear();
432-
m_modified = true;
433438
}
434439

435-
bool ModMetadata::save(const std::string &root_path)
440+
bool ModMetadata::setString(const std::string &name, const std::string &var)
436441
{
437-
Json::Value json;
438-
for (StringMap::const_iterator it = m_stringvars.begin();
439-
it != m_stringvars.end(); ++it) {
440-
json[it->first] = it->second;
441-
}
442-
443-
if (!fs::PathExists(root_path)) {
444-
if (!fs::CreateAllDirs(root_path)) {
445-
errorstream << "ModMetadata[" << m_mod_name
446-
<< "]: Unable to save. '" << root_path
447-
<< "' tree cannot be created." << std::endl;
448-
return false;
442+
if (Metadata::setString(name, var)) {
443+
if (var.empty()) {
444+
m_database->removeModEntry(m_mod_name, name);
445+
} else {
446+
m_database->setModEntry(m_mod_name, name, var);
449447
}
450-
} else if (!fs::IsDir(root_path)) {
451-
errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '"
452-
<< root_path << "' is not a directory." << std::endl;
453-
return false;
454-
}
455-
456-
bool w_ok = fs::safeWriteToFile(
457-
root_path + DIR_DELIM + m_mod_name, fastWriteJson(json));
458-
459-
if (w_ok) {
460-
m_modified = false;
461-
} else {
462-
errorstream << "ModMetadata[" << m_mod_name << "]: failed write file."
463-
<< std::endl;
464-
}
465-
return w_ok;
466-
}
467-
468-
bool ModMetadata::load(const std::string &root_path)
469-
{
470-
m_stringvars.clear();
471-
472-
std::ifstream is((root_path + DIR_DELIM + m_mod_name).c_str(),
473-
std::ios_base::binary);
474-
if (!is.good()) {
475-
return false;
476-
}
477-
478-
Json::Value root;
479-
Json::CharReaderBuilder builder;
480-
builder.settings_["collectComments"] = false;
481-
std::string errs;
482-
483-
if (!Json::parseFromStream(builder, is, &root, &errs)) {
484-
errorstream << "ModMetadata[" << m_mod_name
485-
<< "]: failed read data "
486-
"(Json decoding failure). Message: "
487-
<< errs << std::endl;
488-
return false;
489-
}
490-
491-
const Json::Value::Members attr_list = root.getMemberNames();
492-
for (const auto &it : attr_list) {
493-
Json::Value attr_value = root[it];
494-
m_stringvars[it] = attr_value.asString();
448+
return true;
495449
}
496-
497-
return true;
498-
}
499-
500-
bool ModMetadata::setString(const std::string &name, const std::string &var)
501-
{
502-
m_modified = Metadata::setString(name, var);
503-
return m_modified;
450+
return false;
504451
}

‎src/content/mods.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3131
#include "config.h"
3232
#include "metadata.h"
3333

34+
class ModMetadataDatabase;
35+
3436
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
3537

3638
struct ModSpec
@@ -149,20 +151,16 @@ class ModMetadata : public Metadata
149151
{
150152
public:
151153
ModMetadata() = delete;
152-
ModMetadata(const std::string &mod_name);
154+
ModMetadata(const std::string &mod_name, ModMetadataDatabase *database);
153155
~ModMetadata() = default;
154156

155157
virtual void clear();
156158

157-
bool save(const std::string &root_path);
158-
bool load(const std::string &root_path);
159-
160-
bool isModified() const { return m_modified; }
161159
const std::string &getModName() const { return m_mod_name; }
162160

163161
virtual bool setString(const std::string &name, const std::string &var);
164162

165163
private:
166164
std::string m_mod_name;
167-
bool m_modified = false;
165+
ModMetadataDatabase *m_database;
168166
};

‎src/content/subgames.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
358358
conf.set("backend", "sqlite3");
359359
conf.set("player_backend", "sqlite3");
360360
conf.set("auth_backend", "sqlite3");
361+
conf.set("mod_storage_backend", "sqlite3");
361362
conf.setBool("creative_mode", g_settings->getBool("creative_mode"));
362363
conf.setBool("enable_damage", g_settings->getBool("enable_damage"));
363364

‎src/database/database-dummy.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,41 @@ void Database_Dummy::listPlayers(std::vector<std::string> &res)
8080
res.emplace_back(player);
8181
}
8282
}
83+
84+
bool Database_Dummy::getModEntries(const std::string &modname, StringMap *storage)
85+
{
86+
const auto mod_pair = m_mod_meta_database.find(modname);
87+
if (mod_pair != m_mod_meta_database.cend()) {
88+
for (const auto &pair : mod_pair->second) {
89+
(*storage)[pair.first] = pair.second;
90+
}
91+
}
92+
return true;
93+
}
94+
95+
bool Database_Dummy::setModEntry(const std::string &modname,
96+
const std::string &key, const std::string &value)
97+
{
98+
auto mod_pair = m_mod_meta_database.find(modname);
99+
if (mod_pair == m_mod_meta_database.end()) {
100+
m_mod_meta_database[modname] = StringMap({{key, value}});
101+
} else {
102+
mod_pair->second[key] = value;
103+
}
104+
return true;
105+
}
106+
107+
bool Database_Dummy::removeModEntry(const std::string &modname, const std::string &key)
108+
{
109+
auto mod_pair = m_mod_meta_database.find(modname);
110+
if (mod_pair != m_mod_meta_database.end())
111+
return mod_pair->second.erase(key) > 0;
112+
return false;
113+
}
114+
115+
void Database_Dummy::listMods(std::vector<std::string> *res)
116+
{
117+
for (const auto &pair : m_mod_meta_database) {
118+
res->push_back(pair.first);
119+
}
120+
}

‎src/database/database-dummy.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2424
#include "database.h"
2525
#include "irrlichttypes.h"
2626

27-
class Database_Dummy : public MapDatabase, public PlayerDatabase
27+
class Database_Dummy : public MapDatabase, public PlayerDatabase, public ModMetadataDatabase
2828
{
2929
public:
3030
bool saveBlock(const v3s16 &pos, const std::string &data);
@@ -37,10 +37,17 @@ class Database_Dummy : public MapDatabase, public PlayerDatabase
3737
bool removePlayer(const std::string &name);
3838
void listPlayers(std::vector<std::string> &res);
3939

40+
bool getModEntries(const std::string &modname, StringMap *storage);
41+
bool setModEntry(const std::string &modname,
42+
const std::string &key, const std::string &value);
43+
bool removeModEntry(const std::string &modname, const std::string &key);
44+
void listMods(std::vector<std::string> *res);
45+
4046
void beginSave() {}
4147
void endSave() {}
4248

4349
private:
4450
std::map<s64, std::string> m_database;
4551
std::set<std::string> m_player_database;
52+
std::unordered_map<std::string, StringMap> m_mod_meta_database;
4653
};

0 commit comments

Comments
 (0)
Please sign in to comment.