diff --git a/.gitignore b/.gitignore index d996a0d4c..c9fc16611 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ AppDir /clientmods/* !/clientmods/preview/ /client/mod_storage/ +/mod_data ## Configuration/log files minetest.conf diff --git a/doc/lua_api.md b/doc/lua_api.md index be11a4c5d..b0bcdf34e 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5306,6 +5306,12 @@ Utilities * `minetest.get_worldpath()`: returns e.g. `"/home/user/.minetest/world"` * Useful for storing custom data +* `minetest.get_mod_data_path()`: returns e.g. `"/home/user/.minetest/mod_data/mymod"` + * Useful for storing custom data *independently of worlds*. + * Must be called during mod load time. + * Can read or write to this directory at any time. + * It's possible that multiple Minetest instances are running at the same + time, which may lead to corruption if you are not careful. * `minetest.is_singleplayer()` * `minetest.features`: Table containing API feature flags diff --git a/src/gamedef.h b/src/gamedef.h index 0f17cabf9..9a6c55ab1 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -75,6 +75,7 @@ public: virtual const ModSpec* getModSpec(const std::string &modname) const = 0; virtual const SubgameSpec* getGameSpec() const { return nullptr; } virtual std::string getWorldPath() const { return ""; } + virtual std::string getModDataPath() const { return ""; } virtual ModStorageDatabase *getModStorageDatabase() = 0; virtual bool joinModChannel(const std::string &channel) = 0; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 8f5c261b2..cadeafdd1 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -600,6 +600,13 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, } } + // Allow read/write access to all mod common dirs + str = fs::AbsolutePath(gamedef->getModDataPath()); + if (!str.empty() && fs::PathStartsWith(abs_path, str)) { + if (write_allowed) *write_allowed = true; + return true; + } + str = fs::AbsolutePath(gamedef->getWorldPath()); if (!str.empty()) { // Don't allow access to other paths in the world mod/game path. diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 7fd086910..185287dd7 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "remoteplayer.h" #include "log.h" +#include "filesys.h" #include // request_shutdown() @@ -507,6 +508,24 @@ int ModApiServer::l_get_worldpath(lua_State *L) return 1; } +// get_mod_data_path() +int ModApiServer::l_get_mod_data_path(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string modname = ScriptApiBase::getCurrentModNameInsecure(L); + if (modname.empty()) + return 0; + + const Server *srv = getServer(L); + std::string path = srv->getModDataPath() + DIR_DELIM + modname; + if (!fs::CreateAllDirs(path)) + throw LuaError("Failed to create dir"); + + lua_pushstring(L, path.c_str()); + return 1; +} + // sound_play(spec, parameters, [ephemeral]) int ModApiServer::l_sound_play(lua_State *L) { @@ -716,6 +735,7 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(get_server_status); API_FCT(get_server_uptime); API_FCT(get_server_max_lag); + API_FCT(get_mod_data_path); API_FCT(get_worldpath); API_FCT(is_singleplayer); diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 33dd814b4..505dce735 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -39,6 +39,9 @@ private: // get_worldpath() static int l_get_worldpath(lua_State *L); + // get_mod_data_path() + static int l_get_mod_data_path(lua_State *L); + // is_singleplayer() static int l_is_singleplayer(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 27b953c50..5dd2d4691 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -317,6 +317,10 @@ Server::Server( "Number of map edit events"); m_lag_gauge->set(g_settings->getFloat("dedicated_server_step")); + + m_path_mod_data = porting::path_user + DIR_DELIM "mod_data"; + if (!fs::CreateDir(m_path_mod_data)) + throw ServerError("Failed to create mod data dir"); } Server::~Server() diff --git a/src/server.h b/src/server.h index 7158cbb64..16c1ea4cc 100644 --- a/src/server.h +++ b/src/server.h @@ -306,6 +306,7 @@ public: virtual const SubgameSpec* getGameSpec() const { return &m_gamespec; } static std::string getBuiltinLuaPath(); virtual std::string getWorldPath() const { return m_path_world; } + virtual std::string getModDataPath() const { return m_path_mod_data; } inline bool isSingleplayer() const { return m_simple_singleplayer_mode; } @@ -609,6 +610,7 @@ private: */ // World directory std::string m_path_world; + std::string m_path_mod_data; // Subgame specification SubgameSpec m_gamespec; // If true, do not allow multiple players and hide some multiplayer