Offline mode can be used even when online.
Allow the user to pick a player name for offline mode. Big auth refactor. Now using session objects instead of the accounts themselves. Sessions only last for one instance start and hold all the auth and player data.
This commit is contained in:
parent
d7113de3bd
commit
ffbc5bb62c
@ -365,6 +365,8 @@ logic/net/PasteUpload.cpp
|
||||
logic/net/URLConstants.h
|
||||
|
||||
# Yggdrasil login stuff
|
||||
logic/auth/AuthSession.h
|
||||
logic/auth/AuthSession.cpp
|
||||
logic/auth/MojangAccountList.h
|
||||
logic/auth/MojangAccountList.cpp
|
||||
logic/auth/MojangAccount.h
|
||||
|
@ -71,7 +71,6 @@
|
||||
|
||||
#include "logic/auth/flows/AuthenticateTask.h"
|
||||
#include "logic/auth/flows/RefreshTask.h"
|
||||
#include "logic/auth/flows/ValidateTask.h"
|
||||
|
||||
#include "logic/updater/DownloadUpdateTask.h"
|
||||
|
||||
@ -132,8 +131,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
newsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
|
||||
QObject::connect(newsLabel, &QAbstractButton::clicked, this, &MainWindow::newsButtonClicked);
|
||||
QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this, &MainWindow::updateNewsLabel);
|
||||
QObject::connect(newsLabel, &QAbstractButton::clicked, this,
|
||||
&MainWindow::newsButtonClicked);
|
||||
QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this,
|
||||
&MainWindow::updateNewsLabel);
|
||||
updateNewsLabel();
|
||||
}
|
||||
|
||||
@ -173,8 +174,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
view->setModel(proxymodel);
|
||||
|
||||
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(view, SIGNAL(customContextMenuRequested(const QPoint&)),
|
||||
this, SLOT(showInstanceContextMenu(const QPoint&)));
|
||||
connect(view, SIGNAL(customContextMenuRequested(const QPoint &)), this,
|
||||
SLOT(showInstanceContextMenu(const QPoint &)));
|
||||
|
||||
ui->horizontalLayout->addWidget(view);
|
||||
}
|
||||
@ -213,8 +214,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
|
||||
// Start status checker
|
||||
{
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this, &MainWindow::updateStatusUI);
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this, &MainWindow::updateStatusFailedUI);
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this,
|
||||
&MainWindow::updateStatusUI);
|
||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this,
|
||||
&MainWindow::updateStatusFailedUI);
|
||||
|
||||
connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
|
||||
connect(&statusTimer, &QTimer::timeout, this, &MainWindow::reloadStatus);
|
||||
@ -313,8 +316,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
if (MMC->settings()->get("AutoUpdate").toBool())
|
||||
on_actionCheckUpdate_triggered();
|
||||
|
||||
connect(MMC->notificationChecker().get(), &NotificationChecker::notificationCheckFinished,
|
||||
this, &MainWindow::notificationsChanged);
|
||||
connect(MMC->notificationChecker().get(),
|
||||
&NotificationChecker::notificationCheckFinished, this,
|
||||
&MainWindow::notificationsChanged);
|
||||
}
|
||||
|
||||
setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
|
||||
@ -330,9 +334,9 @@ MainWindow::~MainWindow()
|
||||
delete drawer;
|
||||
}
|
||||
|
||||
void MainWindow::showInstanceContextMenu(const QPoint& pos)
|
||||
void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
||||
{
|
||||
if(!view->indexAt(pos).isValid())
|
||||
if (!view->indexAt(pos).isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -522,9 +526,12 @@ static QString convertStatus(const QString &status)
|
||||
{
|
||||
QString ret = "?";
|
||||
|
||||
if(status == "green") ret = "↑";
|
||||
else if(status == "yellow") ret = "-";
|
||||
else if(status == "red") ret="↓";
|
||||
if (status == "green")
|
||||
ret = "↑";
|
||||
else if (status == "yellow")
|
||||
ret = "-";
|
||||
else if (status == "red")
|
||||
ret = "↓";
|
||||
|
||||
return "<span style=\"font-size:11pt; font-weight:600;\">" + ret + "</span>";
|
||||
}
|
||||
@ -533,7 +540,7 @@ void MainWindow::reloadStatus()
|
||||
{
|
||||
m_statusRefresh->setChecked(true);
|
||||
MMC->statusChecker()->reloadStatus();
|
||||
//updateStatusUI();
|
||||
// updateStatusUI();
|
||||
}
|
||||
|
||||
static QString makeStatusString(const QMap<QString, QString> statuses)
|
||||
@ -632,7 +639,8 @@ void MainWindow::notificationsChanged()
|
||||
}
|
||||
|
||||
QMessageBox box(icon, tr("Notification"), entry.message, QMessageBox::Close, this);
|
||||
QPushButton *dontShowAgainButton = box.addButton(tr("Don't show again"), QMessageBox::AcceptRole);
|
||||
QPushButton *dontShowAgainButton =
|
||||
box.addButton(tr("Don't show again"), QMessageBox::AcceptRole);
|
||||
box.setDefaultButton(QMessageBox::Close);
|
||||
box.exec();
|
||||
if (box.clickedButton() == dontShowAgainButton)
|
||||
@ -657,9 +665,9 @@ void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit
|
||||
if (updateDlg.exec(&updateTask))
|
||||
{
|
||||
UpdateFlags baseFlags = None;
|
||||
#ifdef MultiMC_UPDATER_DRY_RUN
|
||||
baseFlags |= DryRun;
|
||||
#endif
|
||||
#ifdef MultiMC_UPDATER_DRY_RUN
|
||||
baseFlags |= DryRun;
|
||||
#endif
|
||||
if (installOnExit)
|
||||
MMC->installUpdates(updateTask.updateFilesDir(), baseFlags | OnExit);
|
||||
else
|
||||
@ -751,7 +759,7 @@ void MainWindow::on_actionAddInstance_triggered()
|
||||
if (MMC->accounts()->anyAccountIsValid())
|
||||
{
|
||||
ProgressDialog loadDialog(this);
|
||||
auto update = newInstance->doUpdate(false);
|
||||
auto update = newInstance->doUpdate();
|
||||
connect(update.get(), &Task::failed, [this](QString reason)
|
||||
{
|
||||
QString error = QString("Instance load failed: %1").arg(reason);
|
||||
@ -837,7 +845,7 @@ void MainWindow::on_actionChangeInstIcon_triggered()
|
||||
|
||||
void MainWindow::iconUpdated(QString icon)
|
||||
{
|
||||
if(icon == m_currentInstIcon)
|
||||
if (icon == m_currentInstIcon)
|
||||
{
|
||||
ui->actionChangeInstIcon->setIcon(MMC->icons()->getBigIcon(m_currentInstIcon));
|
||||
}
|
||||
@ -860,7 +868,8 @@ void MainWindow::setSelectedInstanceById(const QString &id)
|
||||
selectionIndex = proxymodel->mapFromSource(index);
|
||||
}
|
||||
}
|
||||
view->selectionModel()->setCurrentIndex(selectionIndex, QItemSelectionModel::ClearAndSelect);
|
||||
view->selectionModel()->setCurrentIndex(selectionIndex,
|
||||
QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionChangeInstGroup_triggered()
|
||||
@ -1060,7 +1069,16 @@ void MainWindow::on_actionLaunchInstance_triggered()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::doLaunch()
|
||||
void MainWindow::on_actionLaunchInstanceOffline_triggered()
|
||||
{
|
||||
if (m_selectedInstance)
|
||||
{
|
||||
NagUtils::checkJVMArgs(m_selectedInstance->settings().get("JvmArgs").toString(), this);
|
||||
doLaunch(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::doLaunch(bool online)
|
||||
{
|
||||
if (!m_selectedInstance)
|
||||
return;
|
||||
@ -1104,89 +1122,111 @@ void MainWindow::doLaunch()
|
||||
if (!account.get())
|
||||
return;
|
||||
|
||||
// we try empty password first :)
|
||||
QString password;
|
||||
// we loop until the user succeeds in logging in or gives up
|
||||
bool tryagain = true;
|
||||
// the failure. the default failure.
|
||||
QString failReason = tr("Your account is currently not logged in. Please enter "
|
||||
"your password to log in again.");
|
||||
// do the login. if the account has an access token, try to refresh it first.
|
||||
if (account->accountStatus() != NotVerified)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account is still logged in.
|
||||
ProgressDialog progDialog(this);
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
auto task = account->login();
|
||||
progDialog.exec(task.get());
|
||||
|
||||
auto status = account->accountStatus();
|
||||
if (status != NotVerified)
|
||||
{
|
||||
updateInstance(m_selectedInstance, account);
|
||||
}
|
||||
else
|
||||
while (tryagain)
|
||||
{
|
||||
AuthSessionPtr session(new AuthSession());
|
||||
session->wants_online = online;
|
||||
auto task = account->login(session, password);
|
||||
if (task)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account
|
||||
// is still logged in.
|
||||
ProgressDialog progDialog(this);
|
||||
if (online)
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
progDialog.exec(task.get());
|
||||
if (!task->successful())
|
||||
{
|
||||
failReason = task->failReason();
|
||||
}
|
||||
if (loginWithPassword(account, failReason))
|
||||
updateInstance(m_selectedInstance, account);
|
||||
}
|
||||
// in any case, revert from online to verified.
|
||||
account->downgrade();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loginWithPassword(account, failReason))
|
||||
switch (session->status)
|
||||
{
|
||||
updateInstance(m_selectedInstance, account);
|
||||
account->downgrade();
|
||||
case AuthSession::Undetermined:
|
||||
{
|
||||
QLOG_ERROR() << "Received undetermined session status during login. Bye.";
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
case AuthSession::RequiresPassword:
|
||||
{
|
||||
EditAccountDialog passDialog(failReason, this, EditAccountDialog::PasswordField);
|
||||
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 = session->player_name;
|
||||
QString name = QInputDialog::getText(this, tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal, session->player_name, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length())
|
||||
{
|
||||
usedname = name;
|
||||
}
|
||||
session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
case AuthSession::PlayableOnline:
|
||||
{
|
||||
// update first if the server actually responded
|
||||
if (session->auth_server_online)
|
||||
{
|
||||
updateInstance(m_selectedInstance, session);
|
||||
}
|
||||
else
|
||||
{
|
||||
launchInstance(m_selectedInstance, session);
|
||||
}
|
||||
tryagain = false;
|
||||
}
|
||||
}
|
||||
// in any case, revert from online to verified.
|
||||
account->downgrade();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
|
||||
void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session)
|
||||
{
|
||||
EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
// To refresh the token, we just create an authenticate task with the given account and
|
||||
// the user's password.
|
||||
ProgressDialog progDialog(this);
|
||||
auto task = account->login(passDialog.password());
|
||||
progDialog.exec(task.get());
|
||||
if (task->successful())
|
||||
return true;
|
||||
else
|
||||
{
|
||||
// If the authentication task failed, recurse with the task's error message.
|
||||
return loginWithPassword(account, task->failReason());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::updateInstance(BaseInstance *instance, MojangAccountPtr account)
|
||||
{
|
||||
bool only_prepare = account->accountStatus() != Online;
|
||||
auto updateTask = instance->doUpdate(only_prepare);
|
||||
auto updateTask = instance->doUpdate();
|
||||
if (!updateTask)
|
||||
{
|
||||
launchInstance(instance, account);
|
||||
launchInstance(instance, session);
|
||||
return;
|
||||
}
|
||||
ProgressDialog tDialog(this);
|
||||
connect(updateTask.get(), &Task::succeeded, [this, instance, account]
|
||||
{ launchInstance(instance, account); });
|
||||
connect(updateTask.get(), &Task::succeeded, [this, instance, session]
|
||||
{ launchInstance(instance, session); });
|
||||
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
||||
tDialog.exec(updateTask.get());
|
||||
}
|
||||
|
||||
void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account)
|
||||
void MainWindow::launchInstance(BaseInstance *instance, AuthSessionPtr session)
|
||||
{
|
||||
Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL");
|
||||
Q_ASSERT_X(account.get() != nullptr, "launchInstance", "account is NULL");
|
||||
Q_ASSERT_X(session.get() != nullptr, "launchInstance", "session is NULL");
|
||||
|
||||
proc = instance->prepareForLaunch(account);
|
||||
proc = instance->prepareForLaunch(session);
|
||||
if (!proc)
|
||||
return;
|
||||
|
||||
@ -1195,7 +1235,7 @@ void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account
|
||||
console = new ConsoleWindow(proc);
|
||||
connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
|
||||
|
||||
proc->setLogin(account);
|
||||
proc->setLogin(session);
|
||||
proc->launch();
|
||||
}
|
||||
|
||||
@ -1258,7 +1298,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
||||
VersionSelectDialog vselect(m_selectedInstance->versionList().get(),
|
||||
tr("Change Minecraft version"), this);
|
||||
vselect.setFilter(1, "OneSix");
|
||||
if(!vselect.exec() || !vselect.selectedVersion())
|
||||
if (!vselect.exec() || !vselect.selectedVersion())
|
||||
return;
|
||||
|
||||
if (!MMC->accounts()->anyAccountIsValid())
|
||||
@ -1276,7 +1316,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
||||
auto result = CustomMessageBox::selectable(
|
||||
this, tr("Are you sure?"),
|
||||
tr("This will remove any library/version customization you did previously. "
|
||||
"This includes things like Forge install and similar."),
|
||||
"This includes things like Forge install and similar."),
|
||||
QMessageBox::Warning, QMessageBox::Ok | QMessageBox::Abort,
|
||||
QMessageBox::Abort)->exec();
|
||||
|
||||
@ -1285,7 +1325,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
||||
}
|
||||
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
||||
|
||||
auto updateTask = m_selectedInstance->doUpdate(false);
|
||||
auto updateTask = m_selectedInstance->doUpdate();
|
||||
if (!updateTask)
|
||||
{
|
||||
return;
|
||||
@ -1384,7 +1424,7 @@ void MainWindow::instanceEnded()
|
||||
void MainWindow::checkMigrateLegacyAssets()
|
||||
{
|
||||
int legacyAssets = AssetsUtils::findLegacyAssets();
|
||||
if(legacyAssets > 0)
|
||||
if (legacyAssets > 0)
|
||||
{
|
||||
ProgressDialog migrateDlg(this);
|
||||
AssetsMigrateTask migrateTask(legacyAssets, &migrateDlg);
|
||||
|
@ -96,6 +96,8 @@ slots:
|
||||
|
||||
void on_actionLaunchInstance_triggered();
|
||||
|
||||
void on_actionLaunchInstanceOffline_triggered();
|
||||
|
||||
void on_actionDeleteInstance_triggered();
|
||||
|
||||
void on_actionRenameInstance_triggered();
|
||||
@ -112,25 +114,18 @@ slots:
|
||||
* Launches the currently selected instance with the default account.
|
||||
* If no default account is selected, prompts the user to pick an account.
|
||||
*/
|
||||
void doLaunch();
|
||||
|
||||
/*!
|
||||
* Opens an input dialog, allowing the user to input their password and refresh its access token.
|
||||
* This function will execute the proper Yggdrasil task to refresh the access token.
|
||||
* Returns true if successful. False if the user cancelled.
|
||||
*/
|
||||
bool loginWithPassword(MojangAccountPtr account, const QString& errorMsg="");
|
||||
void doLaunch(bool online = true);
|
||||
|
||||
/*!
|
||||
* Launches the given instance with the given account.
|
||||
* This function assumes that the given account has a valid, usable access token.
|
||||
*/
|
||||
void launchInstance(BaseInstance* instance, MojangAccountPtr account);
|
||||
void launchInstance(BaseInstance *instance, AuthSessionPtr session);
|
||||
|
||||
/*!
|
||||
* Prepares the given instance for launch with the given account.
|
||||
*/
|
||||
void updateInstance(BaseInstance* instance, MojangAccountPtr account);
|
||||
void updateInstance(BaseInstance *instance, AuthSessionPtr account);
|
||||
|
||||
void onGameUpdateError(QString error);
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>688</width>
|
||||
<height>460</height>
|
||||
<width>694</width>
|
||||
<height>563</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -107,6 +107,7 @@
|
||||
</attribute>
|
||||
<addaction name="actionChangeInstIcon"/>
|
||||
<addaction name="actionLaunchInstance"/>
|
||||
<addaction name="actionLaunchInstanceOffline"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEditInstNotes"/>
|
||||
<addaction name="actionChangeInstGroup"/>
|
||||
@ -152,7 +153,9 @@
|
||||
</widget>
|
||||
<action name="actionAddInstance">
|
||||
<property name="icon">
|
||||
<iconset theme="new"/>
|
||||
<iconset theme="new">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Instance</string>
|
||||
@ -166,7 +169,9 @@
|
||||
</action>
|
||||
<action name="actionViewInstanceFolder">
|
||||
<property name="icon">
|
||||
<iconset theme="viewfolder"/>
|
||||
<iconset theme="viewfolder">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Instance Folder</string>
|
||||
@ -180,7 +185,9 @@
|
||||
</action>
|
||||
<action name="actionRefresh">
|
||||
<property name="icon">
|
||||
<iconset theme="refresh"/>
|
||||
<iconset theme="refresh">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
@ -194,7 +201,9 @@
|
||||
</action>
|
||||
<action name="actionViewCentralModsFolder">
|
||||
<property name="icon">
|
||||
<iconset theme="centralmods"/>
|
||||
<iconset theme="centralmods">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Central Mods Folder</string>
|
||||
@ -208,7 +217,9 @@
|
||||
</action>
|
||||
<action name="actionCheckUpdate">
|
||||
<property name="icon">
|
||||
<iconset theme="checkupdate"/>
|
||||
<iconset theme="checkupdate">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for Updates</string>
|
||||
@ -222,7 +233,9 @@
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="icon">
|
||||
<iconset theme="settings"/>
|
||||
<iconset theme="settings">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
@ -239,7 +252,9 @@
|
||||
</action>
|
||||
<action name="actionReportBug">
|
||||
<property name="icon">
|
||||
<iconset theme="bug"/>
|
||||
<iconset theme="bug">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report a Bug</string>
|
||||
@ -253,7 +268,9 @@
|
||||
</action>
|
||||
<action name="actionMoreNews">
|
||||
<property name="icon">
|
||||
<iconset theme="news"/>
|
||||
<iconset theme="news">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>More News</string>
|
||||
@ -270,7 +287,9 @@
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="icon">
|
||||
<iconset theme="about"/>
|
||||
<iconset theme="about">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About MultiMC</string>
|
||||
@ -463,7 +482,9 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="cat"/>
|
||||
<iconset theme="cat">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Meow</string>
|
||||
@ -474,7 +495,9 @@
|
||||
</action>
|
||||
<action name="actionCopyInstance">
|
||||
<property name="icon">
|
||||
<iconset theme="copy"/>
|
||||
<iconset theme="copy">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy Instance</string>
|
||||
@ -494,6 +517,17 @@
|
||||
<string>Manage your Mojang or Minecraft accounts.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLaunchInstanceOffline">
|
||||
<property name="text">
|
||||
<string>Play Offline</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Launch the selected instance in offline mode.</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Launch the selected instance.</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
@ -126,7 +126,7 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
||||
|
||||
MojangAccountPtr account = MojangAccount::createFromUsername(username);
|
||||
ProgressDialog progDialog(this);
|
||||
auto task = account->login(password);
|
||||
auto task = account->login(nullptr, password);
|
||||
progDialog.exec(task.get());
|
||||
if(task->successful())
|
||||
{
|
||||
|
@ -155,10 +155,10 @@ public:
|
||||
virtual SettingsObject &settings() const;
|
||||
|
||||
/// returns a valid update task
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) = 0;
|
||||
virtual std::shared_ptr<Task> doUpdate() = 0;
|
||||
|
||||
/// returns a valid minecraft process, ready for launch with the given account.
|
||||
virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0;
|
||||
virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr account) = 0;
|
||||
|
||||
/// do any necessary cleanups after the instance finishes. also runs before
|
||||
/// 'prepareForLaunch'
|
||||
|
@ -42,15 +42,15 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
|
||||
settings->registerSetting("IntendedJarVersion", "");
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> LegacyInstance::doUpdate(bool only_prepare)
|
||||
std::shared_ptr<Task> LegacyInstance::doUpdate()
|
||||
{
|
||||
// make sure the jar mods list is initialized by asking for it.
|
||||
auto list = jarModList();
|
||||
// create an update task
|
||||
return std::shared_ptr<Task>(new LegacyUpdate(this, only_prepare, this));
|
||||
return std::shared_ptr<Task>(new LegacyUpdate(this, this));
|
||||
}
|
||||
|
||||
MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
MinecraftProcess *LegacyInstance::prepareForLaunch(AuthSessionPtr account)
|
||||
{
|
||||
MinecraftProcess *proc = new MinecraftProcess(this);
|
||||
|
||||
@ -66,13 +66,14 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
if (settings().get("LaunchMaximized").toBool())
|
||||
windowParams = "max";
|
||||
else
|
||||
windowParams = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg(
|
||||
settings().get("MinecraftWinHeight").toInt());
|
||||
windowParams = QString("%1x%2")
|
||||
.arg(settings().get("MinecraftWinWidth").toInt())
|
||||
.arg(settings().get("MinecraftWinHeight").toInt());
|
||||
|
||||
QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
|
||||
.absolutePath();
|
||||
launchScript += "userName " + account->currentProfile()->name + "\n";
|
||||
launchScript += "sessionId " + account->sessionId() + "\n";
|
||||
launchScript += "userName " + account->player_name + "\n";
|
||||
launchScript += "sessionId " + account->session + "\n";
|
||||
launchScript += "windowTitle " + windowTitle() + "\n";
|
||||
launchScript += "windowParams " + windowParams + "\n";
|
||||
launchScript += "lwjgl " + lwjgl + "\n";
|
||||
|
@ -76,9 +76,9 @@ public:
|
||||
|
||||
virtual bool shouldUpdate() const override;
|
||||
virtual void setShouldUpdate(bool val) override;
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
|
||||
virtual std::shared_ptr<Task> doUpdate() override;
|
||||
|
||||
virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override;
|
||||
virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr account) override;
|
||||
virtual void cleanupAfterRun() override;
|
||||
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
||||
|
||||
|
@ -27,13 +27,13 @@
|
||||
#include "logger/QsLog.h"
|
||||
#include "logic/net/URLConstants.h"
|
||||
|
||||
LegacyUpdate::LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent)
|
||||
: Task(parent), m_inst(inst), m_only_prepare(only_prepare)
|
||||
LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
|
||||
{
|
||||
}
|
||||
|
||||
void LegacyUpdate::executeTask()
|
||||
{
|
||||
/*
|
||||
if(m_only_prepare)
|
||||
{
|
||||
// FIXME: think this through some more.
|
||||
@ -49,8 +49,9 @@ void LegacyUpdate::executeTask()
|
||||
}
|
||||
else
|
||||
{
|
||||
lwjglStart();
|
||||
}
|
||||
*/
|
||||
lwjglStart();
|
||||
//}
|
||||
}
|
||||
|
||||
void LegacyUpdate::lwjglStart()
|
||||
@ -268,7 +269,6 @@ void LegacyUpdate::jarStart()
|
||||
|
||||
auto dljob = new NetJob("Minecraft.jar for version " + version_id);
|
||||
|
||||
|
||||
auto metacache = MMC->metacache();
|
||||
auto entry = metacache->resolveEntry("versions", localPath);
|
||||
dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
|
||||
@ -425,7 +425,7 @@ void LegacyUpdate::ModTheJar()
|
||||
auto &mod = modList->operator[](i);
|
||||
|
||||
// do not merge disabled mods.
|
||||
if(!mod.enabled())
|
||||
if (!mod.enabled())
|
||||
continue;
|
||||
|
||||
if (mod.type() == Mod::MOD_ZIPFILE)
|
||||
|
@ -31,7 +31,7 @@ class LegacyUpdate : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent = 0);
|
||||
explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0);
|
||||
virtual void executeTask();
|
||||
|
||||
private
|
||||
@ -72,5 +72,4 @@ private:
|
||||
private:
|
||||
NetJobPtr legacyDownloadJob;
|
||||
BaseInstance *m_inst = nullptr;
|
||||
bool m_only_prepare = false;
|
||||
};
|
||||
|
@ -79,26 +79,18 @@ void MinecraftProcess::setWorkdir(QString path)
|
||||
|
||||
QString MinecraftProcess::censorPrivateInfo(QString in)
|
||||
{
|
||||
if(!m_account)
|
||||
if(!m_session)
|
||||
return in;
|
||||
|
||||
QString sessionId = m_account->sessionId();
|
||||
QString accessToken = m_account->accessToken();
|
||||
QString clientToken = m_account->clientToken();
|
||||
in.replace(sessionId, "<SESSION ID>");
|
||||
in.replace(accessToken, "<ACCESS TOKEN>");
|
||||
in.replace(clientToken, "<CLIENT TOKEN>");
|
||||
auto profile = m_account->currentProfile();
|
||||
if(profile)
|
||||
{
|
||||
QString profileId = profile->id;
|
||||
QString profileName = profile->name;
|
||||
in.replace(profileId, "<PROFILE ID>");
|
||||
in.replace(profileName, "<PROFILE NAME>");
|
||||
}
|
||||
if(m_session->session != "-")
|
||||
in.replace(m_session->session, "<SESSION ID>");
|
||||
in.replace(m_session->access_token, "<ACCESS TOKEN>");
|
||||
in.replace(m_session->client_token, "<CLIENT TOKEN>");
|
||||
in.replace(m_session->uuid, "<PROFILE ID>");
|
||||
in.replace(m_session->player_name, "<PROFILE NAME>");
|
||||
|
||||
auto i = m_account->user().properties.begin();
|
||||
while (i != m_account->user().properties.end())
|
||||
auto i = m_session->u.properties.begin();
|
||||
while (i != m_session->u.properties.end())
|
||||
{
|
||||
in.replace(i.value(), "<" + i.key().toUpper() + ">");
|
||||
++i;
|
||||
|
@ -78,9 +78,9 @@ public:
|
||||
|
||||
void killMinecraft();
|
||||
|
||||
inline void setLogin(MojangAccountPtr account)
|
||||
inline void setLogin(AuthSessionPtr session)
|
||||
{
|
||||
m_account = account;
|
||||
m_session = session;
|
||||
}
|
||||
|
||||
signals:
|
||||
@ -117,7 +117,7 @@ protected:
|
||||
QString m_out_leftover;
|
||||
QProcess m_prepostlaunchprocess;
|
||||
bool killed = false;
|
||||
MojangAccountPtr m_account;
|
||||
AuthSessionPtr m_session;
|
||||
QString launchScript;
|
||||
QString m_nativeFolder;
|
||||
|
||||
|
@ -106,7 +106,7 @@ bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> OneSixFTBInstance::doUpdate(bool only_prepare)
|
||||
std::shared_ptr<Task> OneSixFTBInstance::doUpdate()
|
||||
{
|
||||
std::shared_ptr<SequentialTask> task;
|
||||
task.reset(new SequentialTask(this));
|
||||
@ -114,11 +114,11 @@ std::shared_ptr<Task> OneSixFTBInstance::doUpdate(bool only_prepare)
|
||||
{
|
||||
task->addTask(std::shared_ptr<Task>(MMC->forgelist()->getLoadTask()));
|
||||
}
|
||||
task->addTask(OneSixInstance::doUpdate(only_prepare));
|
||||
task->addTask(OneSixInstance::doUpdate());
|
||||
task->addTask(std::shared_ptr<Task>(new OneSixFTBInstanceForge(m_forge->version(), this, this)));
|
||||
//FIXME: yes. this may appear dumb. but the previous step can change the list, so we do it all again.
|
||||
//TODO: Add a graph task. Construct graphs of tasks so we may capture the logic properly.
|
||||
task->addTask(OneSixInstance::doUpdate(only_prepare));
|
||||
task->addTask(OneSixInstance::doUpdate());
|
||||
return task;
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ public:
|
||||
virtual QString getStatusbarDescription();
|
||||
virtual bool menuActionEnabled(QString action_name) const;
|
||||
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
|
||||
virtual std::shared_ptr<Task> doUpdate() override;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
|
@ -41,9 +41,9 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o
|
||||
reloadFullVersion();
|
||||
}
|
||||
|
||||
std::shared_ptr<Task> OneSixInstance::doUpdate(bool only_prepare)
|
||||
std::shared_ptr<Task> OneSixInstance::doUpdate()
|
||||
{
|
||||
return std::shared_ptr<Task>(new OneSixUpdate(this, only_prepare));
|
||||
return std::shared_ptr<Task>(new OneSixUpdate(this));
|
||||
}
|
||||
|
||||
QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||
@ -130,7 +130,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
|
||||
return virtualRoot;
|
||||
}
|
||||
|
||||
QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
|
||||
{
|
||||
I_D(OneSixInstance);
|
||||
auto version = d->version;
|
||||
@ -138,17 +138,11 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
|
||||
QMap<QString, QString> token_mapping;
|
||||
// yggdrasil!
|
||||
token_mapping["auth_username"] = account->username();
|
||||
token_mapping["auth_session"] = account->sessionId();
|
||||
token_mapping["auth_access_token"] = account->accessToken();
|
||||
token_mapping["auth_player_name"] = account->currentProfile()->name;
|
||||
token_mapping["auth_uuid"] = account->currentProfile()->id;
|
||||
|
||||
// this is for offline?:
|
||||
/*
|
||||
map["auth_player_name"] = "Player";
|
||||
map["auth_player_name"] = "00000000-0000-0000-0000-000000000000";
|
||||
*/
|
||||
token_mapping["auth_username"] = session->username;
|
||||
token_mapping["auth_session"] = session->session;
|
||||
token_mapping["auth_access_token"] = session->access_token;
|
||||
token_mapping["auth_player_name"] = session->player_name;
|
||||
token_mapping["auth_uuid"] = session->uuid;
|
||||
|
||||
// these do nothing and are stupid.
|
||||
token_mapping["profile_name"] = name();
|
||||
@ -159,17 +153,8 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
QString absAssetsDir = QDir("assets/").absolutePath();
|
||||
token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath();
|
||||
|
||||
auto user = account->user();
|
||||
QJsonObject userAttrs;
|
||||
for (auto key : user.properties.keys())
|
||||
{
|
||||
auto array = QJsonArray::fromStringList(user.properties.values(key));
|
||||
userAttrs.insert(key, array);
|
||||
}
|
||||
QJsonDocument value(userAttrs);
|
||||
|
||||
token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact);
|
||||
token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang";
|
||||
token_mapping["user_properties"] = session->serializeUserProperties();
|
||||
token_mapping["user_type"] = session->user_type;
|
||||
// 1.7.3+ assets tokens
|
||||
token_mapping["assets_root"] = absAssetsDir;
|
||||
token_mapping["assets_index_name"] = version->assets;
|
||||
@ -182,7 +167,7 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
return parts;
|
||||
}
|
||||
|
||||
MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
|
||||
{
|
||||
I_D(OneSixInstance);
|
||||
|
||||
@ -207,7 +192,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
}
|
||||
launchScript += "mainClass " + version->mainClass + "\n";
|
||||
|
||||
for (auto param : processMinecraftArgs(account))
|
||||
for (auto param : processMinecraftArgs(session))
|
||||
{
|
||||
launchScript += "param " + param + "\n";
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ public:
|
||||
QString loaderModsDir() const;
|
||||
virtual QString instanceConfigFolder() const override;
|
||||
|
||||
virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override;
|
||||
virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override;
|
||||
virtual std::shared_ptr<Task> doUpdate() override;
|
||||
virtual MinecraftProcess *prepareForLaunch(AuthSessionPtr session) override;
|
||||
|
||||
virtual void cleanupAfterRun() override;
|
||||
|
||||
@ -73,6 +73,6 @@ public:
|
||||
virtual QString getStatusbarDescription() override;
|
||||
|
||||
private:
|
||||
QStringList processMinecraftArgs(MojangAccountPtr account);
|
||||
QStringList processMinecraftArgs(AuthSessionPtr account);
|
||||
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
||||
};
|
||||
|
@ -35,8 +35,8 @@
|
||||
#include "pathutils.h"
|
||||
#include <JlCompress.h>
|
||||
|
||||
OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent)
|
||||
: Task(parent), m_inst(inst), m_only_prepare(only_prepare)
|
||||
OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent)
|
||||
: Task(parent), m_inst(inst)
|
||||
{
|
||||
}
|
||||
|
||||
@ -52,12 +52,6 @@ void OneSixUpdate::executeTask()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_only_prepare)
|
||||
{
|
||||
prepareForLaunch();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_inst->shouldUpdate())
|
||||
{
|
||||
// Get a pointer to the version object that corresponds to the instance's version.
|
||||
@ -222,7 +216,7 @@ void OneSixUpdate::assetIndexFailed()
|
||||
|
||||
void OneSixUpdate::assetsFinished()
|
||||
{
|
||||
prepareForLaunch();
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void OneSixUpdate::assetsFailed()
|
||||
@ -330,43 +324,3 @@ void OneSixUpdate::jarlibFailed()
|
||||
emitFailed("Failed to download the following files:\n" + failed_all +
|
||||
"\n\nPlease try again.");
|
||||
}
|
||||
|
||||
void OneSixUpdate::prepareForLaunch()
|
||||
{
|
||||
setStatus(tr("Preparing for launch..."));
|
||||
QLOG_INFO() << m_inst->name() << ": preparing for launch";
|
||||
auto onesix_inst = (OneSixInstance *)m_inst;
|
||||
|
||||
// delete any leftovers, if they are present.
|
||||
onesix_inst->cleanupAfterRun();
|
||||
|
||||
QString natives_dir_raw = PathCombine(onesix_inst->instanceRoot(), "natives/");
|
||||
auto version = onesix_inst->getFullVersion();
|
||||
if (!version)
|
||||
{
|
||||
emitFailed("The version information for this instance is not complete. Try re-creating "
|
||||
"it or changing the version.");
|
||||
return;
|
||||
}
|
||||
/*
|
||||
for (auto lib : version->getActiveNativeLibs())
|
||||
{
|
||||
if (!lib->filesExist())
|
||||
{
|
||||
emitFailed("Native library is missing some files:\n" + lib->storagePath() +
|
||||
"\n\nRun the instance at least once in online mode to get all the "
|
||||
"required files.");
|
||||
return;
|
||||
}
|
||||
if (!lib->extractTo(natives_dir_raw))
|
||||
{
|
||||
emitFailed("Could not extract the native library:\n" + lib->storagePath() + " to " +
|
||||
natives_dir_raw +
|
||||
"\n\nMake sure MultiMC has appropriate permissions and there is enough "
|
||||
"space on the storage device.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
emitSucceeded();
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class OneSixUpdate : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0);
|
||||
explicit OneSixUpdate(BaseInstance *inst, QObject *parent = 0);
|
||||
virtual void executeTask();
|
||||
|
||||
private
|
||||
@ -49,9 +49,6 @@ slots:
|
||||
void assetsFinished();
|
||||
void assetsFailed();
|
||||
|
||||
// extract the appropriate libraries
|
||||
void prepareForLaunch();
|
||||
|
||||
private:
|
||||
NetJobPtr specificVersionDownloadJob;
|
||||
NetJobPtr jarlibDownloadJob;
|
||||
@ -59,5 +56,4 @@ private:
|
||||
// target version, determined during this task
|
||||
std::shared_ptr<MinecraftVersion> targetVersion;
|
||||
BaseInstance *m_inst = nullptr;
|
||||
bool m_only_prepare = false;
|
||||
};
|
||||
|
30
logic/auth/AuthSession.cpp
Normal file
30
logic/auth/AuthSession.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "AuthSession.h"
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QStringList>
|
||||
|
||||
QString AuthSession::serializeUserProperties()
|
||||
{
|
||||
QJsonObject userAttrs;
|
||||
for (auto key : u.properties.keys())
|
||||
{
|
||||
auto array = QJsonArray::fromStringList(u.properties.values(key));
|
||||
userAttrs.insert(key, array);
|
||||
}
|
||||
QJsonDocument value(userAttrs);
|
||||
return value.toJson(QJsonDocument::Compact);
|
||||
|
||||
}
|
||||
|
||||
bool AuthSession::MakeOffline(QString offline_playername)
|
||||
{
|
||||
if (status != PlayableOffline && status != PlayableOnline)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
session = "-";
|
||||
player_name = offline_playername;
|
||||
status = PlayableOffline;
|
||||
return true;
|
||||
}
|
49
logic/auth/AuthSession.h
Normal file
49
logic/auth/AuthSession.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QMultiMap>
|
||||
#include <memory>
|
||||
|
||||
struct User
|
||||
{
|
||||
QString id;
|
||||
QMultiMap<QString, QString> properties;
|
||||
};
|
||||
|
||||
struct AuthSession
|
||||
{
|
||||
bool MakeOffline(QString offline_playername);
|
||||
|
||||
QString serializeUserProperties();
|
||||
|
||||
enum Status
|
||||
{
|
||||
Undetermined,
|
||||
RequiresPassword,
|
||||
PlayableOffline,
|
||||
PlayableOnline
|
||||
} status = Undetermined;
|
||||
|
||||
User u;
|
||||
|
||||
// client token
|
||||
QString client_token;
|
||||
// account user name
|
||||
QString username;
|
||||
// combined session ID
|
||||
QString session;
|
||||
// volatile auth token
|
||||
QString access_token;
|
||||
// profile name
|
||||
QString player_name;
|
||||
// profile ID
|
||||
QString uuid;
|
||||
// 'legacy' or 'mojang', depending on account type
|
||||
QString user_type;
|
||||
// Did the auth server reply?
|
||||
bool auth_server_online = false;
|
||||
// Did the user request online mode?
|
||||
bool wants_online = true;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AuthSession> AuthSessionPtr;
|
@ -24,6 +24,7 @@
|
||||
#include <QJsonArray>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
@ -165,15 +166,26 @@ AccountStatus MojangAccount::accountStatus() const
|
||||
{
|
||||
if (m_accessToken.isEmpty())
|
||||
return NotVerified;
|
||||
if (!m_online)
|
||||
else
|
||||
return Verified;
|
||||
return Online;
|
||||
}
|
||||
|
||||
std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password)
|
||||
std::shared_ptr<YggdrasilTask> MojangAccount::login(AuthSessionPtr session,
|
||||
QString password)
|
||||
{
|
||||
if (m_currentTask)
|
||||
return m_currentTask;
|
||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||
|
||||
// take care of the true offline status
|
||||
if (accountStatus() == NotVerified && password.isEmpty())
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->status = AuthSession::RequiresPassword;
|
||||
fillSession(session);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (password.isEmpty())
|
||||
{
|
||||
m_currentTask.reset(new RefreshTask(this));
|
||||
@ -182,6 +194,8 @@ std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password)
|
||||
{
|
||||
m_currentTask.reset(new AuthenticateTask(this, password));
|
||||
}
|
||||
m_currentTask->assignSession(session);
|
||||
|
||||
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
||||
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
||||
return m_currentTask;
|
||||
@ -189,24 +203,76 @@ std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password)
|
||||
|
||||
void MojangAccount::authSucceeded()
|
||||
{
|
||||
m_online = true;
|
||||
auto session = m_currentTask->getAssignedSession();
|
||||
if (session)
|
||||
{
|
||||
session->status =
|
||||
session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
|
||||
fillSession(session);
|
||||
session->auth_server_online = true;
|
||||
}
|
||||
m_currentTask.reset();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void MojangAccount::authFailed(QString reason)
|
||||
{
|
||||
auto session = m_currentTask->getAssignedSession();
|
||||
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
||||
// -> we treat the error as no-op
|
||||
if (reason == "Yggdrasil task cancelled.")
|
||||
{
|
||||
// do nothing
|
||||
if (session)
|
||||
{
|
||||
session->status = accountStatus() == Verified ? AuthSession::PlayableOffline
|
||||
: AuthSession::RequiresPassword;
|
||||
session->auth_server_online = false;
|
||||
fillSession(session);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_online = false;
|
||||
m_accessToken = QString();
|
||||
emit changed();
|
||||
if (session)
|
||||
{
|
||||
session->status = AuthSession::RequiresPassword;
|
||||
session->auth_server_online = true;
|
||||
fillSession(session);
|
||||
}
|
||||
}
|
||||
m_currentTask.reset();
|
||||
}
|
||||
|
||||
void MojangAccount::fillSession(AuthSessionPtr session)
|
||||
{
|
||||
// the user name. you have to have an user name
|
||||
session->username = m_username;
|
||||
// volatile auth token
|
||||
session->access_token = m_accessToken;
|
||||
// the semi-permanent client token
|
||||
session->client_token = m_clientToken;
|
||||
if (currentProfile())
|
||||
{
|
||||
// profile name
|
||||
session->player_name = currentProfile()->name;
|
||||
// profile ID
|
||||
session->uuid = currentProfile()->id;
|
||||
// 'legacy' or 'mojang', depending on account type
|
||||
session->user_type = currentProfile()->legacy ? "legacy" : "mojang";
|
||||
if (!session->access_token.isEmpty())
|
||||
{
|
||||
session->session = "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
|
||||
}
|
||||
else
|
||||
{
|
||||
session->session = "-";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
session->player_name = "Player";
|
||||
session->session = "-";
|
||||
}
|
||||
session->u = user();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <QMap>
|
||||
|
||||
#include <memory>
|
||||
#include "AuthSession.h"
|
||||
|
||||
class Task;
|
||||
class YggdrasilTask;
|
||||
@ -45,17 +46,10 @@ struct AccountProfile
|
||||
bool legacy;
|
||||
};
|
||||
|
||||
struct User
|
||||
{
|
||||
QString id;
|
||||
QMultiMap<QString,QString> properties;
|
||||
};
|
||||
|
||||
enum AccountStatus
|
||||
{
|
||||
NotVerified,
|
||||
Verified,
|
||||
Online
|
||||
Verified
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,7 +78,7 @@ public: /* construction */
|
||||
QJsonObject saveToJson() const;
|
||||
|
||||
public: /* manipulation */
|
||||
/**
|
||||
/**
|
||||
* 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.
|
||||
@ -95,12 +89,9 @@ public: /* manipulation */
|
||||
* Attempt to login. Empty password means we use the token.
|
||||
* If the attempt fails because we already are performing some task, it returns false.
|
||||
*/
|
||||
std::shared_ptr<YggdrasilTask> login(QString password = QString());
|
||||
std::shared_ptr<YggdrasilTask> login(AuthSessionPtr session,
|
||||
QString password = QString());
|
||||
|
||||
void downgrade()
|
||||
{
|
||||
m_online = false;
|
||||
}
|
||||
public: /* queries */
|
||||
const QString &username() const
|
||||
{
|
||||
@ -122,19 +113,11 @@ public: /* queries */
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
const User & user()
|
||||
const User &user()
|
||||
{
|
||||
return m_user;
|
||||
}
|
||||
|
||||
//! Get the session ID required for legacy Minecraft versions
|
||||
QString sessionId() const
|
||||
{
|
||||
if (m_currentProfile != -1 && !m_accessToken.isEmpty())
|
||||
return "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
|
||||
return "-";
|
||||
}
|
||||
|
||||
//! Returns the currently selected profile (if none, returns nullptr)
|
||||
const AccountProfile *currentProfile() const;
|
||||
|
||||
@ -169,16 +152,17 @@ protected: /* variables */
|
||||
// the user structure, whatever it is.
|
||||
User m_user;
|
||||
|
||||
// true when the account is verified
|
||||
bool m_online = false;
|
||||
|
||||
// current task we are executing here
|
||||
std::shared_ptr<YggdrasilTask> m_currentTask;
|
||||
|
||||
private slots:
|
||||
private
|
||||
slots:
|
||||
void authSucceeded();
|
||||
void authFailed(QString reason);
|
||||
|
||||
private:
|
||||
void fillSession(AuthSessionPtr session);
|
||||
|
||||
public:
|
||||
friend class YggdrasilTask;
|
||||
friend class AuthenticateTask;
|
||||
|
@ -35,6 +35,21 @@ class YggdrasilTask : public Task
|
||||
public:
|
||||
explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
|
||||
|
||||
/**
|
||||
* assign a session to this task. the session will be filled with required infomration
|
||||
* upon completion
|
||||
*/
|
||||
void assignSession(AuthSessionPtr session)
|
||||
{
|
||||
m_session = session;
|
||||
}
|
||||
|
||||
/// get the assigned session for filling with information.
|
||||
AuthSessionPtr getAssignedSession()
|
||||
{
|
||||
return m_session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class describing a Yggdrasil error response.
|
||||
*/
|
||||
@ -117,4 +132,6 @@ protected:
|
||||
|
||||
const int timeout_max = 10000;
|
||||
const int time_step = 50;
|
||||
|
||||
AuthSessionPtr m_session;
|
||||
};
|
||||
|
@ -25,8 +25,7 @@
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
RefreshTask::RefreshTask(MojangAccount *account, QObject *parent)
|
||||
: YggdrasilTask(account, parent)
|
||||
RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
|
||||
{
|
||||
}
|
||||
|
||||
@ -126,7 +125,6 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
m_account->m_user = u;
|
||||
}
|
||||
|
||||
|
||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
||||
// we've succeeded.
|
||||
QLOG_DEBUG() << "Finished reading refresh response.";
|
||||
|
@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RefreshTask(MojangAccount * account, QObject *parent = 0);
|
||||
RefreshTask(MojangAccount * account);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const;
|
||||
@ -41,3 +41,4 @@ protected:
|
||||
|
||||
QString getStateMessage(const YggdrasilTask::State state) const;
|
||||
};
|
||||
|
||||
|
@ -31,7 +31,6 @@ void NetJob::partSucceeded(int index)
|
||||
num_succeeded++;
|
||||
QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/"
|
||||
<< downloads.size();
|
||||
emit filesProgress(num_succeeded, num_failed, downloads.size());
|
||||
|
||||
if (num_failed + num_succeeded == downloads.size())
|
||||
{
|
||||
@ -55,7 +54,6 @@ void NetJob::partFailed(int index)
|
||||
{
|
||||
QLOG_ERROR() << "Part" << index << "failed 3 times (" << downloads[index]->m_url << ")";
|
||||
num_failed++;
|
||||
emit filesProgress(num_succeeded, num_failed, downloads.size());
|
||||
if (num_failed + num_succeeded == downloads.size())
|
||||
{
|
||||
QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
|
||||
|
@ -84,7 +84,6 @@ public:
|
||||
{
|
||||
return m_job_name;
|
||||
}
|
||||
;
|
||||
virtual bool isRunning() const
|
||||
{
|
||||
return m_running;
|
||||
@ -94,7 +93,6 @@ public:
|
||||
signals:
|
||||
void started();
|
||||
void progress(qint64 current, qint64 total);
|
||||
void filesProgress(int, int, int);
|
||||
void succeeded();
|
||||
void failed();
|
||||
public
|
||||
|
Loading…
Reference in New Issue
Block a user