Skip to content
Permalink
Browse files
Use a database for mod storage (#11763)
  • Loading branch information
TurkeyMcMac committed Jan 7, 2022
1 parent b81948a commit bf22569019749e421e8ffe0a73cff988a9a9c846
@@ -112,6 +112,10 @@ leveldb, and files.
Migrate from current players backend to another. Possible values are sqlite3,
leveldb, postgresql, dummy, and files.
.TP
.B \-\-migrate-mod-storage <value>
Migrate from current mod storage backend to another. Possible values are
sqlite3, dummy, and files.
.TP
.B \-\-terminal
Display an interactive terminal over ncurses during execution.

@@ -128,6 +128,11 @@ Client::Client(
// Add local player
m_env.setLocalPlayer(new LocalPlayer(this, playername));

// Make the mod storage database and begin the save for later
m_mod_storage_database =
new ModMetadataDatabaseSQLite3(porting::path_user + DIR_DELIM + "client");
m_mod_storage_database->beginSave();

if (g_settings->getBool("enable_minimap")) {
m_minimap = new Minimap(this);
}
@@ -305,6 +310,11 @@ Client::~Client()
m_minimap = nullptr;

delete m_media_downloader;

// Write the changes and delete
if (m_mod_storage_database)
m_mod_storage_database->endSave();
delete m_mod_storage_database;
}

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

// Write changes to the mod storage
m_mod_storage_save_timer -= dtime;
if (m_mod_storage_save_timer <= 0.0f) {
m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
int n = 0;
for (std::unordered_map<std::string, ModMetadata *>::const_iterator
it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
if (it->second->isModified()) {
it->second->save(getModStoragePath());
n++;
}
}
if (n > 0)
infostream << "Saved " << n << " modified mod storages." << std::endl;
m_mod_storage_database->endSave();
m_mod_storage_database->beginSave();
}

// Write server map
@@ -1960,16 +1963,8 @@ 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()) {
// Save unconditionaly on unregistration
it->second->save(getModStoragePath());
if (it != m_mod_storages.end())
m_mod_storages.erase(name);
}
}

std::string Client::getModStoragePath() const
{
return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
}

/*
@@ -380,8 +380,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
{ return checkPrivilege(priv); }
virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false);
const std::string* getModFile(std::string filename);
ModMetadataDatabase *getModStorageDatabase() override { return m_mod_storage_database; }

std::string getModStoragePath() const override;
bool registerModStorage(ModMetadata *meta) override;
void unregisterModStorage(const std::string &name) override;

@@ -590,6 +590,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
// Client modding
ClientScripting *m_script = nullptr;
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
ModMetadataDatabase *m_mod_storage_database = nullptr;
float m_mod_storage_save_timer = 10.0f;
std::vector<ModSpec> m_mods;
StringMap m_mod_vfs;
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <json/json.h>
#include <algorithm>
#include "content/mods.h"
#include "database/database.h"
#include "filesys.h"
#include "log.h"
#include "content/subgames.h"
@@ -422,83 +423,29 @@ ClientModConfiguration::ClientModConfiguration(const std::string &path) :
}
#endif

ModMetadata::ModMetadata(const std::string &mod_name) : m_mod_name(mod_name)
ModMetadata::ModMetadata(const std::string &mod_name, ModMetadataDatabase *database):
m_mod_name(mod_name), m_database(database)
{
m_database->getModEntries(m_mod_name, &m_stringvars);
}

void ModMetadata::clear()
{
for (const auto &pair : m_stringvars) {
m_database->removeModEntry(m_mod_name, pair.first);
}
Metadata::clear();
m_modified = true;
}

bool ModMetadata::save(const std::string &root_path)
bool ModMetadata::setString(const std::string &name, const std::string &var)
{
Json::Value json;
for (StringMap::const_iterator it = m_stringvars.begin();
it != m_stringvars.end(); ++it) {
json[it->first] = it->second;
}

if (!fs::PathExists(root_path)) {
if (!fs::CreateAllDirs(root_path)) {
errorstream << "ModMetadata[" << m_mod_name
<< "]: Unable to save. '" << root_path
<< "' tree cannot be created." << std::endl;
return false;
if (Metadata::setString(name, var)) {
if (var.empty()) {
m_database->removeModEntry(m_mod_name, name);
} else {
m_database->setModEntry(m_mod_name, name, var);
}
} else if (!fs::IsDir(root_path)) {
errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '"
<< root_path << "' is not a directory." << std::endl;
return false;
}

bool w_ok = fs::safeWriteToFile(
root_path + DIR_DELIM + m_mod_name, fastWriteJson(json));

if (w_ok) {
m_modified = false;
} else {
errorstream << "ModMetadata[" << m_mod_name << "]: failed write file."
<< std::endl;
}
return w_ok;
}

bool ModMetadata::load(const std::string &root_path)
{
m_stringvars.clear();

std::ifstream is((root_path + DIR_DELIM + m_mod_name).c_str(),
std::ios_base::binary);
if (!is.good()) {
return false;
}

Json::Value root;
Json::CharReaderBuilder builder;
builder.settings_["collectComments"] = false;
std::string errs;

if (!Json::parseFromStream(builder, is, &root, &errs)) {
errorstream << "ModMetadata[" << m_mod_name
<< "]: failed read data "
"(Json decoding failure). Message: "
<< errs << std::endl;
return false;
}

const Json::Value::Members attr_list = root.getMemberNames();
for (const auto &it : attr_list) {
Json::Value attr_value = root[it];
m_stringvars[it] = attr_value.asString();
return true;
}

return true;
}

bool ModMetadata::setString(const std::string &name, const std::string &var)
{
m_modified = Metadata::setString(name, var);
return m_modified;
return false;
}
@@ -31,6 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "config.h"
#include "metadata.h"

class ModMetadataDatabase;

#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"

struct ModSpec
@@ -149,20 +151,16 @@ class ModMetadata : public Metadata
{
public:
ModMetadata() = delete;
ModMetadata(const std::string &mod_name);
ModMetadata(const std::string &mod_name, ModMetadataDatabase *database);
~ModMetadata() = default;

virtual void clear();

bool save(const std::string &root_path);
bool load(const std::string &root_path);

bool isModified() const { return m_modified; }
const std::string &getModName() const { return m_mod_name; }

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

private:
std::string m_mod_name;
bool m_modified = false;
ModMetadataDatabase *m_database;
};
@@ -358,6 +358,7 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
conf.set("backend", "sqlite3");
conf.set("player_backend", "sqlite3");
conf.set("auth_backend", "sqlite3");
conf.set("mod_storage_backend", "sqlite3");
conf.setBool("creative_mode", g_settings->getBool("creative_mode"));
conf.setBool("enable_damage", g_settings->getBool("enable_damage"));

@@ -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);
}
}
@@ -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 @@ class Database_Dummy : public MapDatabase, public PlayerDatabase
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;
};

0 comments on commit bf22569

Please sign in to comment.