diff options
Diffstat (limited to 'launcher')
68 files changed, 2098 insertions, 1439 deletions
diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 37724038..2d0c81bb 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -827,6 +827,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) qDebug() << "Loading accounts..."; m_accounts->setListFilePath("accounts.json", true); m_accounts->loadList(); + m_accounts->fillQueue(); qDebug() << "<> Accounts loaded."; } diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 08c878d1..2dfc78b5 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -196,36 +196,52 @@ set(ICONS_SOURCES # Support for Minecraft instances and launch set(MINECRAFT_SOURCES # Minecraft support - minecraft/auth/AccountData.h minecraft/auth/AccountData.cpp - minecraft/auth/AccountTask.h + minecraft/auth/AccountData.h + minecraft/auth/AccountList.cpp + minecraft/auth/AccountList.h minecraft/auth/AccountTask.cpp - minecraft/auth/AuthSession.h + minecraft/auth/AccountTask.h + minecraft/auth/AuthRequest.cpp + minecraft/auth/AuthRequest.h minecraft/auth/AuthSession.cpp - minecraft/auth/AccountList.h - minecraft/auth/AccountList.cpp - minecraft/auth/MinecraftAccount.h + minecraft/auth/AuthSession.h + minecraft/auth/AuthStep.cpp + minecraft/auth/AuthStep.h minecraft/auth/MinecraftAccount.cpp - minecraft/auth/flows/AuthContext.h - minecraft/auth/flows/AuthContext.cpp - minecraft/auth/flows/AuthRequest.h - minecraft/auth/flows/AuthRequest.cpp - - minecraft/auth/flows/MSAInteractive.h - minecraft/auth/flows/MSAInteractive.cpp - minecraft/auth/flows/MSASilent.h - minecraft/auth/flows/MSASilent.cpp - - minecraft/auth/flows/MojangLogin.h - minecraft/auth/flows/MojangLogin.cpp - minecraft/auth/flows/MojangRefresh.h - minecraft/auth/flows/MojangRefresh.cpp - - minecraft/auth/flows/Yggdrasil.h - minecraft/auth/flows/Yggdrasil.cpp - - minecraft/auth/flows/Parsers.h - minecraft/auth/flows/Parsers.cpp + minecraft/auth/MinecraftAccount.h + minecraft/auth/Parsers.cpp + minecraft/auth/Parsers.h + minecraft/auth/Yggdrasil.cpp + minecraft/auth/Yggdrasil.h + + minecraft/auth/flows/AuthFlow.cpp + minecraft/auth/flows/AuthFlow.h + minecraft/auth/flows/Mojang.cpp + minecraft/auth/flows/Mojang.h + minecraft/auth/flows/MSA.cpp + minecraft/auth/flows/MSA.h + + minecraft/auth/steps/EntitlementsStep.cpp + minecraft/auth/steps/EntitlementsStep.h + minecraft/auth/steps/GetSkinStep.cpp + minecraft/auth/steps/GetSkinStep.h + minecraft/auth/steps/LauncherLoginStep.cpp + minecraft/auth/steps/LauncherLoginStep.h + minecraft/auth/steps/MigrationEligibilityStep.cpp + minecraft/auth/steps/MigrationEligibilityStep.h + minecraft/auth/steps/MinecraftProfileStep.cpp + minecraft/auth/steps/MinecraftProfileStep.h + minecraft/auth/steps/MSAStep.cpp + minecraft/auth/steps/MSAStep.h + minecraft/auth/steps/XboxAuthorizationStep.cpp + minecraft/auth/steps/XboxAuthorizationStep.h + minecraft/auth/steps/XboxProfileStep.cpp + minecraft/auth/steps/XboxProfileStep.h + minecraft/auth/steps/XboxUserStep.cpp + minecraft/auth/steps/XboxUserStep.h + minecraft/auth/steps/YggdrasilStep.cpp + minecraft/auth/steps/YggdrasilStep.h minecraft/gameoptions/GameOptions.h minecraft/gameoptions/GameOptions.cpp diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index 8bd5732f..39fec9e6 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -35,6 +35,8 @@ void LaunchController::executeTask() return; } + JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget); + login(); } @@ -90,8 +92,6 @@ void LaunchController::decideAccount() void LaunchController::login() { - JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget); - decideAccount(); // if no account is selected, we bail @@ -113,120 +113,107 @@ void LaunchController::login() { { m_session = std::make_shared<AuthSession>(); m_session->wants_online = m_online; - shared_qobject_ptr<AccountTask> task; - if(!password.isNull()) { - task = m_accountToUse->login(m_session, password); - } - else { - task = m_accountToUse->refresh(m_session); - } - if (task) - { - // We'll need to validate the access token to make sure the account - // is still logged in. - ProgressDialog progDialog(m_parentWidget); - if (m_online) - { - progDialog.setSkipButton(true, tr("Play Offline")); - } - progDialog.execWithTask(task.get()); - if (!task->wasSuccessful()) - { - auto failReasonNew = task->failReason(); - if(failReasonNew == "Invalid token." || failReasonNew == "Invalid Signature") - { - // account->invalidateClientToken(); - failReason = needLoginAgain; - } - else failReason = failReasonNew; - } - } - switch (m_session->status) - { - case AuthSession::Undetermined: { - qCritical() << "Received undetermined session status during login. Bye."; - tryagain = false; - emitFailed(tr("Received undetermined session status during login.")); - return; - } - case AuthSession::RequiresPassword: { - // FIXME: this needs to understand MSA - EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField); - auto username = m_session->username; - auto chopN = [](QString toChop, int N) -> QString - { - if(toChop.size() > N) - { - auto left = toChop.left(N); - left += QString("\u25CF").repeated(toChop.size() - N); - return left; - } - return toChop; - }; + m_accountToUse->fillSession(m_session); - if(username.contains('@')) - { - auto parts = username.split('@'); - auto mailbox = chopN(parts[0],3); - QString domain = chopN(parts[1], 3); - username = mailbox + '@' + domain; - } - passDialog.setUsername(username); - if (passDialog.exec() == QDialog::Accepted) + switch(m_accountToUse->accountState()) { + case AccountState::Offline: { + // we ask the user for a player name + bool ok = false; + QString usedname = m_session->player_name; + QString name = QInputDialog::getText( + m_parentWidget, + tr("Player name"), + tr("Choose your offline mode player name."), + QLineEdit::Normal, + m_session->player_name, + &ok + ); + if (!ok) { - password = passDialog.password(); + tryagain = false; + break; } - else + if (name.length()) { - tryagain = false; - emitFailed(tr("Received undetermined session status during login.")); + usedname = name; } - break; + m_session->MakeOffline(usedname); + // offline flavored game from here :3 + // NOTE: fallthrough is intentional } - case AuthSession::RequiresProfileSetup: { - auto entitlement = m_accountToUse->accountData()->minecraftEntitlement; - QString errorString; - if(!entitlement.canPlayMinecraft) { - errorString = tr("The account does not own Minecraft. You need to purchase the game first to play it."); - QMessageBox::warning( - nullptr, - tr("Missing Minecraft profile"), - errorString, - QMessageBox::StandardButton::Ok, - QMessageBox::StandardButton::Ok - ); - tryagain = false; - emitFailed(errorString); - return; + case AccountState::Online: { + if(m_accountToUse->ownsMinecraft() && !m_accountToUse->hasProfile()) { + auto entitlement = m_accountToUse->accountData()->minecraftEntitlement; + QString errorString; + if(!entitlement.canPlayMinecraft) { + errorString = tr("The account does not own Minecraft. You need to purchase the game first to play it."); + QMessageBox::warning( + nullptr, + tr("Missing Minecraft profile"), + errorString, + QMessageBox::StandardButton::Ok, + QMessageBox::StandardButton::Ok + ); + emitFailed(errorString); + return; + } + // Now handle setting up a profile name here... + ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); + if (dialog.exec() == QDialog::Accepted) + { + tryagain = true; + continue; + } + else + { + emitFailed(tr("Received undetermined session status during login.")); + return; + } } - // Now handle setting up a profile name here... - ProfileSetupDialog dialog(m_accountToUse, m_parentWidget); - if (dialog.exec() == QDialog::Accepted) - { - tryagain = true; - continue; + else { + launchInstance(); } - else + return; + } + case AccountState::Unchecked: { + m_accountToUse->refresh(); + // NOTE: fallthrough intentional + } + case AccountState::Working: { + // refresh is in progress, we need to wait for it to finish to proceed. + ProgressDialog progDialog(m_parentWidget); + if (m_online) { - tryagain = false; - emitFailed(tr("Received undetermined session status during login.")); - return; + progDialog.setSkipButton(true, tr("Play Offline")); } + auto task = m_accountToUse->currentTask(); + progDialog.execWithTask(task.get()); + continue; + } + // FIXME: this is missing - the meaning is that the account is queued for refresh and we should wait for that + /* + case AccountState::Queued: { + return; } - case AuthSession::RequiresOAuth: { - auto errorString = tr("Microsoft account has expired and needs to be logged into manually again."); + */ + case AccountState::Errored: { + // This means some sort of soft error that we can fix with a refresh ... so let's refresh. + // TODO: implement + return; + } + case AccountState::Expired: { + auto errorString = tr("The account has expired and needs to be logged into manually again."); QMessageBox::warning( m_parentWidget, - tr("Microsoft Account refresh failed"), + tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok ); - tryagain = false; emitFailed(errorString); return; } - case AuthSession::GoneOrMigrated: { + case AccountState::Gone: { auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to."); QMessageBox::warning( m_parentWidget, @@ -235,40 +222,9 @@ void LaunchController::login() { QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok ); - tryagain = false; emitFailed(errorString); return; } - case AuthSession::PlayableOffline: { - // we ask the user for a player name - bool ok = false; - QString usedname = m_session->player_name; - QString name = QInputDialog::getText( - m_parentWidget, - tr("Player name"), - tr("Choose your offline mode player name."), - QLineEdit::Normal, - m_session->player_name, - &ok - ); - if (!ok) - { - tryagain = false; - break; - } - if (name.length()) - { - usedname = name; - } - m_session->MakeOffline(usedname); - // offline flavored game from here :3 - } - case AuthSession::PlayableOnline: - { - launchInstance(); - tryagain = false; - return; - } } } emitFailed(tr("Failed to launch.")); @@ -334,14 +290,7 @@ void LaunchController::launchInstance() online_mode = "offline"; } - QString auth_server_status; - if(m_session->auth_server_online) { - auth_server_status = "online"; - } else { - auth_server_status = "offline"; - } - - m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\nAuthentication server is " + auth_server_status + "\n", MessageLevel::Launcher)); + m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher)); // Prepend Version m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher)); diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h index 09cd2c73..fa42747e 100644 --- a/launcher/minecraft/auth/AccountData.h +++ b/launcher/minecraft/auth/AccountData.h @@ -41,6 +41,16 @@ enum class AccountType { Mojang }; +enum class AccountState { + Unchecked, + Offline, + Working, + Online, + Errored, + Expired, + Gone +}; + struct AccountData { QJsonObject saveState() const; bool resumeStateFromV2(QJsonObject data); @@ -77,4 +87,9 @@ struct AccountData { MinecraftProfile minecraftProfile; MinecraftEntitlement minecraftEntitlement; Katabasis::Validity validity_ = Katabasis::Validity::None; + + // runtime only information (not saved with the account) + QString internalId; + QString errorString; + AccountState accountState = AccountState::Unchecked; }; diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp index d7537345..c44e3e89 100644 --- a/launcher/minecraft/auth/AccountList.cpp +++ b/launcher/minecraft/auth/AccountList.cpp @@ -15,6 +15,7 @@ #include "AccountList.h" #include "AccountData.h" +#include "AccountTask.h" #include <QIODevice> #include <QFile> @@ -24,6 +25,7 @@ #include <QJsonObject> #include <QJsonParseError> #include <QDir> +#include <QTimer> #include <QDebug> @@ -35,7 +37,14 @@ enum AccountListVersion { MojangMSA = 3 }; -AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { } +AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { + m_refreshTimer = new QTimer(this); + m_refreshTimer->setSingleShot(true); + connect(m_refreshTimer, &QTimer::timeout, this, &AccountList::fillQueue); + m_nextTimer = new QTimer(this); + m_nextTimer->setSingleShot(true); + connect(m_nextTimer, &QTimer::timeout, this, &AccountList::tryNext); +} AccountList::~AccountList() noexcept {} @@ -244,13 +253,29 @@ QVariant AccountList::data(const QModelIndex &index, int role) const } case StatusColumn: { - if(account->isActive()) { - return tr("Working", "Account status"); - } - if(account->isExpired()) { - return tr("Expired", "Account status"); + switch(account->accountState()) { + case AccountState::Unchecked: { + return tr("Unchecked", "Account status"); + } + case AccountState::Offline: { + return tr("Offline", "Account status"); + } + case AccountState |
