From 18d24678fe64e15008df0f7f89bc91864cb7ff22 Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Wed, 26 May 2021 14:28:59 +0300 Subject: [PATCH 1/6] Removes the existing "crack". Will be replaced by proper support of different account types. --- api/logic/minecraft/auth/AuthSession.cpp | 14 --- api/logic/minecraft/auth/AuthSession.h | 1 - application/LaunchController.cpp | 153 ++++++++++------------- 3 files changed, 66 insertions(+), 102 deletions(-) diff --git a/api/logic/minecraft/auth/AuthSession.cpp b/api/logic/minecraft/auth/AuthSession.cpp index b2ea62c8..35a4ccef 100644 --- a/api/logic/minecraft/auth/AuthSession.cpp +++ b/api/logic/minecraft/auth/AuthSession.cpp @@ -29,17 +29,3 @@ bool AuthSession::MakeOffline(QString offline_playername) status = PlayableOffline; return true; } - -bool AuthSession::MakeCracked(QString offline_playername) -{ - session = "-"; - // Filling session with dummy data - client_token = "ff64ff64ff64ff64ff64ff64ff64ff64"; - access_token = "ff64ff64ff64ff64ff64ff64ff64ff64"; - // TODO: Fetch actual UUID's from Mojang API so they match with real ones - uuid = QString(QCryptographicHash::hash(offline_playername.toLocal8Bit(), QCryptographicHash::Md5).toHex()); - - player_name = offline_playername; - status = PlayableOffline; - return true; -} diff --git a/api/logic/minecraft/auth/AuthSession.h b/api/logic/minecraft/auth/AuthSession.h index 02c7eb47..b397d9a1 100644 --- a/api/logic/minecraft/auth/AuthSession.h +++ b/api/logic/minecraft/auth/AuthSession.h @@ -17,7 +17,6 @@ struct User struct MULTIMC_LOGIC_EXPORT AuthSession { bool MakeOffline(QString offline_playername); - bool MakeCracked(QString offline_playername); QString serializeUserProperties(); diff --git a/application/LaunchController.cpp b/application/LaunchController.cpp index cbb9ca17..4113a0d3 100644 --- a/application/LaunchController.cpp +++ b/application/LaunchController.cpp @@ -36,27 +36,6 @@ void LaunchController::login() { JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget); - // Mojang account login bypass - bool ok = false; - QString usedname = "Player"; - QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), - tr("Choose your offline mode player name."), - QLineEdit::Normal, "Player", &ok); - if (!ok) - { - return; - } - if (name.length()) - { - usedname = name; - } - m_session = std::make_shared(); - m_session->MakeCracked(usedname); - - launchInstance(); - - // Original login code - /* // Find an account to use. std::shared_ptr accounts = MMC->accounts(); MojangAccountPtr account = accounts->activeAccount(); @@ -64,11 +43,11 @@ void LaunchController::login() { // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable( - m_parentWidget, tr("No Accounts"), - tr("In order to play Minecraft, you must have at least one Mojang or Minecraft " - "account logged in to MultiMC." - "Would you like to open the account manager to add an account now?"), - QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec(); + m_parentWidget, tr("No Accounts"), + tr("In order to play Minecraft, you must have at least one Mojang or Minecraft " + "account logged in to MultiMC." + "Would you like to open the account manager to add an account now?"), + QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec(); if (reply == QMessageBox::Yes) { @@ -135,75 +114,75 @@ void LaunchController::login() } 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.")); - break; - } - case AuthSession::RequiresPassword: - { - 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; - }; - - 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) - { - password = passDialog.password(); - } - else - { - tryagain = false; - } - break; - } - 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) + case AuthSession::Undetermined: { + qCritical() << "Received undetermined session status during login. Bye."; tryagain = false; + emitFailed(tr("Received undetermined session status during login.")); break; } - if (name.length()) + case AuthSession::RequiresPassword: { - usedname = name; + 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; + }; + + 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) + { + password = passDialog.password(); + } + else + { + tryagain = false; + } + break; + } + 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; } - m_session->MakeOffline(usedname); - // offline flavored game from here :3 - } - case AuthSession::PlayableOnline: - { - launchInstance(); - tryagain = false; - return; - } } } - emitFailed(tr("Failed to launch."));*/ + emitFailed(tr("Failed to launch.")); } void LaunchController::launchInstance() From 431af1cd6224c0de71f5bf7c59d5921836f9a4cd Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Sun, 30 May 2021 12:23:21 +0300 Subject: [PATCH 2/6] Initial support for alternative login types --- api/logic/minecraft/auth/MojangAccount.cpp | 12 ++++++++++++ api/logic/minecraft/auth/MojangAccount.h | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp index f5853fe3..2cf3a9eb 100644 --- a/api/logic/minecraft/auth/MojangAccount.cpp +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -38,6 +38,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) return nullptr; } + QString loginType = object.value("loginType").toString("mojang"); QString username = object.value("username").toString(""); QString clientToken = object.value("clientToken").toString(""); QString accessToken = object.value("accessToken").toString(""); @@ -82,6 +83,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) */ account->m_user = u; } + account->m_loginType = loginType; account->m_username = username; account->m_clientToken = clientToken; account->m_accessToken = accessToken; @@ -106,6 +108,7 @@ MojangAccountPtr MojangAccount::createFromUsername(const QString &username) QJsonObject MojangAccount::saveToJson() const { QJsonObject json; + json.insert("loginType", m_loginType); json.insert("username", m_username); json.insert("clientToken", m_clientToken); json.insert("accessToken", m_accessToken); @@ -174,6 +177,15 @@ std::shared_ptr MojangAccount::login(AuthSessionPtr session, QStr { Q_ASSERT(m_currentTask.get() == nullptr); + // Handling alternative account types + if (m_loginType == "dummy") + { + session->status = AuthSession::PlayableOffline; + session->auth_server_online = false; + fillSession(session); + return nullptr; + } + // take care of the true offline status if (accountStatus() == NotVerified && password.isEmpty()) { diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h index 30a5f2ff..1876e00c 100644 --- a/api/logic/minecraft/auth/MojangAccount.h +++ b/api/logic/minecraft/auth/MojangAccount.h @@ -99,6 +99,11 @@ public: /* manipulation */ void invalidateClientToken(); public: /* queries */ + const QString &loginType() const + { + return m_loginType; + } + const QString &username() const { return m_username; @@ -139,6 +144,11 @@ signals: // TODO: better signalling for the various possible state changes - especially errors protected: /* variables */ + // Authentication system used. + // Usable values: "mojang", "dummy" + QString m_loginType; + + // Username taken by account. QString m_username; // Used to identify the client - the user can have multiple clients for the same account From ffd5dd948c8ebcd0d79e28097d65aa409334d2b9 Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Sun, 30 May 2021 16:03:22 +0300 Subject: [PATCH 3/6] Launching the game now possible. Feature parity with master branch --- api/logic/minecraft/auth/MojangAccount.cpp | 22 ++++++++++++++++++---- api/logic/minecraft/auth/MojangAccount.h | 6 ++++++ application/dialogs/LoginDialog.cpp | 8 +++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp index 2cf3a9eb..7e47e352 100644 --- a/api/logic/minecraft/auth/MojangAccount.cpp +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -145,6 +145,17 @@ QJsonObject MojangAccount::saveToJson() const return json; } +bool MojangAccount::setLoginType(const QString &loginType) +{ + // TODO: Implement a cleaner validity check + if (loginType == "mojang" or loginType == "dummy") + { + m_loginType = loginType; + return true; + } + return false; +} + bool MojangAccount::setCurrentProfile(const QString &profileId) { for (int i = 0; i < m_profiles.length(); i++) @@ -180,9 +191,12 @@ std::shared_ptr MojangAccount::login(AuthSessionPtr session, QStr // Handling alternative account types if (m_loginType == "dummy") { - session->status = AuthSession::PlayableOffline; - session->auth_server_online = false; - fillSession(session); + if (session) + { + session->status = AuthSession::PlayableOnline; + session->auth_server_online = false; + fillSession(session); + } return nullptr; } @@ -292,7 +306,7 @@ void MojangAccount::fillSession(AuthSessionPtr session) } else { - session->player_name = "Player"; + session->player_name = m_username; session->session = "-"; } session->u = user(); diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h index 1876e00c..96a9f46b 100644 --- a/api/logic/minecraft/auth/MojangAccount.h +++ b/api/logic/minecraft/auth/MojangAccount.h @@ -85,6 +85,12 @@ public: /* construction */ public: /* manipulation */ /** + * Overrides the login type on the account. + * Accepts "mojang" and "dummy". Returns false if other. + */ + bool setLoginType(const QString &loginType); + + /** * Sets the currently selected profile to the profile with the given ID string. * If profileId is not in the list of available profiles, the function will simply return * false. diff --git a/application/dialogs/LoginDialog.cpp b/application/dialogs/LoginDialog.cpp index 32f8a48f..74eb90e7 100644 --- a/application/dialogs/LoginDialog.cpp +++ b/application/dialogs/LoginDialog.cpp @@ -43,13 +43,19 @@ void LoginDialog::accept() // Setup the login task and start it m_account = MojangAccount::createFromUsername(ui->userTextBox->text()); + m_account->setLoginType("dummy"); // TODO: Add the login type selector m_loginTask = m_account->login(nullptr, ui->passTextBox->text()); connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed); connect(m_loginTask.get(), &Task::succeeded, this, &LoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus); connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress); - m_loginTask->start(); + if (true) + { + onTaskSucceeded(); + } else { + m_loginTask->start(); + } } void LoginDialog::setUserInputsEnabled(bool enable) From 0e436b45f2e8948e81818213c8f30ef33b8d95b9 Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Sun, 30 May 2021 16:52:53 +0300 Subject: [PATCH 4/6] Dummy profile support as a workaround for accounts.json not loading --- api/logic/minecraft/auth/MojangAccount.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp index 7e47e352..9df4606c 100644 --- a/api/logic/minecraft/auth/MojangAccount.cpp +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -195,6 +195,15 @@ std::shared_ptr MojangAccount::login(AuthSessionPtr session, QStr { session->status = AuthSession::PlayableOnline; session->auth_server_online = false; + if (!currentProfile()) + { + // TODO: Proper profile support (idk how) + auto dummyProfile = AccountProfile(); + dummyProfile.name = m_username; + dummyProfile.id = "-"; + m_profiles.append(dummyProfile); + m_currentProfile = 0; + } fillSession(session); } return nullptr; From 1c2348342564d3c9fdc98770bf78044fc9546906 Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Sun, 30 May 2021 17:08:55 +0300 Subject: [PATCH 5/6] Profiles of both types now can be created. Ready for review --- application/dialogs/LoginDialog.cpp | 7 +++++-- application/dialogs/LoginDialog.ui | 26 +++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/application/dialogs/LoginDialog.cpp b/application/dialogs/LoginDialog.cpp index 74eb90e7..dc1fe843 100644 --- a/application/dialogs/LoginDialog.cpp +++ b/application/dialogs/LoginDialog.cpp @@ -43,14 +43,17 @@ void LoginDialog::accept() // Setup the login task and start it m_account = MojangAccount::createFromUsername(ui->userTextBox->text()); - m_account->setLoginType("dummy"); // TODO: Add the login type selector + if (ui->radioMojang->isChecked()) + m_account->setLoginType("mojang"); + else if (ui->radioDummy->isChecked()) + m_account->setLoginType("dummy"); m_loginTask = m_account->login(nullptr, ui->passTextBox->text()); connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed); connect(m_loginTask.get(), &Task::succeeded, this, &LoginDialog::onTaskSucceeded); connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus); connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress); - if (true) + if (!m_loginTask) { onTaskSucceeded(); } else { diff --git a/application/dialogs/LoginDialog.ui b/application/dialogs/LoginDialog.ui index d92fbae3..9ba5f80d 100644 --- a/application/dialogs/LoginDialog.ui +++ b/application/dialogs/LoginDialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 162 + 219 @@ -60,6 +60,30 @@ + + + + + + Mojang / Minecraft + + + true + + + + + + + Offline (cracked) + + + false + + + + + From 356905aa75f2decd2d3ba0e1d800db10f892935e Mon Sep 17 00:00:00 2001 From: Zakhar Afonin Date: Sun, 30 May 2021 17:22:15 +0300 Subject: [PATCH 6/6] Fixed: a dummy profile now gets created regardless of game being launched once --- api/logic/minecraft/auth/MojangAccount.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp index 9df4606c..0774b9a2 100644 --- a/api/logic/minecraft/auth/MojangAccount.cpp +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -195,17 +195,17 @@ std::shared_ptr MojangAccount::login(AuthSessionPtr session, QStr { session->status = AuthSession::PlayableOnline; session->auth_server_online = false; - if (!currentProfile()) - { - // TODO: Proper profile support (idk how) - auto dummyProfile = AccountProfile(); - dummyProfile.name = m_username; - dummyProfile.id = "-"; - m_profiles.append(dummyProfile); - m_currentProfile = 0; - } fillSession(session); } + if (!currentProfile()) + { + // TODO: Proper profile support (idk how) + auto dummyProfile = AccountProfile(); + dummyProfile.name = m_username; + dummyProfile.id = "-"; + m_profiles.append(dummyProfile); + m_currentProfile = 0; + } return nullptr; }