mirror of
https://github.com/redstrate/Astra.git
synced 2025-04-20 19:57:45 +00:00
Implement sync locking, and upload data when quitting the game
This commit is contained in:
parent
3d989d5790
commit
a0da4d02b7
9 changed files with 92 additions and 18 deletions
|
@ -21,8 +21,9 @@ public:
|
||||||
explicit CharacterSync(Account &account, LauncherCore &launcher, QObject *parent = nullptr);
|
explicit CharacterSync(Account &account, LauncherCore &launcher, QObject *parent = nullptr);
|
||||||
|
|
||||||
/// Checks and synchronizes character files as necessary.
|
/// Checks and synchronizes character files as necessary.
|
||||||
|
/// \param initialSync Whether this is the initial sync on game start.
|
||||||
/// \return False if the synchronization failed.
|
/// \return False if the synchronization failed.
|
||||||
QCoro::Task<bool> sync();
|
QCoro::Task<bool> sync(bool initialSync = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QCoro::Task<void> uploadCharacterData(const QDir &dir, const QString &id);
|
QCoro::Task<void> uploadCharacterData(const QDir &dir, const QString &id);
|
||||||
|
|
|
@ -136,7 +136,7 @@ public:
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void loadingFinished();
|
void loadingFinished();
|
||||||
void successfulLaunch();
|
void successfulLaunch();
|
||||||
void gameClosed();
|
void gameClosed(Profile *profile);
|
||||||
void loginError(QString message);
|
void loginError(QString message);
|
||||||
void dalamudError(QString message);
|
void dalamudError(QString message);
|
||||||
void miscError(QString message);
|
void miscError(QString message);
|
||||||
|
@ -158,6 +158,8 @@ private:
|
||||||
|
|
||||||
QCoro::Task<> fetchNews();
|
QCoro::Task<> fetchNews();
|
||||||
|
|
||||||
|
QCoro::Task<> handleGameExit(Profile *profile);
|
||||||
|
|
||||||
SteamAPI *m_steamApi = nullptr;
|
SteamAPI *m_steamApi = nullptr;
|
||||||
|
|
||||||
bool m_loadingFinished = false;
|
bool m_loadingFinished = false;
|
||||||
|
|
|
@ -61,6 +61,15 @@ public:
|
||||||
/// Downloads character data
|
/// Downloads character data
|
||||||
QCoro::Task<bool> downloadCharacterData(const QString &mxcUri, const QString &destPath);
|
QCoro::Task<bool> downloadCharacterData(const QString &mxcUri, const QString &destPath);
|
||||||
|
|
||||||
|
/// Checks the lock on the sync
|
||||||
|
QCoro::Task<std::optional<QString>> checkLock();
|
||||||
|
|
||||||
|
/// Sets the sync lock to the device's hostname
|
||||||
|
QCoro::Task<> setLock();
|
||||||
|
|
||||||
|
/// Breaks the sync lock
|
||||||
|
QCoro::Task<> breakLock();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void connectedChanged();
|
void connectedChanged();
|
||||||
void userIdChanged();
|
void userIdChanged();
|
||||||
|
|
|
@ -16,14 +16,12 @@ CharacterSync::CharacterSync(Account &account, LauncherCore &launcher, QObject *
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoro::Task<bool> CharacterSync::sync()
|
QCoro::Task<bool> CharacterSync::sync(const bool initialSync)
|
||||||
{
|
{
|
||||||
if (!launcher.settings()->enableSync()) {
|
if (!launcher.settings()->enableSync()) {
|
||||||
co_return true;
|
co_return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qInfo() << "A";
|
|
||||||
|
|
||||||
auto syncManager = launcher.syncManager();
|
auto syncManager = launcher.syncManager();
|
||||||
if (!syncManager->connected()) {
|
if (!syncManager->connected()) {
|
||||||
qInfo() << "B";
|
qInfo() << "B";
|
||||||
|
@ -32,8 +30,6 @@ QCoro::Task<bool> CharacterSync::sync()
|
||||||
co_return false;
|
co_return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
qInfo() << "C";
|
|
||||||
|
|
||||||
if (!syncManager->isReady()) {
|
if (!syncManager->isReady()) {
|
||||||
Q_EMIT launcher.stageChanged(i18n("Waiting for sync connection..."));
|
Q_EMIT launcher.stageChanged(i18n("Waiting for sync connection..."));
|
||||||
|
|
||||||
|
@ -43,6 +39,21 @@ QCoro::Task<bool> CharacterSync::sync()
|
||||||
|
|
||||||
Q_EMIT launcher.stageChanged(i18n("Synchronizing character data..."));
|
Q_EMIT launcher.stageChanged(i18n("Synchronizing character data..."));
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
// Don't warn about our own failures
|
||||||
|
if (hostname != QSysInfo::machineHostName()) {
|
||||||
|
Q_EMIT launcher.loginError(i18n("Device %1 has not yet uploaded it's character data. Astra will not continue until that device is re-synced."));
|
||||||
|
co_return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syncManager->setLock();
|
||||||
|
} else {
|
||||||
|
syncManager->breakLock();
|
||||||
|
}
|
||||||
|
|
||||||
// so first, we need to list the character folders
|
// so first, we need to list the character folders
|
||||||
// we sync each one separately
|
// we sync each one separately
|
||||||
QList<QFileInfo> characterDirs;
|
QList<QFileInfo> characterDirs;
|
||||||
|
@ -63,15 +74,19 @@ QCoro::Task<bool> CharacterSync::sync()
|
||||||
for (const auto &dir : characterDirs) {
|
for (const auto &dir : characterDirs) {
|
||||||
const QString id = dir.fileName(); // FFXIV_CHR0040000001000001 for example
|
const QString id = dir.fileName(); // FFXIV_CHR0040000001000001 for example
|
||||||
const auto previousData = co_await syncManager->getUploadedCharacterData(id);
|
const auto previousData = co_await syncManager->getUploadedCharacterData(id);
|
||||||
if (!previousData.has_value()) {
|
|
||||||
|
// TODO: make this a little bit smarter. We shouldn't waste time re-uploading data that's exactly the same.
|
||||||
|
if (!initialSync || !previousData.has_value()) {
|
||||||
// if we didn't upload character data yet, upload it now
|
// if we didn't upload character data yet, upload it now
|
||||||
co_await uploadCharacterData(dir.absoluteFilePath(), id);
|
co_await uploadCharacterData(dir.absoluteFilePath(), id);
|
||||||
} else {
|
} else {
|
||||||
// otherwise, download it
|
// otherwise, download it
|
||||||
|
|
||||||
// but check first if it's our hostname
|
const bool exists = QFile::exists(dir.absoluteFilePath() + QStringLiteral("/GEARSET.DAT"));
|
||||||
if (QSysInfo::machineHostName() == previousData->hostname) {
|
|
||||||
qCDebug(ASTRA_LOG) << "Skipping! We uploaded this data.";
|
// but check first if it's our hostname. only skip if it exists
|
||||||
|
if (exists && QSysInfo::machineHostName() == previousData->hostname) {
|
||||||
|
qCDebug(ASTRA_LOG) << "Skipping" << id << "We uploaded this data.";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ void GameRunner::beginVanillaGame(const QString &gameExecutablePath, Profile &pr
|
||||||
connect(gameProcess, &QProcess::finished, this, [this, &profile](const int exitCode) {
|
connect(gameProcess, &QProcess::finished, this, [this, &profile](const int exitCode) {
|
||||||
profile.setLoggedIn(false);
|
profile.setLoggedIn(false);
|
||||||
Q_UNUSED(exitCode)
|
Q_UNUSED(exitCode)
|
||||||
Q_EMIT m_launcher.gameClosed();
|
Q_EMIT m_launcher.gameClosed(&profile);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto args = getGameArgs(profile, auth);
|
auto args = getGameArgs(profile, auth);
|
||||||
|
@ -109,7 +109,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr
|
||||||
auto watcher = new ProcessWatcher(PID);
|
auto watcher = new ProcessWatcher(PID);
|
||||||
connect(watcher, &ProcessWatcher::finished, this, [this, &profile] {
|
connect(watcher, &ProcessWatcher::finished, this, [this, &profile] {
|
||||||
profile.setLoggedIn(false);
|
profile.setLoggedIn(false);
|
||||||
Q_EMIT m_launcher.gameClosed();
|
Q_EMIT m_launcher.gameClosed(&profile);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr
|
||||||
|
|
||||||
// If Dalamud didn't give a valid PID, OK. Let's just do our previous status quo and inidcate we did log out.
|
// If Dalamud didn't give a valid PID, OK. Let's just do our previous status quo and inidcate we did log out.
|
||||||
profile.setLoggedIn(false);
|
profile.setLoggedIn(false);
|
||||||
Q_EMIT m_launcher.gameClosed();
|
Q_EMIT m_launcher.gameClosed(&profile);
|
||||||
});
|
});
|
||||||
|
|
||||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
|
|
@ -40,6 +40,8 @@ LauncherCore::LauncherCore()
|
||||||
m_accountManager = new AccountManager(*this, this);
|
m_accountManager = new AccountManager(*this, this);
|
||||||
m_runner = new GameRunner(*this, this);
|
m_runner = new GameRunner(*this, this);
|
||||||
|
|
||||||
|
connect(this, &LauncherCore::gameClosed, this, &LauncherCore::handleGameExit);
|
||||||
|
|
||||||
#ifdef BUILD_SYNC
|
#ifdef BUILD_SYNC
|
||||||
m_syncManager = new SyncManager(this);
|
m_syncManager = new SyncManager(this);
|
||||||
#endif
|
#endif
|
||||||
|
@ -513,4 +515,19 @@ QCoro::Task<> LauncherCore::fetchNews()
|
||||||
Q_EMIT newsChanged();
|
Q_EMIT newsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QCoro::Task<> LauncherCore::handleGameExit(Profile *profile)
|
||||||
|
{
|
||||||
|
#ifdef BUILD_SYNC
|
||||||
|
qCDebug(ASTRA_LOG) << "Game closed! Uploading character data...";
|
||||||
|
const auto characterSync = new CharacterSync(*profile->account(), *this, this);
|
||||||
|
co_await characterSync->sync(false); // TODO: handle errors and especially interactive ones
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Otherwise, quit when everything is finished.
|
||||||
|
if (m_settings->closeWhenLaunched()) {
|
||||||
|
QCoreApplication::exit();
|
||||||
|
}
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_launchercore.cpp"
|
#include "moc_launchercore.cpp"
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
const QString roomType = QStringLiteral("zone.xiv.astra-sync");
|
const QString roomType = QStringLiteral("zone.xiv.astra-sync");
|
||||||
const QString syncEventType = QStringLiteral("zone.xiv.astra.sync");
|
const QString syncEventType = QStringLiteral("zone.xiv.astra.sync");
|
||||||
|
const QString lockEventType = QStringLiteral("zone.xiv.astra.lock");
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
|
@ -250,4 +251,35 @@ QCoro::Task<bool> SyncManager::downloadCharacterData(const QString &mxcUri, cons
|
||||||
co_return true;
|
co_return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QCoro::Task<std::optional<QString>> SyncManager::checkLock()
|
||||||
|
{
|
||||||
|
const auto lockEvent = m_currentRoom->currentState().contentJson(syncEventType, QStringLiteral("latest"));
|
||||||
|
if (lockEvent.isEmpty()) {
|
||||||
|
co_return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(ASTRA_LOG) << "previous lock event:" << lockEvent;
|
||||||
|
const QString hostname = lockEvent[QStringLiteral("hostname")].toString();
|
||||||
|
if (hostname == QStringLiteral("none")) {
|
||||||
|
co_return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
co_return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoro::Task<> SyncManager::setLock()
|
||||||
|
{
|
||||||
|
auto lockSetState =
|
||||||
|
m_currentRoom->setState(syncEventType, QStringLiteral("latest"), QJsonObject{{QStringLiteral("hostname"), QSysInfo::machineHostName()}});
|
||||||
|
co_await qCoro(lockSetState, &BaseJob::finished);
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoro::Task<> SyncManager::breakLock()
|
||||||
|
{
|
||||||
|
auto lockSetState = m_currentRoom->setState(syncEventType, QStringLiteral("latest"), QJsonObject{{QStringLiteral("hostname"), QStringLiteral("none")}});
|
||||||
|
co_await qCoro(lockSetState, &BaseJob::finished);
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_syncmanager.cpp"
|
#include "moc_syncmanager.cpp"
|
||||||
|
|
|
@ -113,9 +113,7 @@ Kirigami.ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGameClosed() {
|
function onGameClosed() {
|
||||||
if (LauncherCore.settings.closeWhenLaunched) {
|
if (!LauncherCore.settings.closeWhenLaunched) {
|
||||||
Qt.callLater(Qt.quit);
|
|
||||||
} else {
|
|
||||||
appWindow.checkSetup();
|
appWindow.checkSetup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ FormCard.FormCardPage {
|
||||||
FormCard.FormCheckDelegate {
|
FormCard.FormCheckDelegate {
|
||||||
id: closeAstraDelegate
|
id: closeAstraDelegate
|
||||||
|
|
||||||
text: i18n("Close Astra when game is launched")
|
text: i18n("Hide Astra when game is launched")
|
||||||
checked: LauncherCore.settings.closeWhenLaunched
|
checked: LauncherCore.settings.closeWhenLaunched
|
||||||
onCheckedChanged: LauncherCore.settings.closeWhenLaunched = checked
|
onCheckedChanged: LauncherCore.settings.closeWhenLaunched = checked
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue