+
+ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
+{
+ std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
+ Settings world_mt;
+ if (!world_mt.readConfigFile(world_mt_path.c_str()))
+ throw BaseException("Cannot read world.mt!");
+
+ std::string backend = world_mt.exists("mod_storage_backend") ?
+ world_mt.get("mod_storage_backend") : "files";
+ if (backend == "files")
+ warningstream << "/!\\ You are using the old mod storage files backend. "
+ << "This backend is deprecated and may be removed in a future release /!\\"
+ << std::endl << "Switching to SQLite3 is advised, "
+ << "please read http://wiki.minetest.net/Database_backends." << std::endl;
+
+ return openModStorageDatabase(backend, world_path, world_mt);
+}
+
+ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
+ const std::string &world_path, const Settings &world_mt)
+{
+ if (backend == "sqlite3")
+ return new ModMetadataDatabaseSQLite3(world_path);
+
+ if (backend == "files")
+ return new ModMetadataDatabaseFiles(world_path);
+
+ if (backend == "dummy")
+ return new Database_Dummy();
+
+ throw BaseException("Mod storage database backend " + backend + " not supported");
+}
+
+bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
+{
+ std::string migrate_to = cmd_args.get("migrate-mod-storage");
+ Settings world_mt;
+ std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
+ if (!world_mt.readConfigFile(world_mt_path.c_str())) {
+ errorstream << "Cannot read world.mt!" << std::endl;
+ return false;
+ }
+
+ std::string backend = world_mt.exists("mod_storage_backend") ?
+ world_mt.get("mod_storage_backend") : "files";
+ if (backend == migrate_to) {
+ errorstream << "Cannot migrate: new backend is same"
+ << " as the old one" << std::endl;
+ return false;
+ }
+
+ ModMetadataDatabase *srcdb = nullptr;
+ ModMetadataDatabase *dstdb = nullptr;
+
+ bool succeeded = false;
+
+ try {
+ srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
+ dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
+
+ dstdb->beginSave();
+
+ std::vector<std::string> mod_list;
+ srcdb->listMods(&mod_list);
+ for (const std::string &modname : mod_list) {
+ StringMap meta;
+ srcdb->getModEntries(modname, &meta);
+ for (const auto &pair : meta) {
+ dstdb->setModEntry(modname, pair.first, pair.second);
+ }
+ }
+
+ dstdb->endSave();
+
+ succeeded = true;
+
+ actionstream << "Successfully migrated the metadata of "
+ << mod_list.size() << " mods" << std::endl;
+ world_mt.set("mod_storage_backend", migrate_to);
+ if (!world_mt.updateConfigFile(world_mt_path.c_str()))
+ errorstream << "Failed to update world.mt!" << std::endl;
+ else
+ actionstream << "world.mt updated" << std::endl;
+
+ } catch (BaseException &e) {
+ errorstream << "An error occurred during migration: " << e.what() << std::endl;
+ }
+
+ delete srcdb;
+ delete dstdb;
+
+ if (succeeded && backend == "files") {
+ // Back up files
+ const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
+ const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
+ if (!fs::Rename(storage_path, backup_path))
+ warningstream << "After migration, " << storage_path
+ << " could not be renamed to " << backup_path << std::endl;
+ }
+
+ return succeeded;
+}