summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--gui/MainWindow.cpp204
-rw-r--r--gui/MainWindow.h15
-rw-r--r--gui/MainWindow.ui60
-rw-r--r--gui/dialogs/AccountListDialog.cpp2
-rw-r--r--gui/dialogs/OneSixModEditDialog.cpp8
-rw-r--r--gui/dialogs/VersionSelectDialog.cpp5
-rw-r--r--gui/dialogs/VersionSelectDialog.h1
-rw-r--r--gui/dialogs/VersionSelectDialog.ui9
-rw-r--r--gui/widgets/Common.cpp27
-rw-r--r--gui/widgets/Common.h6
-rw-r--r--gui/widgets/InstanceDelegate.cpp25
-rw-r--r--gui/widgets/VersionListView.cpp150
-rw-r--r--gui/widgets/VersionListView.h43
-rw-r--r--logic/BaseInstance.h4
-rw-r--r--logic/LegacyInstance.cpp15
-rw-r--r--logic/LegacyInstance.h4
-rw-r--r--logic/LegacyUpdate.cpp12
-rw-r--r--logic/LegacyUpdate.h3
-rw-r--r--logic/MinecraftProcess.cpp26
-rw-r--r--logic/MinecraftProcess.h6
-rw-r--r--logic/OneSixFTBInstance.cpp6
-rw-r--r--logic/OneSixFTBInstance.h2
-rw-r--r--logic/OneSixInstance.cpp39
-rw-r--r--logic/OneSixInstance.h6
-rw-r--r--logic/OneSixUpdate.cpp52
-rw-r--r--logic/OneSixUpdate.h6
-rw-r--r--logic/auth/AuthSession.cpp30
-rw-r--r--logic/auth/AuthSession.h49
-rw-r--r--logic/auth/MojangAccount.cpp82
-rw-r--r--logic/auth/MojangAccount.h38
-rw-r--r--logic/auth/YggdrasilTask.h17
-rw-r--r--logic/auth/flows/RefreshTask.cpp4
-rw-r--r--logic/auth/flows/RefreshTask.h3
-rw-r--r--logic/net/NetJob.cpp2
-rw-r--r--logic/net/NetJob.h2
36 files changed, 665 insertions, 304 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0acebacf..ba28a042 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -315,10 +315,14 @@ gui/dialogs/UpdateDialog.h
gui/dialogs/UpdateDialog.cpp
# GUI - widgets
+gui/widgets/Common.h
+gui/widgets/Common.cpp
gui/widgets/InstanceDelegate.h
gui/widgets/InstanceDelegate.cpp
gui/widgets/ModListView.h
gui/widgets/ModListView.cpp
+gui/widgets/VersionListView.h
+gui/widgets/VersionListView.cpp
gui/widgets/LabeledToolButton.h
gui/widgets/LabeledToolButton.cpp
gui/widgets/MCModInfoFrame.h
@@ -365,6 +369,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
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index ee9c3fad..9977dc75 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -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)
+
+ while (tryagain)
{
- // 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
+ 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;
}
- // in any case, revert from online to verified.
- account->downgrade();
- }
-}
-
-bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
-{
- 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
+ case AuthSession::RequiresPassword:
{
- // If the authentication task failed, recurse with the task's error message.
- return loginWithPassword(account, task->failReason());
+ 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;
+ }
}
}
- return false;
}
-void MainWindow::updateInstance(BaseInstance *instance, MojangAccountPtr account)
+void MainWindow::updateInstance(BaseInstance *instance, AuthSessionPtr session)
{
- 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);
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index eb478776..eeba2c26 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -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);
diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui
index 25af6f60..8cf26d18 100644
--- a/gui/MainWindow.ui
+++ b/gui/MainWindow.ui
@@ -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>
diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp
index 1712e352..a38035a6 100644
--- a/gui/dialogs/AccountListDialog.cpp
+++ b/gui/dialogs/AccountListDialog.cpp
@@ -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())
{
diff --git a/gui/dialogs/OneSixModEditDialog.cpp b/gui/dialogs/OneSixModEditDialog.cpp
index ebd685e8..1742ff80 100644
--- a/gui/dialogs/OneSixModEditDialog.cpp
+++ b/gui/dialogs/OneSixModEditDialog.cpp
@@ -161,6 +161,8 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
}
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
vselect.setFilter(1, m_inst->currentVersionId());
+ vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
+ m_inst->currentVersionId());
if (vselect.exec() && vselect.selectedVersion())
{
ForgeVersionPtr forgeVersion =
@@ -224,9 +226,9 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
}
if (!liteloader.add(m_inst))
{
- QMessageBox::critical(
- this, tr("LiteLoader"),
- tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details."));
+ QMessageBox::critical(this, tr("LiteLoader"),
+ tr("For reasons unknown, the LiteLoader installation failed. "
+ "Check your MultiMC log files for details."));
}
else
{
diff --git a/gui/dialogs/VersionSelectDialog.cpp b/gui/dialogs/VersionSelectDialog.cpp
index d6efe3c0..0f379f56 100644
--- a/gui/dialogs/VersionSelectDialog.cpp
+++ b/gui/dialogs/VersionSelectDialog.cpp
@@ -51,6 +51,11 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
}
}
+void VersionSelectDialog::setEmptyString(QString emptyString)
+{
+ ui->listView->setEmptyString(emptyString);
+}
+
VersionSelectDialog::~VersionSelectDialog()
{
delete ui;
diff --git a/gui/dialogs/VersionSelectDialog.h b/gui/dialogs/VersionSelectDialog.h
index e36341db..61fa8ab6 100644
--- a/gui/dialogs/VersionSelectDialog.h
+++ b/gui/dialogs/VersionSelectDialog.h
@@ -44,6 +44,7 @@ public:
BaseVersionPtr selectedVersion() const;
void setFilter(int column, QString filter);
+ void setEmptyString(QString emptyString);
void setResizeOn(int column);
private
diff --git a/gui/dialogs/VersionSelectDialog.ui b/gui/dialogs/VersionSelectDialog.ui
index 58264f24..07e9e73e 100644
--- a/gui/dialogs/VersionSelectDialog.ui
+++ b/gui/dialogs/VersionSelectDialog.ui
@@ -15,7 +15,7 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QTreeView" name="listView">
+ <widget class="VersionListView" name="listView">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
@@ -65,6 +65,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>VersionListView</class>
+ <extends>QTreeView</extends>
+ <header>gui/widgets/VersionListView.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections>
<connection>
diff --git a/gui/widgets/Common.cpp b/gui/widgets/Common.cpp
new file mode 100644
index 00000000..9b730d6c
--- /dev/null
+++ b/gui/widgets/Common.cpp
@@ -0,0 +1,27 @@
+#include "Common.h"
+
+// Origin: Qt
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed)
+{
+ QStringList lines;
+ height = 0;
+ widthUsed = 0;
+ textLayout.beginLayout();
+ QString str = textLayout.text();
+ while (true)
+ {
+ QTextLine line = textLayout.createLine();
+ if (!line.isValid())
+ break;
+ if (line.textLength() == 0)
+ break;
+ line.setLineWidth(lineWidth);
+ line.setPosition(QPointF(0, height));
+ height += line.height();
+ lines.append(str.mid(line.textStart(), line.textLength()));
+ widthUsed = qMax(widthUsed, line.naturalTextWidth());
+ }
+ textLayout.endLayout();
+ return lines;
+}
diff --git a/gui/widgets/Common.h b/gui/widgets/Common.h
new file mode 100644
index 00000000..fc46e08f
--- /dev/null
+++ b/gui/widgets/Common.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <QStringList>
+#include <QTextLayout>
+
+QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
+ qreal &widthUsed); \ No newline at end of file
diff --git a/gui/widgets/InstanceDelegate.cpp b/gui/widgets/InstanceDelegate.cpp
index 5020b8b6..33da7130 100644
--- a/gui/widgets/InstanceDelegate.cpp
+++ b/gui/widgets/InstanceDelegate.cpp
@@ -19,30 +19,7 @@
#include <QTextLayout>
#include <QApplication>
#include <QtCore/qmath.h>
-
-// Origin: Qt
-static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
- qreal &widthUsed)
-{
- height = 0;
- widthUsed = 0;
- textLayout.beginLayout();
- QString str = textLayout.text();
- while (true)
- {
- QTextLine line = textLayout.createLine();
- if (!line.isValid())
- break;
- if (line.textLength() == 0)
- break;
- line.setLineWidth(lineWidth);
- line.setPosition(QPointF(0, height));
- height += line.height();
- widthUsed = qMax(widthUsed, line.naturalTextWidth());
- }
- textLayout.endLayout();
-}
-
+#include "Common.h"
#define QFIXED_MAX (INT_MAX / 256)
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
diff --git a/gui/widgets/VersionListView.cpp b/gui/widgets/VersionListView.cpp
new file mode 100644
index 00000000..b7f45f27
--- /dev/null
+++ b/gui/widgets/VersionListView.cpp
@@ -0,0 +1,150 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QHeaderView>
+#include <QApplication>
+#include <QMouseEvent>
+#include <QDrag>
+#include <QPainter>
+#include "VersionListView.h"
+#include "Common.h"
+
+VersionListView::VersionListView(QWidget *parent)
+ :QTreeView ( parent )
+{
+ m_emptyString = tr("No versions are currently available.");
+}
+
+void VersionListView::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ if(!m_itemCount)
+ viewport()->update();
+ m_itemCount += end-start+1;
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+
+void VersionListView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
+{
+ m_itemCount -= end-start+1;
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::rowsInserted(parent, start, end);
+}
+
+void VersionListView::setModel(QAbstractItemModel *model)
+{
+ m_itemCount = model->rowCount();
+ if(!m_itemCount)
+ viewport()->update();
+ QTreeView::setModel(model);
+}
+
+void VersionListView::reset()
+{
+ if(model())
+ {
+ m_itemCount = model()->rowCount();
+ }
+ viewport()->update();
+ QTreeView::reset();
+}
+
+void VersionListView::setEmptyString(QString emptyString)
+{
+ m_emptyString = emptyString;
+ if(!m_itemCount)
+ {
+ viewport()->update();
+ }
+}
+
+void VersionListView::paintEvent(QPaintEvent *event)
+{
+ if(m_itemCount)
+ {
+ QTreeView::paintEvent(event);
+ }
+ else
+ {
+ paintInfoLabel(event);
+ }
+}
+
+void VersionListView::paintInfoLabel(QPaintEvent *event)
+{
+ int scrollInterval = 500;
+
+ //calculate the rect for the overlay
+ QPainter painter(viewport());
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ const QChar letter = 'Q';
+ QFont font("sans", 20);
+ font.setBold(true);
+
+ QRect bounds = viewport()->geometry();
+ bounds.moveTop(0);
+ QTextLayout layout(m_emptyString, font);
+ qreal height = 0.0;
+ qreal widthUsed = 0.0;
+ QStringList lines = viewItemTextLayout(layout, bounds.width() - 20, height, widthUsed);
+ QRect rect (0,0, widthUsed, height);
+ rect.setWidth(rect.width()+20);
+ rect.setHeight(rect.height()+20);
+ rect.moveCenter(bounds.center());
+ //check if we are allowed to draw in our area
+ if (!event->rect().intersects(rect)) {
+ return;
+ }
+ //draw the letter of the topmost item semitransparent in the middle
+ QColor background = QApplication::palette().color(QPalette::Foreground);
+ QColor foreground = QApplication::palette().color(QPalette::Base);
+ /*
+ background.setAlpha(128 - scrollFade);
+ foreground.setAlpha(128 - scrollFade);
+ */
+ painter.setBrush(QBrush(background));
+ painter.setPen(foreground);
+ painter.drawRoundedRect(rect, 5.0, 5.0);
+ foreground.setAlpha(190);
+ painter.setPen(foreground);
+ painter.setFont(font);
+ painter.drawText(rect, Qt::AlignCenter, lines.join("\n"));
+
+}
+
+/*
+void ModListView::setModel ( QAbstractItemModel* model )
+{
+ QTreeView::setModel ( model );
+ auto head = header();
+ head->setStretchLastSection(false);
+ // HACK: this is true for the checkbox column of mod lists
+ auto string = model->headerData(0,head->orientation()).toString();
+ if(!string.size())
+ {
+ head->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(1, QHeaderView::Stretch);
+ for(int i = 2; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+ else
+ {
+ head->setSectionResizeMode(0, QHeaderView::Stretch);
+ for(int i = 1; i < head->count(); i++)
+ head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
+ }
+}
+*/ \ No newline at end of file
diff --git a/gui/widgets/VersionListView.h b/gui/widgets/VersionListView.h
new file mode 100644
index 00000000..af9b1f6a
--- /dev/null
+++ b/gui/widgets/VersionListView.h
@@ -0,0 +1,43 @@
+/* Copyright 2013 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <QTreeView>
+
+class Mod;
+
+class VersionListView : public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit VersionListView(QWidget *parent = 0);
+ virtual void paintEvent(QPaintEvent *event) override;
+ void setEmptyString(QString emptyString);
+ virtual void setModel ( QAbstractItemModel* model );
+
+public slots:
+ virtual void reset() override;
+
+protected slots:
+ virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) override;
+ virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
+
+private: /* methods */
+ void paintInfoLabel(QPaintEvent *event);
+
+private: /* variables */
+ int m_itemCount = 0;
+ QString m_emptyString;
+};
diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h
index a861e9b2..cd49f99b 100644
--- a/logic/BaseInstance.h
+++ b/logic/BaseInstance.h
@@ -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'
diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp
index 2828bcbf..a9f0d112 100644
--- a/logic/LegacyInstance.cpp
+++ b/logic/LegacyInstance.cpp
@@ -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";
diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h
index 1e7d9eb6..636addeb 100644
--- a/logic/LegacyInstance.h
+++ b/logic/LegacyInstance.h
@@ -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;
diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp
index cb3598a7..5d82a76b 100644
--- a/logic/LegacyUpdate.cpp
+++ b/logic/LegacyUpdate.cpp
@@ -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)
diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h
index 0b573ca5..613eb1f9 100644
--- a/logic/LegacyUpdate.h
+++ b/logic/LegacyUpdate.h
@@ -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;
};
diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp
index 84610021..9c0a7074 100644
--- a/logic/MinecraftProcess.cpp
+++ b/logic/MinecraftProcess.cpp
@@ -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;
diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h
index 70e5df52..26214026 100644
--- a/logic/MinecraftProcess.h
+++ b/logic/MinecraftProcess.h
@@ -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;
diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp
index 54bfbe2b..ca88142a 100644
--- a/logic/OneSixFTBInstance.cpp
+++ b/logic/OneSixFTBInstance.cpp
@@ -104,7 +104,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));
@@ -112,11 +112,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;
}
diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h
index dc028819..bc543aeb 100644
--- a/logic/OneSixFTBInstance.h
+++ b/logic/OneSixFTBInstance.h
@@ -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;
diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp
index 88c40316..8ecbe7a0 100644
--- a/logic/OneSixInstance.cpp
+++ b/logic/OneSixInstance.cpp
@@ -46,9 +46,9 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *settings,
}
}
-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)
@@ -135,7 +135,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;
@@ -147,17 +147,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();
@@ -168,17 +162,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;
@@ -191,7 +176,7 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
return parts;
}
-MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
+MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
{
I_D(OneSixInstance);
@@ -216,7 +201,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";
}
diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h
index ee64e886..5aa12e44 100644
--- a/logic/OneSixInstance.h
+++ b/logic/OneSixInstance.h
@@ -36,8 +36,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;
@@ -72,6 +72,6 @@ signals:
void versionReloaded();
private:
- QStringList processMinecraftArgs(MojangAccountPtr account);
+ QStringList processMinecraftArgs(AuthSessionPtr account);
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
};
diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp
index c056bc4c..d3ac80c2 100644
--- a/logic/OneSixUpdate.cpp
+++ b/logic/OneSixUpdate.cpp
@@ -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();
-}
diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h
index bc717a94..3c18211e 100644
--- a/logic/OneSixUpdate.h
+++ b/logic/OneSixUpdate.h
@@ -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;
};
diff --git a/logic/auth/AuthSession.cpp b/logic/auth/AuthSession.cpp
new file mode 100644
index 00000000..8758bfbd
--- /dev/null
+++ b/logic/auth/AuthSession.cpp
@@ -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;
+}
diff --git a/logic/auth/AuthSession.h b/logic/auth/AuthSession.h
new file mode 100644
index 00000000..2ac170fa
--- /dev/null
+++ b/logic/auth/AuthSession.h
@@ -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;
diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp
index f41985ec..6c937ef1 100644
--- a/logic/auth/MojangAccount.cpp
+++ b/logic/auth/MojangAccount.cpp
@@ -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();
+}
diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h
index dd5d54ae..a0565e2c 100644
--- a/logic/auth/MojangAccount.h
+++ b/logic/auth/MojangAccount.h
@@ -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;
diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h
index 85f5a1e1..4a87067a 100644
--- a/logic/auth/YggdrasilTask.h
+++ b/logic/auth/YggdrasilTask.h
@@ -36,6 +36,21 @@ 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.
*/
struct Error
@@ -117,4 +132,6 @@ protected:
const int timeout_max = 10000;
const int time_step = 50;
+
+ AuthSessionPtr m_session;
};
diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp
index f63c736e..5a55ed91 100644
--- a/logic/auth/flows/RefreshTask.cpp
+++ b/logic/auth/flows/RefreshTask.cpp
@@ -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.";
diff --git a/logic/auth/flows/RefreshTask.h b/logic/auth/flows/RefreshTask.h
index 2fd50c60..0dadc025 100644
--- a/logic/auth/flows/RefreshTask.h
+++ b/logic/auth/flows/RefreshTask.h
@@ -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;
};
+
diff --git a/logic/net/NetJob.cpp b/logic/net/NetJob.cpp
index 8b79bc54..9e800d13 100644
--- a/logic/net/NetJob.cpp
+++ b/logic/net/NetJob.cpp
@@ -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.";
diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h
index 68c4c408..03d6a36e 100644
--- a/logic/net/NetJob.h
+++ b/logic/net/NetJob.h
@@ -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