Merge remote-tracking branch 'upstream/feature_derpstances' into feature_derpstances
This commit is contained in:
commit
983a40698c
@ -315,10 +315,14 @@ gui/dialogs/UpdateDialog.h
|
|||||||
gui/dialogs/UpdateDialog.cpp
|
gui/dialogs/UpdateDialog.cpp
|
||||||
|
|
||||||
# GUI - widgets
|
# GUI - widgets
|
||||||
|
gui/widgets/Common.h
|
||||||
|
gui/widgets/Common.cpp
|
||||||
gui/widgets/InstanceDelegate.h
|
gui/widgets/InstanceDelegate.h
|
||||||
gui/widgets/InstanceDelegate.cpp
|
gui/widgets/InstanceDelegate.cpp
|
||||||
gui/widgets/ModListView.h
|
gui/widgets/ModListView.h
|
||||||
gui/widgets/ModListView.cpp
|
gui/widgets/ModListView.cpp
|
||||||
|
gui/widgets/VersionListView.h
|
||||||
|
gui/widgets/VersionListView.cpp
|
||||||
gui/widgets/LabeledToolButton.h
|
gui/widgets/LabeledToolButton.h
|
||||||
gui/widgets/LabeledToolButton.cpp
|
gui/widgets/LabeledToolButton.cpp
|
||||||
gui/widgets/MCModInfoFrame.h
|
gui/widgets/MCModInfoFrame.h
|
||||||
@ -365,6 +369,8 @@ logic/net/PasteUpload.cpp
|
|||||||
logic/net/URLConstants.h
|
logic/net/URLConstants.h
|
||||||
|
|
||||||
# Yggdrasil login stuff
|
# Yggdrasil login stuff
|
||||||
|
logic/auth/AuthSession.h
|
||||||
|
logic/auth/AuthSession.cpp
|
||||||
logic/auth/MojangAccountList.h
|
logic/auth/MojangAccountList.h
|
||||||
logic/auth/MojangAccountList.cpp
|
logic/auth/MojangAccountList.cpp
|
||||||
logic/auth/MojangAccount.h
|
logic/auth/MojangAccount.h
|
||||||
|
@ -71,7 +71,6 @@
|
|||||||
|
|
||||||
#include "logic/auth/flows/AuthenticateTask.h"
|
#include "logic/auth/flows/AuthenticateTask.h"
|
||||||
#include "logic/auth/flows/RefreshTask.h"
|
#include "logic/auth/flows/RefreshTask.h"
|
||||||
#include "logic/auth/flows/ValidateTask.h"
|
|
||||||
|
|
||||||
#include "logic/updater/DownloadUpdateTask.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->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
newsLabel->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
|
ui->newsToolBar->insertWidget(ui->actionMoreNews, newsLabel);
|
||||||
QObject::connect(newsLabel, &QAbstractButton::clicked, this, &MainWindow::newsButtonClicked);
|
QObject::connect(newsLabel, &QAbstractButton::clicked, this,
|
||||||
QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this, &MainWindow::updateNewsLabel);
|
&MainWindow::newsButtonClicked);
|
||||||
|
QObject::connect(MMC->newsChecker().get(), &NewsChecker::newsLoaded, this,
|
||||||
|
&MainWindow::updateNewsLabel);
|
||||||
updateNewsLabel();
|
updateNewsLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +174,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
view->setModel(proxymodel);
|
view->setModel(proxymodel);
|
||||||
|
|
||||||
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
connect(view, SIGNAL(customContextMenuRequested(const QPoint&)),
|
connect(view, SIGNAL(customContextMenuRequested(const QPoint &)), this,
|
||||||
this, SLOT(showInstanceContextMenu(const QPoint&)));
|
SLOT(showInstanceContextMenu(const QPoint &)));
|
||||||
|
|
||||||
ui->horizontalLayout->addWidget(view);
|
ui->horizontalLayout->addWidget(view);
|
||||||
}
|
}
|
||||||
@ -213,8 +214,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
|
|
||||||
// Start status checker
|
// Start status checker
|
||||||
{
|
{
|
||||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this, &MainWindow::updateStatusUI);
|
connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this,
|
||||||
connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this, &MainWindow::updateStatusFailedUI);
|
&MainWindow::updateStatusUI);
|
||||||
|
connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this,
|
||||||
|
&MainWindow::updateStatusFailedUI);
|
||||||
|
|
||||||
connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
|
connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
|
||||||
connect(&statusTimer, &QTimer::timeout, 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())
|
if (MMC->settings()->get("AutoUpdate").toBool())
|
||||||
on_actionCheckUpdate_triggered();
|
on_actionCheckUpdate_triggered();
|
||||||
|
|
||||||
connect(MMC->notificationChecker().get(), &NotificationChecker::notificationCheckFinished,
|
connect(MMC->notificationChecker().get(),
|
||||||
this, &MainWindow::notificationsChanged);
|
&NotificationChecker::notificationCheckFinished, this,
|
||||||
|
&MainWindow::notificationsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
|
setSelectedInstanceById(MMC->settings()->get("SelectedInstance").toString());
|
||||||
@ -522,9 +526,12 @@ static QString convertStatus(const QString &status)
|
|||||||
{
|
{
|
||||||
QString ret = "?";
|
QString ret = "?";
|
||||||
|
|
||||||
if(status == "green") ret = "↑";
|
if (status == "green")
|
||||||
else if(status == "yellow") ret = "-";
|
ret = "↑";
|
||||||
else if(status == "red") ret="↓";
|
else if (status == "yellow")
|
||||||
|
ret = "-";
|
||||||
|
else if (status == "red")
|
||||||
|
ret = "↓";
|
||||||
|
|
||||||
return "<span style=\"font-size:11pt; font-weight:600;\">" + ret + "</span>";
|
return "<span style=\"font-size:11pt; font-weight:600;\">" + ret + "</span>";
|
||||||
}
|
}
|
||||||
@ -632,7 +639,8 @@ void MainWindow::notificationsChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox box(icon, tr("Notification"), entry.message, QMessageBox::Close, this);
|
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.setDefaultButton(QMessageBox::Close);
|
||||||
box.exec();
|
box.exec();
|
||||||
if (box.clickedButton() == dontShowAgainButton)
|
if (box.clickedButton() == dontShowAgainButton)
|
||||||
@ -751,7 +759,7 @@ void MainWindow::on_actionAddInstance_triggered()
|
|||||||
if (MMC->accounts()->anyAccountIsValid())
|
if (MMC->accounts()->anyAccountIsValid())
|
||||||
{
|
{
|
||||||
ProgressDialog loadDialog(this);
|
ProgressDialog loadDialog(this);
|
||||||
auto update = newInstance->doUpdate(false);
|
auto update = newInstance->doUpdate();
|
||||||
connect(update.get(), &Task::failed, [this](QString reason)
|
connect(update.get(), &Task::failed, [this](QString reason)
|
||||||
{
|
{
|
||||||
QString error = QString("Instance load failed: %1").arg(reason);
|
QString error = QString("Instance load failed: %1").arg(reason);
|
||||||
@ -860,7 +868,8 @@ void MainWindow::setSelectedInstanceById(const QString &id)
|
|||||||
selectionIndex = proxymodel->mapFromSource(index);
|
selectionIndex = proxymodel->mapFromSource(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view->selectionModel()->setCurrentIndex(selectionIndex, QItemSelectionModel::ClearAndSelect);
|
view->selectionModel()->setCurrentIndex(selectionIndex,
|
||||||
|
QItemSelectionModel::ClearAndSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionChangeInstGroup_triggered()
|
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)
|
if (!m_selectedInstance)
|
||||||
return;
|
return;
|
||||||
@ -1104,89 +1122,111 @@ void MainWindow::doLaunch()
|
|||||||
if (!account.get())
|
if (!account.get())
|
||||||
return;
|
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 "
|
QString failReason = tr("Your account is currently not logged in. Please enter "
|
||||||
"your password to log in again.");
|
"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();
|
while (tryagain)
|
||||||
if (status != NotVerified)
|
|
||||||
{
|
{
|
||||||
updateInstance(m_selectedInstance, account);
|
AuthSessionPtr session(new AuthSession());
|
||||||
}
|
session->wants_online = online;
|
||||||
else
|
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())
|
if (!task->successful())
|
||||||
{
|
{
|
||||||
failReason = task->failReason();
|
failReason = task->failReason();
|
||||||
}
|
}
|
||||||
if (loginWithPassword(account, failReason))
|
|
||||||
updateInstance(m_selectedInstance, account);
|
|
||||||
}
|
}
|
||||||
// in any case, revert from online to verified.
|
switch (session->status)
|
||||||
account->downgrade();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
if (loginWithPassword(account, failReason))
|
case AuthSession::Undetermined:
|
||||||
{
|
{
|
||||||
updateInstance(m_selectedInstance, account);
|
QLOG_ERROR() << "Received undetermined session status during login. Bye.";
|
||||||
account->downgrade();
|
tryagain = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// in any case, revert from online to verified.
|
case AuthSession::RequiresPassword:
|
||||||
account->downgrade();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString &errorMsg)
|
|
||||||
{
|
{
|
||||||
EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField);
|
EditAccountDialog passDialog(failReason, this, EditAccountDialog::PasswordField);
|
||||||
if (passDialog.exec() == QDialog::Accepted)
|
if (passDialog.exec() == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
// To refresh the token, we just create an authenticate task with the given account and
|
password = passDialog.password();
|
||||||
// the user's password.
|
}
|
||||||
ProgressDialog progDialog(this);
|
|
||||||
auto task = account->login(passDialog.password());
|
|
||||||
progDialog.exec(task.get());
|
|
||||||
if (task->successful())
|
|
||||||
return true;
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the authentication task failed, recurse with the task's error message.
|
tryagain = false;
|
||||||
return loginWithPassword(account, task->failReason());
|
}
|
||||||
|
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();
|
||||||
auto updateTask = instance->doUpdate(only_prepare);
|
|
||||||
if (!updateTask)
|
if (!updateTask)
|
||||||
{
|
{
|
||||||
launchInstance(instance, account);
|
launchInstance(instance, session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProgressDialog tDialog(this);
|
ProgressDialog tDialog(this);
|
||||||
connect(updateTask.get(), &Task::succeeded, [this, instance, account]
|
connect(updateTask.get(), &Task::succeeded, [this, instance, session]
|
||||||
{ launchInstance(instance, account); });
|
{ launchInstance(instance, session); });
|
||||||
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
|
||||||
tDialog.exec(updateTask.get());
|
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(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)
|
if (!proc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1195,7 +1235,7 @@ void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account
|
|||||||
console = new ConsoleWindow(proc);
|
console = new ConsoleWindow(proc);
|
||||||
connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
|
connect(console, SIGNAL(isClosing()), this, SLOT(instanceEnded()));
|
||||||
|
|
||||||
proc->setLogin(account);
|
proc->setLogin(session);
|
||||||
proc->launch();
|
proc->launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1285,7 +1325,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered()
|
|||||||
}
|
}
|
||||||
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor());
|
||||||
|
|
||||||
auto updateTask = m_selectedInstance->doUpdate(false);
|
auto updateTask = m_selectedInstance->doUpdate();
|
||||||
if (!updateTask)
|
if (!updateTask)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -96,6 +96,8 @@ slots:
|
|||||||
|
|
||||||
void on_actionLaunchInstance_triggered();
|
void on_actionLaunchInstance_triggered();
|
||||||
|
|
||||||
|
void on_actionLaunchInstanceOffline_triggered();
|
||||||
|
|
||||||
void on_actionDeleteInstance_triggered();
|
void on_actionDeleteInstance_triggered();
|
||||||
|
|
||||||
void on_actionRenameInstance_triggered();
|
void on_actionRenameInstance_triggered();
|
||||||
@ -112,25 +114,18 @@ slots:
|
|||||||
* Launches the currently selected instance with the default account.
|
* Launches the currently selected instance with the default account.
|
||||||
* If no default account is selected, prompts the user to pick an account.
|
* If no default account is selected, prompts the user to pick an account.
|
||||||
*/
|
*/
|
||||||
void doLaunch();
|
void doLaunch(bool online = true);
|
||||||
|
|
||||||
/*!
|
|
||||||
* 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="");
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Launches the given instance with the given account.
|
* Launches the given instance with the given account.
|
||||||
* This function assumes that the given account has a valid, usable access token.
|
* 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.
|
* 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);
|
void onGameUpdateError(QString error);
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>688</width>
|
<width>694</width>
|
||||||
<height>460</height>
|
<height>563</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -107,6 +107,7 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
<addaction name="actionChangeInstIcon"/>
|
<addaction name="actionChangeInstIcon"/>
|
||||||
<addaction name="actionLaunchInstance"/>
|
<addaction name="actionLaunchInstance"/>
|
||||||
|
<addaction name="actionLaunchInstanceOffline"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionEditInstNotes"/>
|
<addaction name="actionEditInstNotes"/>
|
||||||
<addaction name="actionChangeInstGroup"/>
|
<addaction name="actionChangeInstGroup"/>
|
||||||
@ -152,7 +153,9 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<action name="actionAddInstance">
|
<action name="actionAddInstance">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="new"/>
|
<iconset theme="new">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Add Instance</string>
|
<string>Add Instance</string>
|
||||||
@ -166,7 +169,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionViewInstanceFolder">
|
<action name="actionViewInstanceFolder">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="viewfolder"/>
|
<iconset theme="viewfolder">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>View Instance Folder</string>
|
<string>View Instance Folder</string>
|
||||||
@ -180,7 +185,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionRefresh">
|
<action name="actionRefresh">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="refresh"/>
|
<iconset theme="refresh">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Refresh</string>
|
<string>Refresh</string>
|
||||||
@ -194,7 +201,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionViewCentralModsFolder">
|
<action name="actionViewCentralModsFolder">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="centralmods"/>
|
<iconset theme="centralmods">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>View Central Mods Folder</string>
|
<string>View Central Mods Folder</string>
|
||||||
@ -208,7 +217,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionCheckUpdate">
|
<action name="actionCheckUpdate">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="checkupdate"/>
|
<iconset theme="checkupdate">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Check for Updates</string>
|
<string>Check for Updates</string>
|
||||||
@ -222,7 +233,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionSettings">
|
<action name="actionSettings">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="settings"/>
|
<iconset theme="settings">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
@ -239,7 +252,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionReportBug">
|
<action name="actionReportBug">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="bug"/>
|
<iconset theme="bug">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Report a Bug</string>
|
<string>Report a Bug</string>
|
||||||
@ -253,7 +268,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionMoreNews">
|
<action name="actionMoreNews">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="news"/>
|
<iconset theme="news">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>More News</string>
|
<string>More News</string>
|
||||||
@ -270,7 +287,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionAbout">
|
<action name="actionAbout">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="about"/>
|
<iconset theme="about">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>About MultiMC</string>
|
<string>About MultiMC</string>
|
||||||
@ -463,7 +482,9 @@
|
|||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="cat"/>
|
<iconset theme="cat">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Meow</string>
|
<string>Meow</string>
|
||||||
@ -474,7 +495,9 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionCopyInstance">
|
<action name="actionCopyInstance">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="copy"/>
|
<iconset theme="copy">
|
||||||
|
<normaloff/>
|
||||||
|
</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Copy Instance</string>
|
<string>Copy Instance</string>
|
||||||
@ -494,6 +517,17 @@
|
|||||||
<string>Manage your Mojang or Minecraft accounts.</string>
|
<string>Manage your Mojang or Minecraft accounts.</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</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>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -126,7 +126,7 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
|||||||
|
|
||||||
MojangAccountPtr account = MojangAccount::createFromUsername(username);
|
MojangAccountPtr account = MojangAccount::createFromUsername(username);
|
||||||
ProgressDialog progDialog(this);
|
ProgressDialog progDialog(this);
|
||||||
auto task = account->login(password);
|
auto task = account->login(nullptr, password);
|
||||||
progDialog.exec(task.get());
|
progDialog.exec(task.get());
|
||||||
if(task->successful())
|
if(task->successful())
|
||||||
{
|
{
|
||||||
|
@ -161,6 +161,8 @@ void OneSixModEditDialog::on_forgeBtn_clicked()
|
|||||||
}
|
}
|
||||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||||
vselect.setFilter(1, m_inst->currentVersionId());
|
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())
|
if (vselect.exec() && vselect.selectedVersion())
|
||||||
{
|
{
|
||||||
ForgeVersionPtr forgeVersion =
|
ForgeVersionPtr forgeVersion =
|
||||||
@ -224,9 +226,9 @@ void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
|||||||
}
|
}
|
||||||
if (!liteloader.add(m_inst))
|
if (!liteloader.add(m_inst))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(
|
QMessageBox::critical(this, tr("LiteLoader"),
|
||||||
this, tr("LiteLoader"),
|
tr("For reasons unknown, the LiteLoader installation failed. "
|
||||||
tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details."));
|
"Check your MultiMC log files for details."));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,11 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VersionSelectDialog::setEmptyString(QString emptyString)
|
||||||
|
{
|
||||||
|
ui->listView->setEmptyString(emptyString);
|
||||||
|
}
|
||||||
|
|
||||||
VersionSelectDialog::~VersionSelectDialog()
|
VersionSelectDialog::~VersionSelectDialog()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
|
@ -44,6 +44,7 @@ public:
|
|||||||
BaseVersionPtr selectedVersion() const;
|
BaseVersionPtr selectedVersion() const;
|
||||||
|
|
||||||
void setFilter(int column, QString filter);
|
void setFilter(int column, QString filter);
|
||||||
|
void setEmptyString(QString emptyString);
|
||||||
void setResizeOn(int column);
|
void setResizeOn(int column);
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="listView">
|
<widget class="VersionListView" name="listView">
|
||||||
<property name="horizontalScrollBarPolicy">
|
<property name="horizontalScrollBarPolicy">
|
||||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -65,6 +65,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>VersionListView</class>
|
||||||
|
<extends>QTreeView</extends>
|
||||||
|
<header>gui/widgets/VersionListView.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
|
27
gui/widgets/Common.cpp
Normal file
27
gui/widgets/Common.cpp
Normal file
@ -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;
|
||||||
|
}
|
6
gui/widgets/Common.h
Normal file
6
gui/widgets/Common.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
|
QStringList viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height,
|
||||||
|
qreal &widthUsed);
|
@ -19,30 +19,7 @@
|
|||||||
#include <QTextLayout>
|
#include <QTextLayout>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QtCore/qmath.h>
|
#include <QtCore/qmath.h>
|
||||||
|
#include "Common.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();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define QFIXED_MAX (INT_MAX / 256)
|
#define QFIXED_MAX (INT_MAX / 256)
|
||||||
|
|
||||||
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
||||||
|
150
gui/widgets/VersionListView.cpp
Normal file
150
gui/widgets/VersionListView.cpp
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
43
gui/widgets/VersionListView.h
Normal file
43
gui/widgets/VersionListView.h
Normal file
@ -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;
|
||||||
|
};
|
@ -155,10 +155,10 @@ public:
|
|||||||
virtual SettingsObject &settings() const;
|
virtual SettingsObject &settings() const;
|
||||||
|
|
||||||
/// returns a valid update task
|
/// 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.
|
/// 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
|
/// do any necessary cleanups after the instance finishes. also runs before
|
||||||
/// 'prepareForLaunch'
|
/// 'prepareForLaunch'
|
||||||
|
@ -42,15 +42,15 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings,
|
|||||||
settings->registerSetting("IntendedJarVersion", "");
|
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.
|
// make sure the jar mods list is initialized by asking for it.
|
||||||
auto list = jarModList();
|
auto list = jarModList();
|
||||||
// create an update task
|
// 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);
|
MinecraftProcess *proc = new MinecraftProcess(this);
|
||||||
|
|
||||||
@ -66,13 +66,14 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
|||||||
if (settings().get("LaunchMaximized").toBool())
|
if (settings().get("LaunchMaximized").toBool())
|
||||||
windowParams = "max";
|
windowParams = "max";
|
||||||
else
|
else
|
||||||
windowParams = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg(
|
windowParams = QString("%1x%2")
|
||||||
settings().get("MinecraftWinHeight").toInt());
|
.arg(settings().get("MinecraftWinWidth").toInt())
|
||||||
|
.arg(settings().get("MinecraftWinHeight").toInt());
|
||||||
|
|
||||||
QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
|
QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion())
|
||||||
.absolutePath();
|
.absolutePath();
|
||||||
launchScript += "userName " + account->currentProfile()->name + "\n";
|
launchScript += "userName " + account->player_name + "\n";
|
||||||
launchScript += "sessionId " + account->sessionId() + "\n";
|
launchScript += "sessionId " + account->session + "\n";
|
||||||
launchScript += "windowTitle " + windowTitle() + "\n";
|
launchScript += "windowTitle " + windowTitle() + "\n";
|
||||||
launchScript += "windowParams " + windowParams + "\n";
|
launchScript += "windowParams " + windowParams + "\n";
|
||||||
launchScript += "lwjgl " + lwjgl + "\n";
|
launchScript += "lwjgl " + lwjgl + "\n";
|
||||||
|
@ -76,9 +76,9 @@ public:
|
|||||||
|
|
||||||
virtual bool shouldUpdate() const override;
|
virtual bool shouldUpdate() const override;
|
||||||
virtual void setShouldUpdate(bool val) 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 void cleanupAfterRun() override;
|
||||||
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
virtual QDialog *createModEditDialog(QWidget *parent) override;
|
||||||
|
|
||||||
|
@ -27,13 +27,13 @@
|
|||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
#include "logic/net/URLConstants.h"
|
#include "logic/net/URLConstants.h"
|
||||||
|
|
||||||
LegacyUpdate::LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent)
|
LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : Task(parent), m_inst(inst)
|
||||||
: Task(parent), m_inst(inst), m_only_prepare(only_prepare)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyUpdate::executeTask()
|
void LegacyUpdate::executeTask()
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
if(m_only_prepare)
|
if(m_only_prepare)
|
||||||
{
|
{
|
||||||
// FIXME: think this through some more.
|
// FIXME: think this through some more.
|
||||||
@ -49,8 +49,9 @@ void LegacyUpdate::executeTask()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
*/
|
||||||
lwjglStart();
|
lwjglStart();
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LegacyUpdate::lwjglStart()
|
void LegacyUpdate::lwjglStart()
|
||||||
@ -268,7 +269,6 @@ void LegacyUpdate::jarStart()
|
|||||||
|
|
||||||
auto dljob = new NetJob("Minecraft.jar for version " + version_id);
|
auto dljob = new NetJob("Minecraft.jar for version " + version_id);
|
||||||
|
|
||||||
|
|
||||||
auto metacache = MMC->metacache();
|
auto metacache = MMC->metacache();
|
||||||
auto entry = metacache->resolveEntry("versions", localPath);
|
auto entry = metacache->resolveEntry("versions", localPath);
|
||||||
dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
|
dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry));
|
||||||
|
@ -31,7 +31,7 @@ class LegacyUpdate : public Task
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent = 0);
|
explicit LegacyUpdate(BaseInstance *inst, QObject *parent = 0);
|
||||||
virtual void executeTask();
|
virtual void executeTask();
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -72,5 +72,4 @@ private:
|
|||||||
private:
|
private:
|
||||||
NetJobPtr legacyDownloadJob;
|
NetJobPtr legacyDownloadJob;
|
||||||
BaseInstance *m_inst = nullptr;
|
BaseInstance *m_inst = nullptr;
|
||||||
bool m_only_prepare = false;
|
|
||||||
};
|
};
|
||||||
|
@ -79,26 +79,18 @@ void MinecraftProcess::setWorkdir(QString path)
|
|||||||
|
|
||||||
QString MinecraftProcess::censorPrivateInfo(QString in)
|
QString MinecraftProcess::censorPrivateInfo(QString in)
|
||||||
{
|
{
|
||||||
if(!m_account)
|
if(!m_session)
|
||||||
return in;
|
return in;
|
||||||
|
|
||||||
QString sessionId = m_account->sessionId();
|
if(m_session->session != "-")
|
||||||
QString accessToken = m_account->accessToken();
|
in.replace(m_session->session, "<SESSION ID>");
|
||||||
QString clientToken = m_account->clientToken();
|
in.replace(m_session->access_token, "<ACCESS TOKEN>");
|
||||||
in.replace(sessionId, "<SESSION ID>");
|
in.replace(m_session->client_token, "<CLIENT TOKEN>");
|
||||||
in.replace(accessToken, "<ACCESS TOKEN>");
|
in.replace(m_session->uuid, "<PROFILE ID>");
|
||||||
in.replace(clientToken, "<CLIENT TOKEN>");
|
in.replace(m_session->player_name, "<PROFILE NAME>");
|
||||||
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>");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto i = m_account->user().properties.begin();
|
auto i = m_session->u.properties.begin();
|
||||||
while (i != m_account->user().properties.end())
|
while (i != m_session->u.properties.end())
|
||||||
{
|
{
|
||||||
in.replace(i.value(), "<" + i.key().toUpper() + ">");
|
in.replace(i.value(), "<" + i.key().toUpper() + ">");
|
||||||
++i;
|
++i;
|
||||||
|
@ -78,9 +78,9 @@ public:
|
|||||||
|
|
||||||
void killMinecraft();
|
void killMinecraft();
|
||||||
|
|
||||||
inline void setLogin(MojangAccountPtr account)
|
inline void setLogin(AuthSessionPtr session)
|
||||||
{
|
{
|
||||||
m_account = account;
|
m_session = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -117,7 +117,7 @@ protected:
|
|||||||
QString m_out_leftover;
|
QString m_out_leftover;
|
||||||
QProcess m_prepostlaunchprocess;
|
QProcess m_prepostlaunchprocess;
|
||||||
bool killed = false;
|
bool killed = false;
|
||||||
MojangAccountPtr m_account;
|
AuthSessionPtr m_session;
|
||||||
QString launchScript;
|
QString launchScript;
|
||||||
QString m_nativeFolder;
|
QString m_nativeFolder;
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ bool OneSixFTBInstance::menuActionEnabled(QString action_name) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Task> OneSixFTBInstance::doUpdate(bool only_prepare)
|
std::shared_ptr<Task> OneSixFTBInstance::doUpdate()
|
||||||
{
|
{
|
||||||
std::shared_ptr<SequentialTask> task;
|
std::shared_ptr<SequentialTask> task;
|
||||||
task.reset(new SequentialTask(this));
|
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(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)));
|
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.
|
//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.
|
//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;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ public:
|
|||||||
virtual QString getStatusbarDescription();
|
virtual QString getStatusbarDescription();
|
||||||
virtual bool menuActionEnabled(QString action_name) const;
|
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;
|
virtual QString id() const;
|
||||||
|
|
||||||
|
@ -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)
|
QString replaceTokensIn(QString text, QMap<QString, QString> with)
|
||||||
@ -135,7 +135,7 @@ QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version)
|
|||||||
return virtualRoot;
|
return virtualRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
QStringList OneSixInstance::processMinecraftArgs(AuthSessionPtr session)
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
auto version = d->version;
|
auto version = d->version;
|
||||||
@ -147,17 +147,11 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
|||||||
|
|
||||||
QMap<QString, QString> token_mapping;
|
QMap<QString, QString> token_mapping;
|
||||||
// yggdrasil!
|
// yggdrasil!
|
||||||
token_mapping["auth_username"] = account->username();
|
token_mapping["auth_username"] = session->username;
|
||||||
token_mapping["auth_session"] = account->sessionId();
|
token_mapping["auth_session"] = session->session;
|
||||||
token_mapping["auth_access_token"] = account->accessToken();
|
token_mapping["auth_access_token"] = session->access_token;
|
||||||
token_mapping["auth_player_name"] = account->currentProfile()->name;
|
token_mapping["auth_player_name"] = session->player_name;
|
||||||
token_mapping["auth_uuid"] = account->currentProfile()->id;
|
token_mapping["auth_uuid"] = session->uuid;
|
||||||
|
|
||||||
// this is for offline?:
|
|
||||||
/*
|
|
||||||
map["auth_player_name"] = "Player";
|
|
||||||
map["auth_player_name"] = "00000000-0000-0000-0000-000000000000";
|
|
||||||
*/
|
|
||||||
|
|
||||||
// these do nothing and are stupid.
|
// these do nothing and are stupid.
|
||||||
token_mapping["profile_name"] = name();
|
token_mapping["profile_name"] = name();
|
||||||
@ -168,17 +162,8 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
|||||||
QString absAssetsDir = QDir("assets/").absolutePath();
|
QString absAssetsDir = QDir("assets/").absolutePath();
|
||||||
token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath();
|
token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath();
|
||||||
|
|
||||||
auto user = account->user();
|
token_mapping["user_properties"] = session->serializeUserProperties();
|
||||||
QJsonObject userAttrs;
|
token_mapping["user_type"] = session->user_type;
|
||||||
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";
|
|
||||||
// 1.7.3+ assets tokens
|
// 1.7.3+ assets tokens
|
||||||
token_mapping["assets_root"] = absAssetsDir;
|
token_mapping["assets_root"] = absAssetsDir;
|
||||||
token_mapping["assets_index_name"] = version->assets;
|
token_mapping["assets_index_name"] = version->assets;
|
||||||
@ -191,7 +176,7 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
|||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
|
MinecraftProcess *OneSixInstance::prepareForLaunch(AuthSessionPtr session)
|
||||||
{
|
{
|
||||||
I_D(OneSixInstance);
|
I_D(OneSixInstance);
|
||||||
|
|
||||||
@ -216,7 +201,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account)
|
|||||||
}
|
}
|
||||||
launchScript += "mainClass " + version->mainClass + "\n";
|
launchScript += "mainClass " + version->mainClass + "\n";
|
||||||
|
|
||||||
for (auto param : processMinecraftArgs(account))
|
for (auto param : processMinecraftArgs(session))
|
||||||
{
|
{
|
||||||
launchScript += "param " + param + "\n";
|
launchScript += "param " + param + "\n";
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ public:
|
|||||||
QString loaderModsDir() const;
|
QString loaderModsDir() const;
|
||||||
virtual QString instanceConfigFolder() const override;
|
virtual QString instanceConfigFolder() const 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 session) override;
|
||||||
|
|
||||||
virtual void cleanupAfterRun() override;
|
virtual void cleanupAfterRun() override;
|
||||||
|
|
||||||
@ -72,6 +72,6 @@ signals:
|
|||||||
void versionReloaded();
|
void versionReloaded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QStringList processMinecraftArgs(MojangAccountPtr account);
|
QStringList processMinecraftArgs(AuthSessionPtr account);
|
||||||
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
QDir reconstructAssets(std::shared_ptr<OneSixVersion> version);
|
||||||
};
|
};
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
#include "pathutils.h"
|
#include "pathutils.h"
|
||||||
#include <JlCompress.h>
|
#include <JlCompress.h>
|
||||||
|
|
||||||
OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent)
|
OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent)
|
||||||
: Task(parent), m_inst(inst), m_only_prepare(only_prepare)
|
: Task(parent), m_inst(inst)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +52,6 @@ void OneSixUpdate::executeTask()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_only_prepare)
|
|
||||||
{
|
|
||||||
prepareForLaunch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_inst->shouldUpdate())
|
if (m_inst->shouldUpdate())
|
||||||
{
|
{
|
||||||
// Get a pointer to the version object that corresponds to the instance's version.
|
// Get a pointer to the version object that corresponds to the instance's version.
|
||||||
@ -222,7 +216,7 @@ void OneSixUpdate::assetIndexFailed()
|
|||||||
|
|
||||||
void OneSixUpdate::assetsFinished()
|
void OneSixUpdate::assetsFinished()
|
||||||
{
|
{
|
||||||
prepareForLaunch();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OneSixUpdate::assetsFailed()
|
void OneSixUpdate::assetsFailed()
|
||||||
@ -330,43 +324,3 @@ void OneSixUpdate::jarlibFailed()
|
|||||||
emitFailed("Failed to download the following files:\n" + failed_all +
|
emitFailed("Failed to download the following files:\n" + failed_all +
|
||||||
"\n\nPlease try again.");
|
"\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
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0);
|
explicit OneSixUpdate(BaseInstance *inst, QObject *parent = 0);
|
||||||
virtual void executeTask();
|
virtual void executeTask();
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -49,9 +49,6 @@ slots:
|
|||||||
void assetsFinished();
|
void assetsFinished();
|
||||||
void assetsFailed();
|
void assetsFailed();
|
||||||
|
|
||||||
// extract the appropriate libraries
|
|
||||||
void prepareForLaunch();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetJobPtr specificVersionDownloadJob;
|
NetJobPtr specificVersionDownloadJob;
|
||||||
NetJobPtr jarlibDownloadJob;
|
NetJobPtr jarlibDownloadJob;
|
||||||
@ -59,5 +56,4 @@ private:
|
|||||||
// target version, determined during this task
|
// target version, determined during this task
|
||||||
std::shared_ptr<MinecraftVersion> targetVersion;
|
std::shared_ptr<MinecraftVersion> targetVersion;
|
||||||
BaseInstance *m_inst = nullptr;
|
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 <QJsonArray>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include <logger/QsLog.h>
|
#include <logger/QsLog.h>
|
||||||
|
|
||||||
@ -165,15 +166,26 @@ AccountStatus MojangAccount::accountStatus() const
|
|||||||
{
|
{
|
||||||
if (m_accessToken.isEmpty())
|
if (m_accessToken.isEmpty())
|
||||||
return NotVerified;
|
return NotVerified;
|
||||||
if (!m_online)
|
else
|
||||||
return Verified;
|
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)
|
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||||
return m_currentTask;
|
|
||||||
|
// 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())
|
if (password.isEmpty())
|
||||||
{
|
{
|
||||||
m_currentTask.reset(new RefreshTask(this));
|
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.reset(new AuthenticateTask(this, password));
|
||||||
}
|
}
|
||||||
|
m_currentTask->assignSession(session);
|
||||||
|
|
||||||
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
||||||
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
||||||
return m_currentTask;
|
return m_currentTask;
|
||||||
@ -189,24 +203,76 @@ std::shared_ptr<YggdrasilTask> MojangAccount::login(QString password)
|
|||||||
|
|
||||||
void MojangAccount::authSucceeded()
|
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();
|
m_currentTask.reset();
|
||||||
emit changed();
|
emit changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MojangAccount::authFailed(QString reason)
|
void MojangAccount::authFailed(QString reason)
|
||||||
{
|
{
|
||||||
|
auto session = m_currentTask->getAssignedSession();
|
||||||
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
// This is emitted when the yggdrasil tasks time out or are cancelled.
|
||||||
// -> we treat the error as no-op
|
// -> we treat the error as no-op
|
||||||
if (reason == "Yggdrasil task cancelled.")
|
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
|
else
|
||||||
{
|
{
|
||||||
m_online = false;
|
|
||||||
m_accessToken = QString();
|
m_accessToken = QString();
|
||||||
emit changed();
|
emit changed();
|
||||||
|
if (session)
|
||||||
|
{
|
||||||
|
session->status = AuthSession::RequiresPassword;
|
||||||
|
session->auth_server_online = true;
|
||||||
|
fillSession(session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_currentTask.reset();
|
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 <QMap>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "AuthSession.h"
|
||||||
|
|
||||||
class Task;
|
class Task;
|
||||||
class YggdrasilTask;
|
class YggdrasilTask;
|
||||||
@ -45,17 +46,10 @@ struct AccountProfile
|
|||||||
bool legacy;
|
bool legacy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct User
|
|
||||||
{
|
|
||||||
QString id;
|
|
||||||
QMultiMap<QString,QString> properties;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum AccountStatus
|
enum AccountStatus
|
||||||
{
|
{
|
||||||
NotVerified,
|
NotVerified,
|
||||||
Verified,
|
Verified
|
||||||
Online
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -95,12 +89,9 @@ public: /* manipulation */
|
|||||||
* Attempt to login. Empty password means we use the token.
|
* Attempt to login. Empty password means we use the token.
|
||||||
* If the attempt fails because we already are performing some task, it returns false.
|
* 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 */
|
public: /* queries */
|
||||||
const QString &username() const
|
const QString &username() const
|
||||||
{
|
{
|
||||||
@ -127,14 +118,6 @@ public: /* queries */
|
|||||||
return m_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)
|
//! Returns the currently selected profile (if none, returns nullptr)
|
||||||
const AccountProfile *currentProfile() const;
|
const AccountProfile *currentProfile() const;
|
||||||
|
|
||||||
@ -169,16 +152,17 @@ protected: /* variables */
|
|||||||
// the user structure, whatever it is.
|
// the user structure, whatever it is.
|
||||||
User m_user;
|
User m_user;
|
||||||
|
|
||||||
// true when the account is verified
|
|
||||||
bool m_online = false;
|
|
||||||
|
|
||||||
// current task we are executing here
|
// current task we are executing here
|
||||||
std::shared_ptr<YggdrasilTask> m_currentTask;
|
std::shared_ptr<YggdrasilTask> m_currentTask;
|
||||||
|
|
||||||
private slots:
|
private
|
||||||
|
slots:
|
||||||
void authSucceeded();
|
void authSucceeded();
|
||||||
void authFailed(QString reason);
|
void authFailed(QString reason);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void fillSession(AuthSessionPtr session);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
friend class YggdrasilTask;
|
friend class YggdrasilTask;
|
||||||
friend class AuthenticateTask;
|
friend class AuthenticateTask;
|
||||||
|
@ -35,6 +35,21 @@ class YggdrasilTask : public Task
|
|||||||
public:
|
public:
|
||||||
explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
|
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.
|
* Class describing a Yggdrasil error response.
|
||||||
*/
|
*/
|
||||||
@ -117,4 +132,6 @@ protected:
|
|||||||
|
|
||||||
const int timeout_max = 10000;
|
const int timeout_max = 10000;
|
||||||
const int time_step = 50;
|
const int time_step = 50;
|
||||||
|
|
||||||
|
AuthSessionPtr m_session;
|
||||||
};
|
};
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
|
|
||||||
#include "logger/QsLog.h"
|
#include "logger/QsLog.h"
|
||||||
|
|
||||||
RefreshTask::RefreshTask(MojangAccount *account, QObject *parent)
|
RefreshTask::RefreshTask(MojangAccount *account) : YggdrasilTask(account)
|
||||||
: YggdrasilTask(account, parent)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +125,6 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
|||||||
m_account->m_user = u;
|
m_account->m_user = u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We've made it through the minefield of possible errors. Return true to indicate that
|
// We've made it through the minefield of possible errors. Return true to indicate that
|
||||||
// we've succeeded.
|
// we've succeeded.
|
||||||
QLOG_DEBUG() << "Finished reading refresh response.";
|
QLOG_DEBUG() << "Finished reading refresh response.";
|
||||||
|
@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
RefreshTask(MojangAccount * account, QObject *parent = 0);
|
RefreshTask(MojangAccount * account);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QJsonObject getRequestContent() const;
|
virtual QJsonObject getRequestContent() const;
|
||||||
@ -41,3 +41,4 @@ protected:
|
|||||||
|
|
||||||
QString getStateMessage(const YggdrasilTask::State state) const;
|
QString getStateMessage(const YggdrasilTask::State state) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ void NetJob::partSucceeded(int index)
|
|||||||
num_succeeded++;
|
num_succeeded++;
|
||||||
QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/"
|
QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/"
|
||||||
<< downloads.size();
|
<< downloads.size();
|
||||||
emit filesProgress(num_succeeded, num_failed, downloads.size());
|
|
||||||
|
|
||||||
if (num_failed + num_succeeded == 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 << ")";
|
QLOG_ERROR() << "Part" << index << "failed 3 times (" << downloads[index]->m_url << ")";
|
||||||
num_failed++;
|
num_failed++;
|
||||||
emit filesProgress(num_succeeded, num_failed, downloads.size());
|
|
||||||
if (num_failed + num_succeeded == downloads.size())
|
if (num_failed + num_succeeded == downloads.size())
|
||||||
{
|
{
|
||||||
QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
|
QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed.";
|
||||||
|
@ -84,7 +84,6 @@ public:
|
|||||||
{
|
{
|
||||||
return m_job_name;
|
return m_job_name;
|
||||||
}
|
}
|
||||||
;
|
|
||||||
virtual bool isRunning() const
|
virtual bool isRunning() const
|
||||||
{
|
{
|
||||||
return m_running;
|
return m_running;
|
||||||
@ -94,7 +93,6 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void started();
|
void started();
|
||||||
void progress(qint64 current, qint64 total);
|
void progress(qint64 current, qint64 total);
|
||||||
void filesProgress(int, int, int);
|
|
||||||
void succeeded();
|
void succeeded();
|
||||||
void failed();
|
void failed();
|
||||||
public
|
public
|
||||||
|
Loading…
Reference in New Issue
Block a user