From 22862ca14c2b72551cd92f022b8a162e9974433e Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 20 Dec 2023 18:00:40 -0500 Subject: [PATCH] Download custom wine automatically, don't use system one by default --- CMakeLists.txt | 3 +- launcher/CMakeLists.txt | 5 +- launcher/include/assetupdater.h | 10 +++ launcher/include/profile.h | 16 ++-- launcher/profileconfig.kcfg | 11 +-- launcher/src/assetupdater.cpp | 99 ++++++++++++++++++++++-- launcher/src/gamerunner.cpp | 30 ++----- launcher/src/profile.cpp | 69 +++++++---------- launcher/ui/Settings/ProfileSettings.qml | 20 +---- 9 files changed, 151 insertions(+), 112 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5b9efa..98f071e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,7 @@ find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS WebView Concurrent Test) -find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami I18n Config CoreAddons) +find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami I18n Config CoreAddons Archive) find_package(PkgConfig REQUIRED) find_package(QCoro6 REQUIRED COMPONENTS Core Network Qml) qcoro_enable_coroutines() @@ -65,7 +65,6 @@ if (ENABLE_STEAM) endif () find_package(Qt6Keychain REQUIRED) -find_package(QuaZip-Qt6 REQUIRED) add_subdirectory(external) add_subdirectory(launcher) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 7dab296..566f150 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -129,7 +129,6 @@ target_link_libraries(astra PRIVATE physis physis-logger cotp - QuaZip::QuaZip Qt6Keychain::Qt6Keychain Qt6::Core Qt6::Network @@ -143,9 +142,11 @@ target_link_libraries(astra PRIVATE KF6::ConfigCore KF6::ConfigGui KF6::CoreAddons + KF6::Archive QCoro::Core QCoro::Network - QCoro::Qml) + QCoro::Qml + z) target_compile_options(astra PRIVATE -fexceptions) if (BUILD_FLATPAK) diff --git a/launcher/include/assetupdater.h b/launcher/include/assetupdater.h index d513a3f..3c77a9f 100644 --- a/launcher/include/assetupdater.h +++ b/launcher/include/assetupdater.h @@ -25,9 +25,12 @@ public: QCoro::Task update(); private: + QCoro::Task checkRemoteCompatibilityToolVersion(); + QCoro::Task checkRemoteDalamudAssetVersion(); QCoro::Task checkRemoteDalamudVersion(); + QCoro::Task installCompatibilityTool(); QCoro::Task installDalamudAssets(); QCoro::Task installDalamud(); QCoro::Task installRuntime(); @@ -37,12 +40,15 @@ private: [[nodiscard]] QUrl dotnetRuntimePackageUrl(const QString &version) const; [[nodiscard]] QUrl dotnetDesktopPackageUrl(const QString &version) const; + bool extractZip(const QString &filePath, const QString &directory); + LauncherCore &launcher; QString m_remoteDalamudVersion; QString m_remoteRuntimeVersion; QTemporaryDir m_tempDir; + QDir m_wineDir; QDir m_dataDir; QDir m_appDataDir; QDir m_dalamudDir; @@ -52,6 +58,10 @@ private: int m_remoteDalamudAssetVersion = -1; QJsonArray m_remoteDalamudAssetArray; QString m_remoteDalamudDownloadUrl; + QString m_remoteCompatibilityToolVersion; + // TODO: hardcoded + QString m_remoteCompatibilityToolUrl = + QStringLiteral("https://github.com/goatcorp/wine-xiv-git/releases/download/8.5.r4.g4211bac7/wine-xiv-staging-fsync-git-ubuntu-8.5.r4.g4211bac7.tar.xz"); Profile &m_profile; }; diff --git a/launcher/include/profile.h b/launcher/include/profile.h index 83541d0..4b9a6bf 100644 --- a/launcher/include/profile.h +++ b/launcher/include/profile.h @@ -23,7 +23,6 @@ class Profile : public QObject Q_PROPERTY(QString winePath READ winePath WRITE setWinePath NOTIFY winePathChanged) Q_PROPERTY(QString winePrefixPath READ winePrefixPath WRITE setWinePrefixPath NOTIFY winePrefixPathChanged) Q_PROPERTY(WineType wineType READ wineType WRITE setWineType NOTIFY wineTypeChanged) - Q_PROPERTY(bool esyncEnabled READ esyncEnabled WRITE setESyncEnabled NOTIFY useESyncChanged) Q_PROPERTY(bool gamescopeEnabled READ gamescopeEnabled WRITE setGamescopeEnabled NOTIFY useGamescopeChanged) Q_PROPERTY(bool gamemodeEnabled READ gamemodeEnabled WRITE setGamemodeEnabled NOTIFY useGamemodeChanged) Q_PROPERTY(bool directx9Enabled READ directx9Enabled WRITE setDirectX9Enabled NOTIFY useDX9Changed) @@ -46,12 +45,7 @@ class Profile : public QObject public: explicit Profile(LauncherCore &launcher, const QString &key, QObject *parent = nullptr); - enum class WineType { - System, - Custom, - Builtin, // macOS only - XIVOnMac // macOS only - }; + enum class WineType { BuiltIn, Custom }; Q_ENUM(WineType) enum class DalamudChannel { Stable, Staging }; @@ -77,9 +71,6 @@ public: [[nodiscard]] WineType wineType() const; void setWineType(WineType type); - [[nodiscard]] bool esyncEnabled() const; - void setESyncEnabled(bool value); - [[nodiscard]] bool gamescopeEnabled() const; void setGamescopeEnabled(bool value); @@ -144,6 +135,9 @@ public: [[nodiscard]] QString dalamudVersion() const; void setDalamudVersion(const QString &version); + [[nodiscard]] QString compatibilityToolVersion() const; + void setCompatibilityToolVersion(const QString &version); + BootData *bootData(); GameData *gameData(); @@ -157,7 +151,6 @@ Q_SIGNALS: void winePathChanged(); void winePrefixPathChanged(); void wineTypeChanged(); - void useESyncChanged(); void useGamescopeChanged(); void useGamemodeChanged(); void useDX9Changed(); @@ -195,6 +188,7 @@ private: QString m_dalamudVersion; int m_dalamudAssetVersion = -1; QString m_runtimeVersion; + QString m_compatibilityToolVersion; bool m_loggedIn = false; diff --git a/launcher/profileconfig.kcfg b/launcher/profileconfig.kcfg index d76ab32..1535e1f 100644 --- a/launcher/profileconfig.kcfg +++ b/launcher/profileconfig.kcfg @@ -26,19 +26,12 @@ SPDX-License-Identifier: CC0-1.0 - + - - - - - System - - - false + BuiltIn false diff --git a/launcher/src/assetupdater.cpp b/launcher/src/assetupdater.cpp index b62859b..8ebffcc 100644 --- a/launcher/src/assetupdater.cpp +++ b/launcher/src/assetupdater.cpp @@ -6,6 +6,8 @@ #include "utility.h" #include +#include +#include #include #include #include @@ -13,7 +15,6 @@ #include #include -#include #include using namespace Qt::StringLiterals; @@ -27,6 +28,18 @@ AssetUpdater::AssetUpdater(Profile &profile, LauncherCore &launcher, QObject *pa QCoro::Task AssetUpdater::update() { + qInfo(ASTRA_LOG) << "Checking for compatibility tool updates..."; + + m_dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + const QDir compatibilityToolDir = m_dataDir.absoluteFilePath(QStringLiteral("tools")); + m_wineDir = compatibilityToolDir.absoluteFilePath(QStringLiteral("wine")); + + Utility::createPathIfNeeded(m_wineDir); + + if (!co_await checkRemoteCompatibilityToolVersion()) { + co_return false; + } + if (!m_profile.dalamudEnabled()) { co_return true; } @@ -53,6 +66,23 @@ QCoro::Task AssetUpdater::update() co_return true; } +QCoro::Task AssetUpdater::checkRemoteCompatibilityToolVersion() +{ + // TODO: hardcoded for now + m_remoteCompatibilityToolVersion = QStringLiteral("wine-xiv-staging-fsync-git-8.5.r4.g4211bac7"); + + qInfo(ASTRA_LOG) << "Compatibility tool remote version" << m_remoteCompatibilityToolVersion; + + // TODO: this version should not be profile specific + if (m_remoteCompatibilityToolVersion != m_profile.compatibilityToolVersion()) { + qInfo(ASTRA_LOG) << "Compatibility tool out of date"; + + co_return co_await installCompatibilityTool(); + } + + co_return true; +} + QCoro::Task AssetUpdater::checkRemoteDalamudAssetVersion() { // first we want to fetch the list of assets required @@ -129,6 +159,46 @@ QCoro::Task AssetUpdater::checkRemoteDalamudVersion() co_return true; } +QCoro::Task AssetUpdater::installCompatibilityTool() +{ + Q_EMIT launcher.stageChanged(i18n("Updating compatibility tool...")); + + const QNetworkRequest request = QNetworkRequest(QUrl(m_remoteCompatibilityToolUrl)); + Utility::printRequest(QStringLiteral("GET"), request); + + const auto reply = launcher.mgr()->get(request); + co_await reply; + + qInfo(ASTRA_LOG) << "Finished downloading compatibility tool"; + + QFile file(m_tempDir.filePath(QStringLiteral("latest.tar.xz"))); + file.open(QIODevice::WriteOnly); + file.write(reply->readAll()); + file.close(); + + KTar archive(m_tempDir.filePath(QStringLiteral("latest.tar.xz"))); + if (!archive.open(QIODevice::ReadOnly)) { + qCritical(ASTRA_LOG) << "Failed to install compatibility tool"; + Q_EMIT launcher.dalamudError(i18n("Failed to install compatibility tool.")); + co_return false; + } + + // the first directory is the same as the version we download + const KArchiveDirectory *root = dynamic_cast(archive.directory()->entry(m_remoteCompatibilityToolVersion)); + root->copyTo(m_wineDir.absolutePath(), true); + + archive.close(); + + QFile verFile(m_wineDir.absoluteFilePath(QStringLiteral("wine.ver"))); + verFile.open(QIODevice::WriteOnly | QIODevice::Text); + verFile.write(m_remoteCompatibilityToolVersion.toUtf8()); + verFile.close(); + + m_profile.setCompatibilityToolVersion(m_remoteCompatibilityToolVersion); + + co_return true; +} + QCoro::Task AssetUpdater::installDalamudAssets() { Q_EMIT launcher.stageChanged(i18n("Updating Dalamud assets...")); @@ -190,10 +260,7 @@ QCoro::Task AssetUpdater::installDalamud() file.write(reply->readAll()); file.close(); - const bool success = - !JlCompress::extractDir(m_tempDir.filePath(QStringLiteral("latest.zip")), m_dalamudDir.absoluteFilePath(m_profile.dalamudChannelName())).empty(); - - if (!success) { + if (!extractZip(m_tempDir.filePath(QStringLiteral("latest.zip")), m_dalamudDir.absoluteFilePath(m_profile.dalamudChannelName()))) { qCritical(ASTRA_LOG) << "Failed to install Dalamud"; Q_EMIT launcher.dalamudError(i18n("Failed to install Dalamud.")); co_return false; @@ -240,8 +307,8 @@ QCoro::Task AssetUpdater::installRuntime() file.close(); } - bool success = !JlCompress::extractDir(m_tempDir.filePath(QStringLiteral("dotnet-core.zip")), m_dalamudRuntimeDir.absolutePath()).empty(); - success |= !JlCompress::extractDir(m_tempDir.filePath(QStringLiteral("dotnet-desktop.zip")), m_dalamudRuntimeDir.absolutePath()).empty(); + bool success = extractZip(m_tempDir.filePath(QStringLiteral("dotnet-core.zip")), m_dalamudRuntimeDir.absolutePath()); + success |= extractZip(m_tempDir.filePath(QStringLiteral("dotnet-desktop.zip")), m_dalamudRuntimeDir.absolutePath()); if (!success) { qCritical(ASTRA_LOG) << "Failed to install dotnet"; @@ -298,4 +365,22 @@ QUrl AssetUpdater::dotnetDesktopPackageUrl(const QString &version) const return url; } +bool AssetUpdater::extractZip(const QString &filePath, const QString &directory) +{ + KZip archive(filePath); + if (!archive.open(QIODevice::ReadOnly)) { + return false; + } + + const KArchiveDirectory *root = archive.directory(); + if (!root->copyTo(directory, true)) { + archive.close(); + return false; + } + + archive.close(); + + return true; +} + #include "moc_assetupdater.cpp" \ No newline at end of file diff --git a/launcher/src/gamerunner.cpp b/launcher/src/gamerunner.cpp index 5e934a5..831c905 100644 --- a/launcher/src/gamerunner.cpp +++ b/launcher/src/gamerunner.cpp @@ -206,11 +206,12 @@ void GameRunner::launchExecutable(const Profile &profile, QProcess *process, con #endif #if defined(Q_OS_LINUX) - if (profile.esyncEnabled()) { - env.insert(QStringLiteral("WINEESYNC"), QString::number(1)); - env.insert(QStringLiteral("WINEFSYNC"), QString::number(1)); - env.insert(QStringLiteral("WINEFSYNC_FUTEX2"), QString::number(1)); - } + env.insert(QStringLiteral("WINEESYNC"), QString::number(1)); + env.insert(QStringLiteral("WINEFSYNC"), QString::number(1)); + env.insert(QStringLiteral("WINEFSYNC_FUTEX2"), QString::number(1)); + + // env.insert(QStringLiteral("VK_LAYER_RENDERDOC_Capture"), QStringLiteral("VK_LAYER_RENDERDOC_Capture")); + // env.insert(QStringLiteral("ENABLE_VULKAN_RENDERDOC_CAPTURE"), QString::number(1)); const QString logDir = Utility::stateDirectory().absoluteFilePath(QStringLiteral("log")); @@ -268,25 +269,6 @@ void GameRunner::launchExecutable(const Profile &profile, QProcess *process, con } else { env.insert(QStringLiteral("WINEPREFIX"), profile.winePrefixPath()); - // XIV on Mac bundle their own Wine install directory, complete with libs etc - if (profile.wineType() == Profile::WineType::XIVOnMac) { - // TODO: don't hardcode this - QString xivLibPath = QStringLiteral( - "/Applications/XIV on Mac.app/Contents/Resources/wine/lib:/Applications/XIV on " - "Mac.app/Contents/Resources/MoltenVK/modern"); - - env.insert(QStringLiteral("DYLD_FALLBACK_LIBRARY_PATH"), xivLibPath); - env.insert(QStringLiteral("DYLD_VERSIONED_LIBRARY_PATH"), xivLibPath); - env.insert(QStringLiteral("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE"), QString::number(1)); - env.insert(QStringLiteral("MVK_CONFIG_RESUME_LOST_DEVICE"), QString::number(1)); - env.insert(QStringLiteral("MVK_ALLOW_METAL_FENCES"), QString::number(1)); - env.insert(QStringLiteral("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS"), QString::number(1)); - } - -#if defined(FLATPAK) - arguments.push_back(QStringLiteral("flatpak-spawn")); - arguments.push_back(QStringLiteral("--host")); -#endif arguments.push_back(profile.winePath()); } #endif diff --git a/launcher/src/profile.cpp b/launcher/src/profile.cpp index 9a32a30..0f27fd1 100644 --- a/launcher/src/profile.cpp +++ b/launcher/src/profile.cpp @@ -30,6 +30,19 @@ void Profile::readDalamudInfo() { const QDir dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + const QDir compatibilityToolDir = dataDir.absoluteFilePath(QStringLiteral("tools")); + const QDir wineDir = compatibilityToolDir.absoluteFilePath(QStringLiteral("wine")); + if (wineDir.exists()) { + const QString wineVer = wineDir.absoluteFilePath(QStringLiteral("wine.ver")); + if (QFile::exists(wineVer)) { + QFile wineJson(wineVer); + wineJson.open(QFile::ReadOnly | QFile::Text); + + m_compatibilityToolVersion = QString::fromUtf8(wineJson.readAll()); + qInfo(ASTRA_LOG) << "Compatibility tool version:" << m_compatibilityToolVersion; + } + } + const QDir dalamudDir = dataDir.absoluteFilePath(QStringLiteral("dalamud")); if (dalamudDir.exists()) { @@ -96,7 +109,6 @@ void Profile::readGameData() void Profile::readWineInfo() { -#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) auto wineProcess = new QProcess(this); connect(wineProcess, &QProcess::readyReadStandardOutput, this, [wineProcess, this] { @@ -106,7 +118,6 @@ void Profile::readWineInfo() wineProcess->start(winePath(), {QStringLiteral("--version")}); wineProcess->waitForFinished(); -#endif } QString Profile::name() const @@ -140,32 +151,20 @@ void Profile::setGamePath(const QString &path) QString Profile::winePath() const { -#if defined(Q_OS_MAC) switch (wineType()) { - case WineType::System: // system wine - return "/usr/local/bin/wine64"; - case WineType::Custom: // custom path - return m_config->winePath(); - case WineType::Builtin: // ffxiv built-in (for mac users) - return "/Applications/FINAL FANTASY XIV " - "ONLINE.app/Contents/SharedSupport/finalfantasyxiv/FINAL FANTASY XIV ONLINE/wine"; - case WineType::XIVOnMac: - return "/Applications/XIV on Mac.app/Contents/Resources/wine/bin/wine64"; - } -#endif + case WineType::BuiltIn: { + const QDir dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + const QDir compatibilityToolDir = dataDir.absoluteFilePath(QStringLiteral("tools")); + const QDir wineDir = compatibilityToolDir.absoluteFilePath(QStringLiteral("wine")); + const QDir wineBinDir = wineDir.absoluteFilePath(QStringLiteral("bin")); -#if defined(Q_OS_LINUX) - switch (wineType()) { - case WineType::System: // system wine (should be in $PATH) - return QStringLiteral("wine"); + return wineBinDir.absoluteFilePath(QStringLiteral("wine64")); + } case WineType::Custom: // custom pth return m_config->winePath(); default: return {}; } -#endif - - return {}; } void Profile::setWinePath(const QString &path) @@ -206,20 +205,6 @@ void Profile::setWineType(const WineType type) } } -bool Profile::esyncEnabled() const -{ - return m_config->useESync(); -} - -void Profile::setESyncEnabled(const bool value) -{ - if (m_config->useESync() != value) { - m_config->setUseESync(value); - m_config->save(); - Q_EMIT useESyncChanged(); - } -} - bool Profile::gamescopeEnabled() const { return m_config->useGamescope(); @@ -477,10 +462,6 @@ QString Profile::uuid() const QString Profile::wineVersionText() const { - if (m_launcher.isSteam()) { - return i18n("Wine is being managed by Steam."); - } - if (!isWineInstalled()) { return i18n("Wine is not installed."); } else { @@ -558,6 +539,16 @@ void Profile::setDalamudVersion(const QString &version) m_dalamudVersion = version; } +QString Profile::compatibilityToolVersion() const +{ + return m_compatibilityToolVersion; +} + +void Profile::setCompatibilityToolVersion(const QString &version) +{ + m_compatibilityToolVersion = version; +} + BootData *Profile::bootData() { return m_bootData; diff --git a/launcher/ui/Settings/ProfileSettings.qml b/launcher/ui/Settings/ProfileSettings.qml index 6f641e1..ca48644 100644 --- a/launcher/ui/Settings/ProfileSettings.qml +++ b/launcher/ui/Settings/ProfileSettings.qml @@ -83,10 +83,9 @@ FormCard.FormCardPage { id: wineTypeDelegate text: i18n("Wine Type") - model: ["System", "Custom"] + model: [i18n("Built-in"), i18n("Custom")] currentIndex: page.profile.wineType onCurrentIndexChanged: page.profile.wineType = currentIndex - enabled: !LauncherCore.isSteam } FormCard.FormDelegateSeparator { @@ -99,7 +98,7 @@ FormCard.FormCardPage { text: i18n("Wine Executable") file: page.profile.winePath - enabled: !LauncherCore.isSteam && page.profile.wineType !== Profile.System + enabled: page.profile.wineType !== Profile.BuiltIn } FormCard.FormDelegateSeparator { @@ -112,7 +111,6 @@ FormCard.FormCardPage { text: i18n("Wine Prefix Path") folder: page.profile.winePrefixPath - enabled: !LauncherCore.isSteam } FormCard.FormDelegateSeparator { @@ -131,20 +129,6 @@ FormCard.FormCardPage { FormCard.FormCard { Layout.fillWidth: true - FormCard.FormCheckDelegate { - id: esyncDelegate - - text: i18n("Enable ESync") - description: i18n("Could improve game performance, but requires a patched Wine and kernel.") - checked: page.profile.esyncEnabled - onCheckedChanged: page.profile.esyncEnabled = checked - } - - FormCard.FormDelegateSeparator { - above: esyncDelegate - below: gamemodeDelegate - } - FormCard.FormCheckDelegate { text: i18n("Enable Gamescope") description: i18n("A micro-compositor that uses Wayland to create a nested session.\nIf you use fullscreen mode, it may improve input handling.")