From 807cf0e062d0a1df2ef3ba8f9f3dd9fd8349a6a6 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 9 Nov 2024 15:47:34 -0500 Subject: [PATCH] Improve character sync error handling, and the sync itself Ported from libQuotient's invokeLogin to our own custom one, so we can avoid the initial sync until login. Then, make sure we find the room *after* the sync, not before it. Also since I wiped the media cache from my server, I hit a crash since it tried to open an invalid ZIP file. Now we can handle that. --- launcher/include/charactersync.h | 2 +- launcher/include/syncmanager.h | 4 +-- launcher/src/charactersync.cpp | 33 ++++++++++++----- launcher/src/syncmanager.cpp | 62 +++++++++++++++++++++----------- 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/launcher/include/charactersync.h b/launcher/include/charactersync.h index c40dae9..ce915ba 100644 --- a/launcher/include/charactersync.h +++ b/launcher/include/charactersync.h @@ -27,7 +27,7 @@ public: private: QCoro::Task uploadCharacterData(const QDir &dir, const QString &id); - QCoro::Task downloadCharacterData(const QDir &dir, const QString &id, const QString &contentUri); + QCoro::Task downloadCharacterData(const QDir &dir, const QString &id, const QString &contentUri); LauncherCore &launcher; Account &m_account; diff --git a/launcher/include/syncmanager.h b/launcher/include/syncmanager.h index 0ba471a..02fe709 100644 --- a/launcher/include/syncmanager.h +++ b/launcher/include/syncmanager.h @@ -105,12 +105,12 @@ Q_SIGNALS: void loginError(const QString &message); private: + void invokeLogin(); QString roomId() const; void setRoomId(const QString &roomId); QCoro::Task<> findRoom(); - QCoro::Task<> beginInitialSync(); Quotient::AccountRegistry m_accountRegistry; Quotient::Room *m_currentRoom = nullptr; -}; \ No newline at end of file +}; diff --git a/launcher/src/charactersync.cpp b/launcher/src/charactersync.cpp index 5d03ce5..b953d0c 100644 --- a/launcher/src/charactersync.cpp +++ b/launcher/src/charactersync.cpp @@ -33,6 +33,9 @@ QCoro::Task CharacterSync::sync(const bool initialSync) co_return false; } + // Perform a manual sync just in case + co_await syncManager->sync(); + if (!syncManager->isReady()) { Q_EMIT launcher.stageChanged(i18n("Waiting for sync connection...")); @@ -42,9 +45,6 @@ QCoro::Task CharacterSync::sync(const bool initialSync) Q_EMIT launcher.stageChanged(i18n("Synchronizing character data...")); - // Perform a manual sync just in case - co_await syncManager->sync(); - // On game boot, check if we need the lock. Otherwise break it when we clean up. if (initialSync) { if (const auto hostname = co_await syncManager->checkLock(); hostname.has_value()) { @@ -110,10 +110,15 @@ QCoro::Task CharacterSync::sync(const bool initialSync) const bool needsDownload = areFilesDifferent; if (needsUpload) { + qCDebug(ASTRA_LOG) << id << "uploading character data"; // if we didn't upload character data yet, upload it now co_await uploadCharacterData(dir.absoluteFilePath(), id); } else if (needsDownload) { - co_await downloadCharacterData(dir.absoluteFilePath(), id, previousData->mxcUri); + qCDebug(ASTRA_LOG) << id << "downloading character data"; + if (!co_await downloadCharacterData(dir.absoluteFilePath(), id, previousData->mxcUri)) { + Q_EMIT launcher.loginError(i18n("Failed to sync character data from the server. Please do another initial sync under Settings and try again.")); + co_return false; + } } } @@ -149,7 +154,7 @@ QCoro::Task CharacterSync::uploadCharacterData(const QDir &dir, const QStr co_return; } -QCoro::Task CharacterSync::downloadCharacterData(const QDir &dir, const QString &id, const QString &contentUri) +QCoro::Task CharacterSync::downloadCharacterData(const QDir &dir, const QString &id, const QString &contentUri) { const QTemporaryDir tempDir; @@ -160,17 +165,27 @@ QCoro::Task CharacterSync::downloadCharacterData(const QDir &dir, const QS auto zip = new KZip(tempZipPath); zip->setCompression(KZip::DeflateCompression); zip->open(QIODevice::ReadOnly); + if (zip->isOpen()) { + qCDebug(ASTRA_LOG) << "contents:" << zip->directory()->entries(); - qCDebug(ASTRA_LOG) << "contents:" << zip->directory()->entries(); + if (auto file = zip->directory()->file(gearsetFilename); file != nullptr) { + Q_UNUSED(file->copyTo(dir.absolutePath())) - Q_UNUSED(zip->directory()->file(gearsetFilename)->copyTo(dir.absolutePath())) + qCDebug(ASTRA_LOG) << "Extracted character data!"; - qCDebug(ASTRA_LOG) << "Extracted character data!"; + zip->close(); + delete zip; + + co_return true; + } + } zip->close(); delete zip; - co_return; + qCDebug(ASTRA_LOG) << "Failed to read character ZIP!"; + + co_return false; } #include "moc_charactersync.cpp" diff --git a/launcher/src/syncmanager.cpp b/launcher/src/syncmanager.cpp index 551cbb5..bb496a3 100644 --- a/launcher/src/syncmanager.cpp +++ b/launcher/src/syncmanager.cpp @@ -33,7 +33,7 @@ using namespace Quotient; SyncManager::SyncManager(QObject *parent) : QObject(parent) { - m_accountRegistry.invokeLogin(); // TODO: port from invokeLogin + invokeLogin(); connect(&m_accountRegistry, &AccountRegistry::rowsInserted, this, [this]() { connection()->setCacheState(false); connection()->setLazyLoading(false); @@ -43,8 +43,6 @@ SyncManager::SyncManager(QObject *parent) Q_EMIT connectedChanged(); Q_EMIT userIdChanged(); Q_EMIT connectionChanged(); - - beginInitialSync(); }); connect(&m_accountRegistry, &AccountRegistry::rowsRemoved, this, [this]() { Q_EMIT connectedChanged(); @@ -117,7 +115,11 @@ QCoro::Task<> SyncManager::sync() auto connection = m_accountRegistry.accounts().first(); connection->sync(); co_await qCoro(connection, &Connection::syncDone); - m_accountRegistry.accounts().first()->stopSync(); + + if (!m_currentRoom) { + co_await findRoom(); + } + co_return; } @@ -132,6 +134,18 @@ QCoro::Task SyncManager::findRoom() // If we have no room id set, we need to find the correct room type const bool needsFirstTimeRoom = roomId.isEmpty(); + if (!needsFirstTimeRoom) { + auto room = m_accountRegistry.accounts().first()->room(roomId); + if (room) { + qCDebug(ASTRA_LOG) << "Found pre-existing room!"; + + m_currentRoom = room; + Q_EMIT isReadyChanged(); + + co_return; + } + } + // Try to find our room auto rooms = m_accountRegistry.accounts().first()->rooms(Quotient::JoinState::Join); for (auto room : rooms) { @@ -151,15 +165,6 @@ QCoro::Task SyncManager::findRoom() Q_EMIT isReadyChanged(); } } - } else { - if (room->id() == roomId) { - qCDebug(ASTRA_LOG) << "Found pre-existing room!"; - - m_currentRoom = room; - Q_EMIT isReadyChanged(); - - co_return; - } } } @@ -190,6 +195,28 @@ QCoro::Task SyncManager::findRoom() co_return; } +void SyncManager::invokeLogin() +{ + // Simplified from libQuotient, but this can be simplified even more + const auto accounts = SettingsGroup("Accounts"_L1).childGroups(); + for (const auto &accountId : accounts) { + AccountSettings account{accountId}; + + if (account.homeserver().isEmpty()) + continue; + + auto accessTokenLoadingJob = new QKeychain::ReadPasswordJob(qAppName(), this); + accessTokenLoadingJob->setKey(accountId); + connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob]() { + AccountSettings account{accountId}; + auto connection = new Connection(account.homeserver()); + connection->assumeIdentity(account.userId(), account.deviceId(), QString::fromUtf8(accessTokenLoadingJob->binaryData())); + m_accountRegistry.add(connection); + }); + accessTokenLoadingJob->start(); + } +} + QString SyncManager::roomId() const { return KSharedConfig::openStateConfig()->group(QStringLiteral("Sync")).readEntry(QStringLiteral("RoomId")); @@ -204,6 +231,7 @@ void SyncManager::setRoomId(const QString &roomId) bool SyncManager::isReady() const { + qInfo() << connected() << m_currentRoom; return connected() && m_currentRoom; } @@ -294,12 +322,4 @@ QCoro::Task<> SyncManager::breakLock() co_return; } -QCoro::Task<> SyncManager::beginInitialSync() -{ - co_await sync(); - - // Find the room we need to sync with - findRoom(); -} - #include "moc_syncmanager.cpp"