diff --git a/src/framework/core/resourcemanager.cpp b/src/framework/core/resourcemanager.cpp index 7559ac559..746b08b11 100644 --- a/src/framework/core/resourcemanager.cpp +++ b/src/framework/core/resourcemanager.cpp @@ -351,6 +351,25 @@ bool ResourceManager::discoverWorkDir(const std::string& existentFile) bool ResourceManager::setupUserWriteDir(const std::string& appWriteDirName) { + // a --user-dir override points every persisted file at a caller-chosen dir, + // so multiple isolated profiles can coexist. see #1540 + if (!m_userDirOverride.empty()) { + std::error_code ec; + // resolve to an absolute path so the write dir does not depend on the + // current working directory + const auto absolutePath = std::filesystem::absolute(m_userDirOverride, ec); + if (ec) { + g_logger.error("Unable to resolve user directory '{}': {}", m_userDirOverride, ec.message()); + return false; + } + std::filesystem::create_directories(absolutePath, ec); + if (ec) { + g_logger.error("Unable to create user directory '{}': {}", absolutePath.generic_string(), ec.message()); + return false; + } + return setWriteDir(absolutePath.generic_string()); + } + const std::string userDir = getUserDir(); std::string dirName; #ifndef WIN32 diff --git a/src/framework/core/resourcemanager.h b/src/framework/core/resourcemanager.h index a114c4c0a..1587c67a4 100644 --- a/src/framework/core/resourcemanager.h +++ b/src/framework/core/resourcemanager.h @@ -35,6 +35,7 @@ public: bool discoverWorkDir(const std::string& existentFile); bool setupUserWriteDir(const std::string& appWriteDirName); + void setUserDirOverride(const std::string& path) { m_userDirOverride = path; } bool setWriteDir(const std::string& writeDir, bool create = false); bool addSearchPath(const std::string& path, bool pushFront = false); @@ -110,6 +111,7 @@ protected: private: std::string m_workDir; std::string m_writeDir; + std::string m_userDirOverride; std::filesystem::path m_binaryPath; std::deque m_searchPaths; }; diff --git a/src/main.cpp b/src/main.cpp index ca8dc4667..aa0b67042 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -65,11 +65,22 @@ bool shouldShowHelp(const std::vector& args) return false; } +std::string parseUserDir(const std::vector& args) +{ + static const std::string prefix = "--user-dir="; + for (const auto& arg : args) { + if (arg.starts_with(prefix)) + return arg.substr(prefix.size()); + } + return {}; +} + void printHelp(const std::string& executableName) { std::cout << "Usage: " << executableName << " [options]\n\n" "General options:\n" " --help, -h, /? Show this help message and exit\n" + " --user-dir= Use for configs/profiles instead of the default user dir\n" " --encrypt Encrypt assets (requires ENABLE_ENCRYPTION == 1 && ENABLE_ENCRYPTION_BUILDER == 1 build)\n\n" "DAT debugging:\n" " --dump-dat-to-json= Dump the specified Tibia DAT file or version as JSON (requires FRAMEWORK_EDITOR build)\n" @@ -118,6 +129,13 @@ int main(const int argc, const char* argv[]) g_resources.init(args[0].data()); #endif + // a --user-dir override isolates all persisted state (configs, remember + // password, bot profiles) under a caller-chosen dir; set before init.lua + // resolves the write dir via setupUserWriteDir. see #1540 + if (const auto userDir = parseUserDir(args); !userDir.empty()) { + g_resources.setUserDirOverride(userDir); + } + #if ENABLE_ENCRYPTION == 1 && ENABLE_ENCRYPTION_BUILDER == 1 if (std::find(args.begin(), args.end(), "--encrypt") != args.end()) { g_lua.init();