aboutsummaryrefslogtreecommitdiff
path: root/launcher/minecraft
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/minecraft')
-rw-r--r--launcher/minecraft/AssetsUtils.cpp4
-rw-r--r--launcher/minecraft/AssetsUtils.h4
-rw-r--r--launcher/minecraft/Component.cpp18
-rw-r--r--launcher/minecraft/ComponentUpdateTask.cpp33
-rw-r--r--launcher/minecraft/Library.cpp5
-rw-r--r--launcher/minecraft/Library.h2
-rw-r--r--launcher/minecraft/Library_test.cpp2
-rw-r--r--launcher/minecraft/MinecraftInstance.cpp44
-rw-r--r--launcher/minecraft/MinecraftInstance.h2
-rw-r--r--launcher/minecraft/MinecraftLoadAndCheck.h2
-rw-r--r--launcher/minecraft/MinecraftUpdate.cpp1
-rw-r--r--launcher/minecraft/PackProfile.cpp23
-rw-r--r--launcher/minecraft/PackProfile.h2
-rw-r--r--launcher/minecraft/PackProfile_p.h2
-rw-r--r--launcher/minecraft/VersionFilterData.cpp1
-rw-r--r--launcher/minecraft/VersionFilterData.h2
-rw-r--r--launcher/minecraft/auth/AccountData.cpp45
-rw-r--r--launcher/minecraft/auth/AccountData.h7
-rw-r--r--launcher/minecraft/auth/AccountList.cpp167
-rw-r--r--launcher/minecraft/auth/AccountList.h21
-rw-r--r--launcher/minecraft/auth/AccountTask.cpp4
-rw-r--r--launcher/minecraft/auth/AuthSession.h4
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp99
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.h29
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.cpp466
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.h13
-rw-r--r--launcher/minecraft/auth/flows/AuthRequest.cpp6
-rw-r--r--launcher/minecraft/auth/flows/AuthRequest.h1
-rw-r--r--launcher/minecraft/auth/flows/MSAInteractive.cpp8
-rw-r--r--launcher/minecraft/auth/flows/MSAInteractive.h5
-rw-r--r--launcher/minecraft/auth/flows/MSASilent.h5
-rw-r--r--launcher/minecraft/auth/flows/MojangLogin.cpp6
-rw-r--r--launcher/minecraft/auth/flows/MojangLogin.h6
-rw-r--r--launcher/minecraft/auth/flows/MojangRefresh.cpp5
-rw-r--r--launcher/minecraft/auth/flows/MojangRefresh.h2
-rw-r--r--launcher/minecraft/auth/flows/Parsers.cpp316
-rw-r--r--launcher/minecraft/auth/flows/Parsers.h19
-rw-r--r--launcher/minecraft/auth/flows/Yggdrasil.cpp72
-rw-r--r--launcher/minecraft/auth/flows/Yggdrasil.h6
-rw-r--r--launcher/minecraft/launch/ClaimAccount.cpp6
-rw-r--r--launcher/minecraft/launch/LauncherPartLaunch.cpp15
-rw-r--r--launcher/minecraft/launch/VerifyJavaInstall.cpp11
-rw-r--r--launcher/minecraft/legacy/LegacyInstance.cpp2
-rw-r--r--launcher/minecraft/legacy/LegacyInstance.h2
-rw-r--r--launcher/minecraft/services/CapeChange.cpp12
-rw-r--r--launcher/minecraft/services/CapeChange.h3
-rw-r--r--launcher/minecraft/services/SkinDelete.cpp8
-rw-r--r--launcher/minecraft/services/SkinDelete.h5
-rw-r--r--launcher/minecraft/services/SkinUpload.cpp8
-rw-r--r--launcher/minecraft/services/SkinUpload.h4
-rw-r--r--launcher/minecraft/update/AssetUpdateTask.cpp12
-rw-r--r--launcher/minecraft/update/AssetUpdateTask.h2
-rw-r--r--launcher/minecraft/update/FMLLibrariesTask.cpp14
-rw-r--r--launcher/minecraft/update/FMLLibrariesTask.h2
-rw-r--r--launcher/minecraft/update/LibrariesTask.cpp8
-rw-r--r--launcher/minecraft/update/LibrariesTask.h2
56 files changed, 952 insertions, 623 deletions
diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp
index c01733b6..1c65a212 100644
--- a/launcher/minecraft/AssetsUtils.cpp
+++ b/launcher/minecraft/AssetsUtils.cpp
@@ -284,7 +284,7 @@ bool reconstructAssets(QString assetsId, QString resourcesFolder)
}
-NetActionPtr AssetObject::getDownloadAction()
+NetAction::Ptr AssetObject::getDownloadAction()
{
QFileInfo objectFile(getLocalPath());
if ((!objectFile.isFile()) || (objectFile.size() != size))
@@ -316,7 +316,7 @@ QString AssetObject::getRelPath()
return hash.left(2) + "/" + hash;
}
-NetJobPtr AssetsIndex::getDownloadJob()
+NetJob::Ptr AssetsIndex::getDownloadJob()
{
auto job = new NetJob(QObject::tr("Assets for %1").arg(id));
for (auto &object : objects.values())
diff --git a/launcher/minecraft/AssetsUtils.h b/launcher/minecraft/AssetsUtils.h
index 32e57060..3dbf19ed 100644
--- a/launcher/minecraft/AssetsUtils.h
+++ b/launcher/minecraft/AssetsUtils.h
@@ -25,7 +25,7 @@ struct AssetObject
QString getRelPath();
QUrl getUrl();
QString getLocalPath();
- NetActionPtr getDownloadAction();
+ NetAction::Ptr getDownloadAction();
QString hash;
qint64 size;
@@ -33,7 +33,7 @@ struct AssetObject
struct AssetsIndex
{
- NetJobPtr getDownloadJob();
+ NetJob::Ptr getDownloadJob();
QString id;
QMap<QString, AssetObject> objects;
diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp
index 92821065..c7dd5e36 100644
--- a/launcher/minecraft/Component.cpp
+++ b/launcher/minecraft/Component.cpp
@@ -1,14 +1,16 @@
#include <meta/VersionList.h>
#include <meta/Index.h>
-#include <Env.h>
#include "Component.h"
+#include <QSaveFile>
+
#include "meta/Version.h"
#include "VersionFile.h"
#include "minecraft/PackProfile.h"
-#include <FileSystem.h>
-#include <QSaveFile>
+#include "FileSystem.h"
#include "OneSixVersionFormat.h"
+#include "Application.h"
+
#include <assert.h>
Component::Component(PackProfile * parent, const QString& uid)
@@ -85,9 +87,9 @@ std::shared_ptr<class VersionFile> Component::getVersionFile() const
std::shared_ptr<class Meta::VersionList> Component::getVersionList() const
{
// FIXME: what if the metadata index isn't loaded yet?
- if(ENV.metadataIndex()->hasUid(m_uid))
+ if(APPLICATION->metadataIndex()->hasUid(m_uid))
{
- return ENV.metadataIndex()->get(m_uid);
+ return APPLICATION->metadataIndex()->get(m_uid);
}
return nullptr;
}
@@ -192,7 +194,7 @@ bool Component::isRevertible()
{
if (isCustom())
{
- if(ENV.metadataIndex()->hasUid(m_uid))
+ if(APPLICATION->metadataIndex()->hasUid(m_uid))
{
return true;
}
@@ -266,7 +268,7 @@ void Component::setVersion(const QString& version)
// we don't have a file, therefore we are loaded with metadata
m_cachedVersion = version;
// see if the meta version is loaded
- auto metaVersion = ENV.metadataIndex()->get(m_uid, version);
+ auto metaVersion = APPLICATION->metadataIndex()->get(m_uid, version);
if(metaVersion->isLoaded())
{
// if yes, we can continue with that.
@@ -350,7 +352,7 @@ bool Component::revert()
m_file.reset();
// check local cache for metadata...
- auto version = ENV.metadataIndex()->get(m_uid, m_version);
+ auto version = APPLICATION->metadataIndex()->get(m_uid, m_version);
if(version->isLoaded())
{
m_metaVersion = version;
diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp
index 241d9a49..8bc05a1b 100644
--- a/launcher/minecraft/ComponentUpdateTask.cpp
+++ b/launcher/minecraft/ComponentUpdateTask.cpp
@@ -3,16 +3,17 @@
#include "PackProfile_p.h"
#include "PackProfile.h"
#include "Component.h"
-#include <Env.h>
-#include <meta/Index.h>
-#include <meta/VersionList.h>
-#include <meta/Version.h>
+#include "meta/Index.h"
+#include "meta/VersionList.h"
+#include "meta/Version.h"
#include "ComponentUpdateTask_p.h"
-#include <cassert>
-#include <Version.h>
+#include "cassert"
+#include "Version.h"
#include "net/Mode.h"
#include "OneSixVersionFormat.h"
+#include "Application.h"
+
/*
* This is responsible for loading the components of a component list AND resolving dependency issues between them
*/
@@ -68,7 +69,7 @@ LoadResult composeLoadResult(LoadResult a, LoadResult b)
return a;
}
-static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
+static LoadResult loadComponent(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode)
{
if(component->m_loaded)
{
@@ -102,7 +103,7 @@ static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>
}
else
{
- auto metaVersion = ENV.metadataIndex()->get(component->m_uid, component->m_version);
+ auto metaVersion = APPLICATION->metadataIndex()->get(component->m_uid, component->m_version);
component->m_metaVersion = metaVersion;
if(metaVersion->isLoaded())
{
@@ -126,7 +127,7 @@ static LoadResult loadComponent(ComponentPtr component, shared_qobject_ptr<Task>
// FIXME: dead code. determine if this can still be useful?
/*
-static LoadResult loadPackProfile(ComponentPtr component, shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
+static LoadResult loadPackProfile(ComponentPtr component, Task::Ptr& loadTask, Net::Mode netmode)
{
if(component->m_loaded)
{
@@ -135,7 +136,7 @@ static LoadResult loadPackProfile(ComponentPtr component, shared_qobject_ptr<Tas
}
LoadResult result = LoadResult::Failed;
- auto metaList = ENV.metadataIndex()->get(component->m_uid);
+ auto metaList = APPLICATION->metadataIndex()->get(component->m_uid);
if(metaList->isLoaded())
{
component->m_loaded = true;
@@ -151,16 +152,16 @@ static LoadResult loadPackProfile(ComponentPtr component, shared_qobject_ptr<Tas
}
*/
-static LoadResult loadIndex(shared_qobject_ptr<Task>& loadTask, Net::Mode netmode)
+static LoadResult loadIndex(Task::Ptr& loadTask, Net::Mode netmode)
{
// FIXME: DECIDE. do we want to run the update task anyway?
- if(ENV.metadataIndex()->isLoaded())
+ if(APPLICATION->metadataIndex()->isLoaded())
{
qDebug() << "Index is already loaded";
return LoadResult::LoadedLocal;
}
- ENV.metadataIndex()->load(netmode);
- loadTask = ENV.metadataIndex()->getCurrentTask();
+ APPLICATION->metadataIndex()->load(netmode);
+ loadTask = APPLICATION->metadataIndex()->getCurrentTask();
if(loadTask)
{
return LoadResult::RequiresRemote;
@@ -179,7 +180,7 @@ void ComponentUpdateTask::loadComponents()
// load the main index (it is needed to determine if components can revert)
{
// FIXME: tear out as a method? or lambda?
- shared_qobject_ptr<Task> indexLoadTask;
+ Task::Ptr indexLoadTask;
auto singleResult = loadIndex(indexLoadTask, d->netmode);
result = composeLoadResult(result, singleResult);
if(indexLoadTask)
@@ -202,7 +203,7 @@ void ComponentUpdateTask::loadComponents()
// load all the components OR their lists...
for (auto component: d->m_list->d->components)
{
- shared_qobject_ptr<Task> loadTask;
+ Task::Ptr loadTask;
LoadResult singleResult;
RemoteLoadStatus::Type loadType;
// FIXME: to do this right, we need to load the lists and decide on which versions to use during dependency resolution. For now, ignore all that...
diff --git a/launcher/minecraft/Library.cpp b/launcher/minecraft/Library.cpp
index f2293679..c7982705 100644
--- a/launcher/minecraft/Library.cpp
+++ b/launcher/minecraft/Library.cpp
@@ -3,7 +3,6 @@
#include <net/Download.h>
#include <net/ChecksumValidator.h>
-#include <Env.h>
#include <FileSystem.h>
#include <BuildConfig.h>
@@ -45,14 +44,14 @@ void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& na
}
}
-QList< std::shared_ptr< NetAction > > Library::getDownloads(
+QList<NetAction::Ptr> Library::getDownloads(
OpSys system,
class HttpMetaCache* cache,
QStringList& failedLocalFiles,
const QString & overridePath
) const
{
- QList<NetActionPtr> out;
+ QList<NetAction::Ptr> out;
bool stale = isAlwaysStale();
bool local = isLocal();
diff --git a/launcher/minecraft/Library.h b/launcher/minecraft/Library.h
index 119b4a86..41d41a8b 100644
--- a/launcher/minecraft/Library.h
+++ b/launcher/minecraft/Library.h
@@ -152,7 +152,7 @@ public: /* methods */
bool isForge() const;
// Get a list of downloads for this library
- QList<NetActionPtr> getDownloads(OpSys system, class HttpMetaCache * cache,
+ QList<NetAction::Ptr> getDownloads(OpSys system, class HttpMetaCache * cache,
QStringList & failedLocalFiles, const QString & overridePath) const;
private: /* methods */
diff --git a/launcher/minecraft/Library_test.cpp b/launcher/minecraft/Library_test.cpp
index 75bb4db1..47531ad6 100644
--- a/launcher/minecraft/Library_test.cpp
+++ b/launcher/minecraft/Library_test.cpp
@@ -55,7 +55,7 @@ slots:
auto downloads = test.getDownloads(currentSystem, cache.get(), failedFiles, QString());
QCOMPARE(downloads.size(), 1);
QCOMPARE(failedFiles, {});
- NetActionPtr dl = downloads[0];
+ NetAction::Ptr dl = downloads[0];
QCOMPARE(dl->m_url, QUrl("file://foo/bar/test/package/testname/testversion/testname-testversion.jar"));
}
void test_legacy_url_local_broken()
diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp
index 2982a340..4c16e572 100644
--- a/launcher/minecraft/MinecraftInstance.cpp
+++ b/launcher/minecraft/MinecraftInstance.cpp
@@ -1,15 +1,16 @@
#include "MinecraftInstance.h"
-#include <minecraft/launch/CreateGameFolders.h>
-#include <minecraft/launch/ExtractNatives.h>
-#include <minecraft/launch/PrintInstanceInfo.h>
-#include <settings/Setting.h>
+#include "minecraft/launch/CreateGameFolders.h"
+#include "minecraft/launch/ExtractNatives.h"
+#include "minecraft/launch/PrintInstanceInfo.h"
+#include "settings/Setting.h"
#include "settings/SettingsObject.h"
-#include "Env.h"
-#include <MMCStrings.h>
-#include <pathmatcher/RegexpMatcher.h>
-#include <pathmatcher/MultiMatcher.h>
-#include <FileSystem.h>
-#include <java/JavaVersion.h>
+#include "Application.h"
+
+#include "MMCStrings.h"
+#include "pathmatcher/RegexpMatcher.h"
+#include "pathmatcher/MultiMatcher.h"
+#include "FileSystem.h"
+#include "java/JavaVersion.h"
#include "MMCTime.h"
#include "launch/LaunchTask.h"
@@ -18,6 +19,8 @@
#include "launch/steps/Update.h"
#include "launch/steps/PreLaunchCommand.h"
#include "launch/steps/TextPrint.h"
+#include "launch/steps/CheckJava.h"
+
#include "minecraft/launch/LauncherPartLaunch.h"
#include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
@@ -25,25 +28,26 @@
#include "minecraft/launch/ReconstructAssets.h"
#include "minecraft/launch/ScanModFolders.h"
#include "minecraft/launch/VerifyJavaInstall.h"
-#include "java/launch/CheckJava.h"
+
#include "java/JavaUtils.h"
+
#include "meta/Index.h"
#include "meta/VersionList.h"
+#include "icons/IconList.h"
+
#include "mod/ModFolderModel.h"
#include "mod/ResourcePackFolderModel.h"
#include "mod/TexturePackFolderModel.h"
-#include "WorldList.h"
-#include "icons/IIconList.h"
+#include "WorldList.h"
-#include <QCoreApplication>
#include "PackProfile.h"
#include "AssetsUtils.h"
#include "MinecraftUpdate.h"
#include "MinecraftLoadAndCheck.h"
-#include <minecraft/gameoptions/GameOptions.h>
-#include <minecraft/update/FoldersTask.h>
+#include "minecraft/gameoptions/GameOptions.h"
+#include "minecraft/update/FoldersTask.h"
#define IBUS "@im=ibus"
@@ -794,17 +798,17 @@ QString MinecraftInstance::getStatusbarDescription()
return description;
}
-shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
+Task::Ptr MinecraftInstance::createUpdateTask(Net::Mode mode)
{
switch (mode)
{
case Net::Mode::Offline:
{
- return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
+ return Task::Ptr(new MinecraftLoadAndCheck(this));
}
case Net::Mode::Online:
{
- return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
+ return Task::Ptr(new MinecraftUpdate(this));
}
}
return nullptr;
@@ -816,7 +820,7 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
auto pptr = process.get();
- ENV.icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
+ APPLICATION->icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
// print a header
{
diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h
index b11270e6..bb45f37b 100644
--- a/launcher/minecraft/MinecraftInstance.h
+++ b/launcher/minecraft/MinecraftInstance.h
@@ -77,7 +77,7 @@ public:
std::shared_ptr<GameOptions> gameOptionsModel() const;
////// Launch stuff //////
- shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
+ Task::Ptr createUpdateTask(Net::Mode mode) override;
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
QStringList extraArguments() const override;
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
diff --git a/launcher/minecraft/MinecraftLoadAndCheck.h b/launcher/minecraft/MinecraftLoadAndCheck.h
index 3435b52b..bfeae46b 100644
--- a/launcher/minecraft/MinecraftLoadAndCheck.h
+++ b/launcher/minecraft/MinecraftLoadAndCheck.h
@@ -41,7 +41,7 @@ private slots:
private:
MinecraftInstance *m_inst = nullptr;
- shared_qobject_ptr<Task> m_task;
+ Task::Ptr m_task;
QString m_preFailure;
QString m_fail_reason;
};
diff --git a/launcher/minecraft/MinecraftUpdate.cpp b/launcher/minecraft/MinecraftUpdate.cpp
index 8f1565b0..32e9cbb6 100644
--- a/launcher/minecraft/MinecraftUpdate.cpp
+++ b/launcher/minecraft/MinecraftUpdate.cpp
@@ -13,7 +13,6 @@
* limitations under the License.
*/
-#include "Env.h"
#include "MinecraftUpdate.h"
#include "MinecraftInstance.h"
diff --git a/launcher/minecraft/PackProfile.cpp b/launcher/minecraft/PackProfile.cpp
index f6918116..59a8f133 100644
--- a/launcher/minecraft/PackProfile.cpp
+++ b/launcher/minecraft/PackProfile.cpp
@@ -20,22 +20,23 @@
#include <QJsonDocument>
#include <QJsonArray>
#include <QDebug>
-
-#include "Exception.h"
-#include <minecraft/OneSixVersionFormat.h>
-#include <FileSystem.h>
#include <QSaveFile>
-#include <Env.h>
-#include <meta/Index.h>
-#include <minecraft/MinecraftInstance.h>
#include <QUuid>
#include <QTimer>
-#include <Json.h>
+
+#include "Exception.h"
+#include "minecraft/OneSixVersionFormat.h"
+#include "FileSystem.h"
+#include "meta/Index.h"
+#include "minecraft/MinecraftInstance.h"
+#include "Json.h"
#include "PackProfile.h"
#include "PackProfile_p.h"
#include "ComponentUpdateTask.h"
+#include "Application.h"
+
PackProfile::PackProfile(MinecraftInstance * instance)
: QAbstractListModel()
{
@@ -339,7 +340,7 @@ void PackProfile::reload(Net::Mode netmode)
}
}
-shared_qobject_ptr<Task> PackProfile::getCurrentTask()
+Task::Ptr PackProfile::getCurrentTask()
{
return d->m_updateTask;
}
@@ -481,7 +482,7 @@ bool PackProfile::migratePreComponentConfig()
}
else if(!intendedVersion.isEmpty())
{
- auto metaVersion = ENV.metadataIndex()->get(uid, intendedVersion);
+ auto metaVersion = APPLICATION->metadataIndex()->get(uid, intendedVersion);
component = new Component(this, metaVersion);
}
else
@@ -546,7 +547,7 @@ bool PackProfile::migratePreComponentConfig()
auto patchVersion = d->getOldConfigVersion(uid);
if(!patchVersion.isEmpty() && !loadedComponents.contains(uid))
{
- auto patch = new Component(this, ENV.metadataIndex()->get(uid, patchVersion));
+ auto patch = new Component(this, APPLICATION->metadataIndex()->get(uid, patchVersion));
patch->setOrder(order);
loadedComponents[uid] = patch;
}
diff --git a/launcher/minecraft/PackProfile.h b/launcher/minecraft/PackProfile.h
index 3d6cc6c3..f30deb5a 100644
--- a/launcher/minecraft/PackProfile.h
+++ b/launcher/minecraft/PackProfile.h
@@ -85,7 +85,7 @@ public:
void resolve(Net::Mode netmode);
/// get current running task...
- shared_qobject_ptr<Task> getCurrentTask();
+ Task::Ptr getCurrentTask();
std::shared_ptr<LaunchProfile> getProfile() const;
diff --git a/launcher/minecraft/PackProfile_p.h b/launcher/minecraft/PackProfile_p.h
index 6cd2a4e5..fce921bb 100644
--- a/launcher/minecraft/PackProfile_p.h
+++ b/launcher/minecraft/PackProfile_p.h
@@ -35,7 +35,7 @@ struct PackProfileData
ComponentIndex componentIndex;
bool dirty = false;
QTimer m_saveTimer;
- shared_qobject_ptr<Task> m_updateTask;
+ Task::Ptr m_updateTask;
bool loaded = false;
bool interactionDisabled = true;
};
diff --git a/launcher/minecraft/VersionFilterData.cpp b/launcher/minecraft/VersionFilterData.cpp
index 38e7b60c..c286d266 100644
--- a/launcher/minecraft/VersionFilterData.cpp
+++ b/launcher/minecraft/VersionFilterData.cpp
@@ -68,4 +68,5 @@ VersionFilterData::VersionFilterData()
java8BeginsDate = timeFromS3Time("2017-03-30T09:32:19+00:00");
java16BeginsDate = timeFromS3Time("2021-05-12T11:19:15+00:00");
+ java17BeginsDate = timeFromS3Time("2021-11-16T17:04:48+00:00");
}
diff --git a/launcher/minecraft/VersionFilterData.h b/launcher/minecraft/VersionFilterData.h
index 79756c3f..13445a51 100644
--- a/launcher/minecraft/VersionFilterData.h
+++ b/launcher/minecraft/VersionFilterData.h
@@ -25,5 +25,7 @@ struct VersionFilterData
QDateTime java8BeginsDate;
// release data of first version to require Java 16 (21w19a)
QDateTime java16BeginsDate;
+ // release data of first version to require Java 17 (1.18 Pre Release 2)
+ QDateTime java17BeginsDate;
};
extern VersionFilterData g_VersionFilterData;
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 5c6de9df..8aa4e37f 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -207,6 +207,35 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token
return out;
}
+void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) {
+ if(p.validity == Katabasis::Validity::None) {
+ return;
+ }
+ QJsonObject out;
+ out["ownsMinecraft"] = QJsonValue(p.ownsMinecraft);
+ out["canPlayMinecraft"] = QJsonValue(p.canPlayMinecraft);
+ parent["entitlement"] = out;
+}
+
+bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out) {
+ auto entitlementObject = parent.value("entitlement").toObject();
+ if(entitlementObject.isEmpty()) {
+ return false;
+ }
+ {
+ auto ownsMinecraftV = entitlementObject.value("ownsMinecraft");
+ auto canPlayMinecraftV = entitlementObject.value("canPlayMinecraft");
+ if(!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) {
+ qWarning() << "mandatory attributes are missing or of unexpected type";
+ return false;
+ }
+ out.canPlayMinecraft = canPlayMinecraftV.toBool(false);
+ out.ownsMinecraft = ownsMinecraftV.toBool(false);
+ out.validity = Katabasis::Validity::Assumed;
+ }
+ return true;
+}
+
}
bool AccountData::resumeStateFromV2(QJsonObject data) {
@@ -304,9 +333,15 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
yggdrasilToken = tokenFromJSONV3(data, "ygg");
minecraftProfile = profileFromJSONV3(data, "profile");
+ if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
+ if(minecraftProfile.validity != Katabasis::Validity::None) {
+ minecraftEntitlement.canPlayMinecraft = true;
+ minecraftEntitlement.ownsMinecraft = true;
+ minecraftEntitlement.validity = Katabasis::Validity::Assumed;
+ }
+ }
validity_ = minecraftProfile.validity;
-
return true;
}
@@ -331,6 +366,7 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
+ entitlementToJSONV3(output, minecraftEntitlement);
return output;
}
@@ -378,7 +414,12 @@ QString AccountData::profileId() const {
}
QString AccountData::profileName() const {
- return minecraftProfile.name;
+ if(minecraftProfile.name.size() == 0) {
+ return QObject::tr("No profile (%1)").arg(accountDisplayString());
+ }
+ else {
+ return minecraftProfile.name;
+ }
}
QString AccountData::accountDisplayString() const {
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index cf58fb76..09cd2c73 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -21,6 +21,12 @@ struct Cape {
QByteArray data;
};
+struct MinecraftEntitlement {
+ bool ownsMinecraft = false;
+ bool canPlayMinecraft = false;
+ Katabasis::Validity validity = Katabasis::Validity::None;
+};
+
struct MinecraftProfile {
QString id;
QString name;
@@ -69,5 +75,6 @@ struct AccountData {
Katabasis::Token yggdrasilToken;
MinecraftProfile minecraftProfile;
+ MinecraftEntitlement minecraftEntitlement;
Katabasis::Validity validity_ = Katabasis::Validity::None;
};
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index a76cac55..d7537345 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -37,6 +37,8 @@ enum AccountListVersion {
AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { }
+AccountList::~AccountList() noexcept {}
+
int AccountList::findAccountByProfileId(const QString& profileId) const {
for (int i = 0; i < count(); i++) {
MinecraftAccountPtr account = at(i);
@@ -62,28 +64,41 @@ const MinecraftAccountPtr AccountList::at(int i) const
return MinecraftAccountPtr(m_accounts.at(i));
}
+QStringList AccountList::profileNames() const {
+ QStringList out;
+ for(auto & account: m_accounts) {
+ auto profileName = account->profileName();
+ if(profileName.isEmpty()) {
+ continue;
+ }
+ out.append(profileName);
+ }
+ return out;
+}
+
void AccountList::addAccount(const MinecraftAccountPtr account)
{
- // We only ever want accounts with valid profiles.
- // Keeping profile-less accounts is pointless and serves no purpose.
auto profileId = account->profileId();
- if(!profileId.size()) {
- return;
- }
-
- // override/replace existing account with the same profileId
- auto existingAccount = findAccountByProfileId(profileId);
- if(existingAccount != -1) {
- m_accounts[existingAccount] = account;
- emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1));
- onListChanged();
- return;
+ if(profileId.size()) {
+ // override/replace existing account with the same profileId
+ auto existingAccount = findAccountByProfileId(profileId);
+ if(existingAccount != -1) {
+ MinecraftAccountPtr existingAccountPtr = m_accounts[existingAccount];
+ m_accounts[existingAccount] = account;
+ if(m_defaultAccount == existingAccountPtr) {
+ m_defaultAccount = account;
+ }
+ emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1));
+ onListChanged();
+ return;
+ }
}
- // if we don't have this porfileId yet, add the account to the end
+ // if we don't have this profileId yet, add the account to the end
int row = m_accounts.count();
beginInsertRows(QModelIndex(), row, row);
- connect(account.get(), SIGNAL(changed()), SLOT(accountChanged()));
+ connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged);
+ connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged);
m_accounts.append(account);
endInsertRows();
onListChanged();
@@ -95,10 +110,10 @@ void AccountList::removeAccount(QModelIndex index)
if(index.isValid() && row >= 0 && row < m_accounts.size())
{
auto & account = m_accounts[row];
- if(account == m_activeAccount)
+ if(account == m_defaultAccount)
{
- m_activeAccount = nullptr;
- onActiveChanged();
+ m_defaultAccount = nullptr;
+ onDefaultAccountChanged();
}
beginRemoveRows(QModelIndex(), row, row);
m_accounts.removeAt(index.row());
@@ -107,54 +122,54 @@ void AccountList::removeAccount(QModelIndex index)
}
}
-MinecraftAccountPtr AccountList::activeAccount() const
+MinecraftAccountPtr AccountList::defaultAccount() const
{
- return m_activeAccount;
+ return m_defaultAccount;
}
-void AccountList::setActiveAccount(const QString &profileId)
+void AccountList::setDefaultAccount(MinecraftAccountPtr newAccount)
{
- if (profileId.isEmpty() && m_activeAccount)
+ if (!newAccount && m_defaultAccount)
{
int idx = 0;
- auto prevActiveAcc = m_activeAccount;
- m_activeAccount = nullptr;
+ auto previousDefaultAccount = m_defaultAccount;
+ m_defaultAccount = nullptr;
for (MinecraftAccountPtr account : m_accounts)
{
- if (account == prevActiveAcc)
+ if (account == previousDefaultAccount)
{
- emit dataChanged(index(idx), index(idx));
+ emit dataChanged(index(idx), index(idx, columnCount(QModelIndex()) - 1));
}
idx ++;
}
- onActiveChanged();
+ onDefaultAccountChanged();
}
else
{
- auto currentActiveAccount = m_activeAccount;
- int currentActiveAccountIdx = -1;
- auto newActiveAccount = m_activeAccount;
- int newActiveAccountIdx = -1;
+ auto currentDefaultAccount = m_defaultAccount;
+ int currentDefaultAccountIdx = -1;
+ auto newDefaultAccount = m_defaultAccount;
+ int newDefaultAccountIdx = -1;
int idx = 0;
for (MinecraftAccountPtr account : m_accounts)
{
- if (account->profileId() == profileId)
+ if (account == newAccount)
{
- newActiveAccount = account;
- newActiveAccountIdx = idx;
+ newDefaultAccount = account;
+ newDefaultAccountIdx = idx;
}
- if(currentActiveAccount == account)
+ if(currentDefaultAccount == account)
{
- currentActiveAccountIdx = idx;
+ currentDefaultAccountIdx = idx;
}
idx++;
}
- if(currentActiveAccount != newActiveAccount)
+ if(currentDefaultAccount != newDefaultAccount)
{
- emit dataChanged(index(currentActiveAccountIdx), index(currentActiveAccountIdx));
- emit dataChanged(index(newActiveAccountIdx), index(newActiveAccountIdx));
- m_activeAccount = newActiveAccount;
- onActiveChanged();
+ emit dataChanged(index(currentDefaultAccountIdx), index(currentDefaultAccountIdx, columnCount(QModelIndex()) - 1));
+ emit dataChanged(index(newDefaultAccountIdx), index(newDefaultAccountIdx, columnCount(QModelIndex()) - 1));
+ m_defaultAccount = newDefaultAccount;
+ onDefaultAccountChanged();
}
}
}
@@ -165,6 +180,23 @@ void AccountList::accountChanged()
onListChanged();
}
+void AccountList::accountActivityChanged(bool active)
+{
+ MinecraftAccount *account = qobject_cast<MinecraftAccount *>(sender());
+ bool found = false;
+ for (int i = 0; i < count(); i++) {
+ if (at(i).get() == account) {
+ emit dataChanged(index(i), index(i, columnCount(QModelIndex()) - 1));
+ found = true;
+ break;
+ }
+ }
+ if(found) {
+ emit listActivityChanged();
+ }
+}
+
+
void AccountList::onListChanged()
{
if (m_autosave)
@@ -174,12 +206,12 @@ void AccountList::onListChanged()
emit listChanged();
}
-void AccountList::onActiveChanged()
+void AccountList::onDefaultAccountChanged()
{
if (m_autosave)
saveList();
- emit activeAccountChanged();
+ emit defaultAccountChanged();
}
int AccountList::count() const
@@ -211,6 +243,16 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
return typeStr;
}
+ case StatusColumn: {
+ if(account->isActive()) {
+ return tr("Working", "Account status");
+ }
+ if(account->isExpired()) {
+ return tr("Expired", "Account status");
+ }
+ return tr("Ready", "Account status");
+ }
+
case ProfileNameColumn: {
return account->profileName();
}
@@ -235,13 +277,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
return account->accountDisplayString();
case PointerRole:
- return qVariantFromValue(account);
+ return QVariant::fromValue(account);
case Qt::CheckStateRole:
switch (index.column())
{
case NameColumn:
- return account == m_activeAccount ? Qt::Checked : Qt::Unchecked;
+ return account == m_defaultAccount ? Qt::Checked : Qt::Unchecked;
}
default:
@@ -260,6 +302,8 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
return tr("Account");
case TypeColumn:
return tr("Type");
+ case StatusColumn:
+ return tr("Status");
case MigrationColumn:
return tr("Can Migrate?");
case ProfileNameColumn:
@@ -275,6 +319,8 @@ QVariant AccountList::headerData(int section, Qt::Orientation orientation, int r
return tr("User name of the account.");
case TypeColumn:
return tr("Type of the account - Mojang or MSA.");
+ case StatusColumn:
+ return tr("Current status of the account.");
case MigrationColumn:
return tr("Can this account migrate to Microsoft account?");
case ProfileNameColumn:
@@ -309,9 +355,9 @@ Qt::ItemFlags AccountList::flags(const QModelIndex &index) const
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
-bool AccountList::setData(const QModelIndex &index, const QVariant &value, int role)
+bool AccountList::setData(const QModelIndex &idx, const QVariant &value, int role)
{
- if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid())
+ if (idx.row() < 0 || idx.row() >= rowCount(idx) || !idx.isValid())
{
return false;
}
@@ -320,12 +366,12 @@ bool AccountList::setData(const QModelIndex &index, const QVariant &value, int r
{
if(value == Qt::Checked)
{
- MinecraftAccountPtr account = at(index.row());
- setActiveAccount(account->profileId());
+ MinecraftAccountPtr account = at(idx.row());
+ setDefaultAccount(account);
}
}
- emit dataChanged(index, index);
+ emit dataChanged(idx, index(idx.row(), columnCount(QModelIndex()) - 1));
return true;
}
@@ -395,7 +441,7 @@ bool AccountList::loadList()
bool AccountList::loadV2(QJsonObject& root) {
beginResetModel();
- auto activeUserName = root.value("activeAccount").toString("");
+ auto defaultUserName = root.value("activeAccount").toString("");
QJsonArray accounts = root.value("accounts").toArray();
for (QJsonValue accountVal : accounts)
{
@@ -411,9 +457,10 @@ bool AccountList::loadV2(QJsonObject& root) {
continue;
}
connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged);
+ connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged);
m_accounts.append(account);
- if (activeUserName.size() && account->mojangUserName() == activeUserName) {
- m_activeAccount = account;
+ if (defaultUserName.size() && account->mojangUserName() == defaultUserName) {
+ m_defaultAccount = account;
}
}
else
@@ -435,16 +482,16 @@ bool AccountList::loadV3(QJsonObject& root) {
if (account.get() != nullptr)
{
auto profileId = account->profileId();
- if(!profileId.size()) {
- continue;
- }
- if(findAccountByProfileId(profileId) != -1) {
- continue;
+ if(profileId.size()) {
+ if(findAccountByProfileId(profileId) != -1) {
+ continue;
+ }
}
connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged);
+ connect(account.get(), &MinecraftAccount::activityChanged, this, &AccountList::accountActivityChanged);
m_accounts.append(account);
if(accountObj.value("active").toBool(false)) {
- m_activeAccount = account;
+ m_defaultAccount = account;
}
}
else
@@ -491,7 +538,7 @@ bool AccountList::saveList()
for (MinecraftAccountPtr account : m_accounts)
{
QJsonObject accountObj = account->saveToJson();
- if(m_activeAccount == account) {
+ if(m_defaultAccount == account) {
accountObj["active"] = true;
}
accounts.append(accountObj);
diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h
index e275eb17..08004628 100644
--- a/launcher/minecraft/auth/AccountList.h
+++ b/launcher/minecraft/auth/AccountList.h
@@ -42,11 +42,13 @@ public:
ProfileNameColumn,
MigrationColumn,
TypeColumn,
+ StatusColumn,
NUM_COLUMNS
};
explicit AccountList(QObject *parent = 0);
+ virtual ~AccountList() noexcept;
const MinecraftAccountPtr at(int i) const;
int count() const;
@@ -63,6 +65,7 @@ public:
void removeAccount(QModelIndex index);
int findAccountByProfileId(const QString &profileId) const;
MinecraftAccountPtr getAccountByProfileName(const QString &profileName) const;
+ QStringList profileNames() const;
/*!
* Sets the path to load/save the list file from/to.
@@ -78,13 +81,14 @@ public:
bool loadV3(QJsonObject &root);
bool saveList();
- MinecraftAccountPtr activeAccount() const;
- void setActiveAccount(const QString &profileId);
+ MinecraftAccountPtr defaultAccount() const;
+ void setDefaultAccount(MinecraftAccountPtr profileId);
bool anyAccountIsValid();
signals:
void listChanged();
- void activeAccountChanged();
+ void listActivityChanged();
+ void defaultAccountChanged();
public slots:
/**
@@ -92,6 +96,11 @@ public slots:
*/
void accountChanged();
+ /**
+ * This is called when a (refresh/login) task involving the account starts or ends
+ */
+ void accountActivityChanged(bool active);
+
protected:
/*!
* Called whenever the list changes.
@@ -101,13 +110,13 @@ protected:
/*!
* Called whenever the active account changes.
- * Emits the activeAccountChanged() signal and autosaves the list if enabled.
+ * Emits the defaultAccountChanged() signal and autosaves the list if enabled.
*/
- void onActiveChanged();
+ void onDefaultAccountChanged();
QList<MinecraftAccountPtr> m_accounts;
- MinecraftAccountPtr m_activeAccount;
+ MinecraftAccountPtr m_defaultAccount;
//! Path to the account list file. Empty string if there isn't one.
QString m_listFilePath;
diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp
index d400ce8d..25d753de 100644
--- a/launcher/minecraft/auth/AccountTask.cpp
+++ b/launcher/minecraft/auth/AccountTask.cpp
@@ -23,10 +23,6 @@
#include <QNetworkReply>
#include <QByteArray>
-#include <Env.h>
-
-#include <BuildConfig.h>
-
#include <QDebug>
AccountTask::AccountTask(AccountData *data, QObject *parent)
diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h
index f609d5d3..55fbdf39 100644
--- a/launcher/minecraft/auth/AuthSession.h
+++ b/launcher/minecraft/auth/AuthSession.h
@@ -3,8 +3,10 @@
#include <QString>
#include <QMultiMap>
#include <memory>
+#include "QObjectPtr.h"
class MinecraftAccount;
+class QNetworkAccessManager;
struct AuthSession
{
@@ -17,6 +19,7 @@ struct AuthSession
Undetermined,
RequiresOAuth,
RequiresPassword,
+ RequiresProfileSetup,
PlayableOffline,
PlayableOnline,
GoneOrMigrated
@@ -40,7 +43,6 @@ struct AuthSession
bool auth_server_online = false;
// Did the user request online mode?
bool wants_online = true;
- std::shared_ptr<MinecraftAccount> m_accountPtr;
};
typedef std::shared_ptr<AuthSession> AuthSessionPtr;
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index 2d76f9ac..30ed6afe 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -28,11 +28,16 @@
#include <QDebug>
#include <QPainter>
-#include <minecraft/auth/flows/MSASilent.h>
-#include <minecraft/auth/flows/MSAInteractive.h>
+#include "flows/MSASilent.h"
+#include "flows/MSAInteractive.h"
+
+#include "flows/MojangRefresh.h"
+#include "flows/MojangLogin.h"
+
+MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
+ m_internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+}
-#include <minecraft/auth/flows/MojangRefresh.h>
-#include <minecraft/auth/flows/MojangLogin.h>
MinecraftAccountPtr MinecraftAccount::loadFromJsonV2(const QJsonObject& json) {
MinecraftAccountPtr account(new MinecraftAccount());
@@ -52,7 +57,7 @@ MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) {
MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username)
{
- MinecraftAccountPtr account(new MinecraftAccount());
+ MinecraftAccountPtr account = new MinecraftAccount();
account->data.type = AccountType::Mojang;
account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
@@ -91,6 +96,23 @@ AccountStatus MinecraftAccount::accountStatus() const {
}
}
+bool MinecraftAccount::isExpired() const {
+ switch(data.type) {
+ case AccountType::Mojang: {
+ return data.accessToken().isEmpty();
+ }
+ break;
+ case AccountType::MSA: {
+ return data.msaToken.validity == Katabasis::Validity::None;
+ }
+ break;
+ default: {
+ return true;
+ }
+ }
+}
+
+
QPixmap MinecraftAccount::getFace() const {
QPixmap skinTexture;
if(!skinTexture.loadFromData(data.minecraftProfile.skin.data, "PNG")) {
@@ -104,7 +126,7 @@ QPixmap MinecraftAccount::getFace() const {
}
-std::shared_ptr<AccountTask> MinecraftAccount::login(AuthSessionPtr session, QString password)
+shared_qobject_ptr<AccountTask> MinecraftAccount::login(AuthSessionPtr session, QString password)
{
Q_ASSERT(m_currentTask.get() == nullptr);
@@ -140,11 +162,12 @@ std::shared_ptr<AccountTask> MinecraftAccount::login(AuthSessionPtr session, QSt
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ emit activityChanged(true);
}
return m_currentTask;
}
-std::shared_ptr<AccountTask> MinecraftAccount::loginMSA(AuthSessionPtr session) {
+shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA(AuthSessionPtr session) {
Q_ASSERT(m_currentTask.get() == nullptr);
if(accountStatus() == Verified && !session->wants_online)
@@ -161,11 +184,12 @@ std::shared_ptr<AccountTask> MinecraftAccount::loginMSA(AuthSessionPtr session)
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ emit activityChanged(true);
}
return m_currentTask;
}
-std::shared_ptr<AccountTask> MinecraftAccount::refresh(AuthSessionPtr session) {
+shared_qobject_ptr<AccountTask> MinecraftAccount::refresh(AuthSessionPtr session) {
Q_ASSERT(m_currentTask.get() == nullptr);
// take care of the true offline status
@@ -203,6 +227,7 @@ std::shared_ptr<AccountTask> MinecraftAccount::refresh(AuthSessionPtr session) {
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ emit activityChanged(true);
}
return m_currentTask;
}
@@ -213,13 +238,27 @@ void MinecraftAccount::authSucceeded()
auto session = m_currentTask->getAssignedSession();
if (session)
{
- session->status =
- session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
+ /*
+ session->status = AuthSession::RequiresProfileSetup;
+ session->auth_server_online = true;
+ */
+ if(data.profileId().size() == 0) {
+ session->status = AuthSession::RequiresProfileSetup;
+ }
+ else {
+ if(session->wants_online) {
+ session->status = AuthSession::PlayableOnline;
+ }
+ else {
+ session->status = AuthSession::PlayableOffline;
+ }
+ }
fillSession(session);
session->auth_server_online = true;
}
m_currentTask.reset();
emit changed();
+ emit activityChanged(false);
}
void MinecraftAccount::authFailed(QString reason)
@@ -284,6 +323,45 @@ void MinecraftAccount::authFailed(QString reason)
}
}
m_currentTask.reset();
+ emit activityChanged(false);
+}
+
+bool MinecraftAccount::isActive() const {
+ return m_currentTask;
+}
+
+bool MinecraftAccount::shouldRefresh() const {
+ /*
+ * Never refresh accounts that are being used by the game, it breaks the game session.
+ * Always refresh accounts that have not been refreshed yet during this session.
+ * Don't refresh broken accounts.
+ * Refresh accounts that would expire in the next 12 hours (fresh token validity is 24 hours).
+ */
+ if(isInUse()) {
+ return false;
+ }
+ switch(data.validity_) {
+ case Katabasis::Validity::Certain: {
+ break;
+ }
+ case Katabasis::Validity::None: {
+ return false;
+ }
+ case Katabasis::Validity::Assumed: {
+ return true;
+ }
+ }
+ auto now = QDateTime::currentDateTimeUtc();
+ auto issuedTimestamp = data.yggdrasilToken.issueInstant;
+ auto expiresTimestamp = data.yggdrasilToken.notAfter;
+
+ if(!expiresTimestamp.isValid()) {
+ expiresTimestamp = issuedTimestamp.addSecs(24 * 3600);
+ }
+ if (now.secsTo(expiresTimestamp) < 12 * 3600) {
+ return true;
+ }
+ return false;
}
void MinecraftAccount::fillSession(AuthSessionPtr session)
@@ -309,7 +387,6 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
{
session->session = "-";
}
- session->m_accountPtr = shared_from_this();
}
void MinecraftAccount::decrementUses()
diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h
index 5b0c1ec7..459ef903 100644
--- a/launcher/minecraft/auth/MinecraftAccount.h
+++ b/launcher/minecraft/auth/MinecraftAccount.h
@@ -27,12 +27,13 @@
#include "AuthSession.h"
#include "Usable.h"
#include "AccountData.h"
+#include "QObjectPtr.h"
class Task;
class AccountTask;
class MinecraftAccount;
-typedef std::shared_ptr<MinecraftAccount> MinecraftAccountPtr;
+typedef shared_qobject_ptr<MinecraftAccount> MinecraftAccountPtr;
Q_DECLARE_METATYPE(MinecraftAccountPtr)
/**
@@ -63,8 +64,7 @@ enum AccountStatus
*/
class MinecraftAccount :
public QObject,
- public Usable,
- public std::enable_shared_from_this<MinecraftAccount>
+ public Usable
{
Q_OBJECT
public: /* construction */
@@ -72,7 +72,7 @@ public: /* construction */
explicit MinecraftAccount(const MinecraftAccount &other, QObject *parent) = delete;
//! Default constructor
- explicit MinecraftAccount(QObject *parent = 0) : QObject(parent) {};
+ explicit MinecraftAccount(QObject *parent = 0);
static MinecraftAccountPtr createFromUsername(const QString &username);
@@ -90,13 +90,17 @@ 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<AccountTask> login(AuthSessionPtr session, QString password = QString());
+ shared_qobject_ptr<AccountTask> login(AuthSessionPtr session, QString password);
- std::shared_ptr<AccountTask> loginMSA(AuthSessionPtr session);
+ shared_qobject_ptr<AccountTask> loginMSA(AuthSessionPtr session);
- std::shared_ptr<AccountTask> refresh(AuthSessionPtr session);
+ shared_qobject_ptr<AccountTask> refresh(AuthSessionPtr session);
public: /* queries */
+ QString internalId() const {
+ return m_internalId;
+ }
+
QString accountDisplayString() const {
return data.accountDisplayString();
}
@@ -117,6 +121,10 @@ public: /* queries */
return data.profileName();
}
+ bool isActive() const;
+
+ bool isExpired() const;
+
bool canMigrate() const {
return data.canMigrateToMSA;
}
@@ -153,19 +161,24 @@ public: /* queries */
return &data;
}
+ bool shouldRefresh() const;
+
signals:
/**
* This signal is emitted when the account changes
*/
void changed();
+ void activityChanged(bool active);
+
// TODO: better signalling for the various possible state changes - especially errors
protected: /* variables */
+ QString m_internalId;
AccountData data;
// current task we are executing here
- std::shared_ptr<AccountTask> m_currentTask;
+ shared_qobject_ptr<AccountTask> m_currentTask;
protected: /* methods */
diff --git a/launcher/minecraft/auth/flows/AuthContext.cpp b/launcher/minecraft/auth/flows/AuthContext.cpp
index 9fb3ec48..00957fd4 100644
--- a/launcher/minecraft/auth/flows/AuthContext.cpp
+++ b/launcher/minecraft/auth/flows/AuthContext.cpp
@@ -4,25 +4,21 @@
#include <QDesktopServices>
#include <QMetaEnum>
#include <QDebug>
-
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
-
+#include <QUuid>
#include <QUrlQuery>
-#include <QPixmap>
-#include <QPainter>
-
#include "AuthContext.h"
#include "katabasis/Globals.h"
#include "AuthRequest.h"
-#include "Secrets.h"
+#include "Parsers.h"
-#include "Env.h"
+#include <Application.h>
-using OAuth2 = Katabasis::OAuth2;
+using OAuth2 = Katabasis::DeviceFlow;
using Activity = Katabasis::Activity;
AuthContext::AuthContext(AccountData * data, QObject *parent) :
@@ -54,25 +50,17 @@ void AuthContext::initMSA() {
return;
}
- auto clientId = Secrets::getMSAClientID('-');
- if(clientId.isEmpty()) {
- return;
- }
-
- Katabasis::OAuth2::Options opts;
+ OAuth2::Options opts;
opts.scope = "XboxLive.signin offline_access";
- opts.clientIdentifier = clientId;
+ opts.clientIdentifier = APPLICATION->msaClientId();
opts.authorizationUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode";
opts.accessTokenUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token";
- opts.listenerPorts = {28562, 28563, 28564, 28565, 28566};
- m_oauth2 = new OAuth2(opts, m_data->msaToken, this, &ENV.qnam());
- m_oauth2->setGrantFlow(Katabasis::OAuth2::GrantFlowDevice);
+ // FIXME: OAuth2 is not aware of our fancy shared pointers
+ m_oauth2 = new OAuth2(opts, m_data->msaToken, this, APPLICATION->network().get());
- connect(m_oauth2, &OAuth2::linkingFailed, this, &AuthContext::onOAuthLinkingFailed);
- connect(m_oauth2, &OAuth2::linkingSucceeded, this, &AuthContext::onOAuthLinkingSucceeded);
- connect(m_oauth2, &OAuth2::showVerificationUriAndCode, this, &AuthContext::showVerificationUriAndCode);
connect(m_oauth2, &OAuth2::activityChanged, this, &AuthContext::onOAuthActivityChanged);
+ connect(m_oauth2, &OAuth2::showVerificationUriAndCode, this, &AuthContext::showVerificationUriAndCode);
}
void AuthContext::initMojang() {
@@ -97,50 +85,56 @@ void AuthContext::onMojangFailed() {
changeState(m_yggdrasil->accountState(), tr("Mojang user authentication failed."));
}
-/*
-bool AuthContext::signOut() {
- if(isBusy()) {
- return false;
- }
-
- start();
-
- beginActivity(Activity::LoggingOut);
- m_oauth2->unlink();
- m_account = AccountData();
- finishActivity();
- return true;
-}
-*/
-
-void AuthContext::onOAuthLinkingFailed() {
- emit hideVerificationUriAndCode();
- finishActivity();
- changeState(STATE_FAILED_HARD, tr("Microsoft user authentication failed."));
-}
-
-void AuthContext::onOAuthLinkingSucceeded() {
- emit hideVerificationUriAndCode();
- auto *o2t = qobject_cast<OAuth2 *>(sender());
- if (!o2t->linked()) {
- finishActivity();
- changeState(STATE_FAILED_HARD, tr("Microsoft user authentication ended with an impossible state (succeeded, but not succeeded at the same time)."));
- return;
- }
- QVariantMap extraTokens = o2t->extraTokens();
-#ifndef NDEBUG
- if (!extraTokens.isEmpty()) {
- qDebug() << "Extra tokens in response:";
- foreach (QString key, extraTokens.keys()) {
- qDebug() << "\t" << key << ":" << extraTokens.value(key);
+void AuthContext::onOAuthActivityChanged(Katabasis::Activity activity) {
+ switch(activity) {
+ case Katabasis::Activity::Idle:
+ case Katabasis::Activity::LoggingIn:
+ case Katabasis::Activity::Refreshing:
+ case Katabasis::Activity::LoggingOut: {
+ // We asked it to do something, it's doing it. Nothing to act upon.
+ return;
}
- }
+ case Katabasis::Activity::Succeeded: {
+ // Succeeded or did not invalidate tokens
+ emit hideVerificationUriAndCode();
+ if (!m_oauth2->linked()) {
+ finishActivity();
+ changeState(STATE_FAILED_HARD, tr("Microsoft user authentication ended with an impossible state (succeeded, but not succeeded at the same time)."));
+ return;
+ }
+ QVariantMap extraTokens = m_oauth2->extraTokens();
+#ifndef NDEBUG
+ if (!extraTokens.isEmpty()) {
+ qDebug() << "Extra tokens in response:";
+ foreach (QString key, extraTokens.keys()) {
+ qDebug() << "\t" << key << ":" << extraTokens.value(key);
+ }
+ }
#endif
- doUserAuth();
-}
+ doUserAuth();
+ return;
+ }
+ case Katabasis::Activity::FailedSoft: {
+ emit hideVerificationUriAndCode();
+ finishActivity();
+ changeState(STATE_FAILED_SOFT, tr("Microsoft user authentication failed with a soft error."));
+ return;
+ }
+ case Katabasis::Activity::FailedGone:
+ case Katabasis::Activity::FailedHard: {
+ emit hideVerificationUriAndCode();
+ finishActivity();
+ changeState(STATE_FAILED_HARD, tr("Microsoft user authentication failed."));
+ return;
+ }
+ default: {
+ emit hideVerificationUriAndCode();
+ finishActivity();
+ changeState(STATE_FAILED_HARD, tr("Microsoft user authentication completed with an unrecognized result."));
+ return;
+ }
-void AuthContext::onOAuthActivityChanged(Katabasis::Activity activity) {
- // respond to activity change here
+ }
}
void AuthContext::doUserAuth() {
@@ -169,137 +163,6 @@ void AuthContext::doUserAuth() {
qDebug() << "First layer of XBox auth ... commencing.";
}
-namespace {
-bool getDateTime(QJsonValue value, QDateTime & out) {
- if(!value.isString()) {
- return false;
- }
- out = QDateTime::fromString(value.toString(), Qt::ISODate);
- return out.isValid();
-}
-
-bool getString(QJsonValue value, QString & out) {
- if(!value.isString()) {
- return false;
- }
- out = value.toString();
- return true;
-}
-
-bool getNumber(QJsonValue value, double & out) {
- if(!value.isDouble()) {
- return false;
- }
- out = value.toDouble();
- return true;
-}
-
-bool getNumber(QJsonValue value, int64_t & out) {
- if(!value.isDouble()) {
- return false;
- }
- out = (int64_t) value.toDouble();
- return true;
-}
-
-bool getBool(QJsonValue value, bool & out) {
- if(!value.isBool()) {
- return false;
- }
- out = value.toBool();
- return true;
-}
-
-/*
-{
- "IssueInstant":"2020-12-07T19:52:08.4463796Z",
- "NotAfter":"2020-12-21T19:52:08.4463796Z",
- "Token":"token",
- "DisplayClaims":{
- "xui":[
- {
- "uhs":"userhash"
- }
- ]
- }
- }
-*/
-// TODO: handle error responses ...
-/*
-{
- "Identity":"0",
- "XErr":2148916238,
- "Message":"",
- "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
-}
-// 2148916233 = missing XBox account
-// 2148916238 = child account not linked to a family
-*/
-
-bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, const char * name) {
- qDebug() << "Parsing" << name <<":";
-#ifndef NDEBUG
- qDebug() << data;
-#endif
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- return false;
- }
-
- auto obj = doc.object();
- if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) {
- qWarning() << "User IssueInstant is not a timestamp";
- return false;
- }
- if(!getDateTime(obj.value("NotAfter"), output.notAfter)) {
- qWarning() << "User NotAfter is not a timestamp";
- return false;
- }
- if(!getString(obj.value("Token"), output.token)) {
- qWarning() << "User Token is not a timestamp";
- return false;
- }
- auto arrayVal = obj.value("DisplayClaims").toObject().value("xui");
- if(!arrayVal.isArray()) {
- qWarning() << "Missing xui claims array";
- return false;
- }
- bool foundUHS = false;
- for(auto item: arrayVal.toArray()) {
- if(!item.isObject()) {
- continue;
- }
- auto obj = item.toObject();
- if(obj.contains("uhs")) {
- foundUHS = true;
- } else {
- continue;
- }
- // consume all 'display claims' ... whatever that means
- for(auto iter = obj.begin(); iter != obj.end(); iter++) {
- QString claim;
- if(!getString(obj.value(iter.key()), claim)) {
- qWarning() << "display claim " << iter.key() << " is not a string...";
- return false;
- }
- output.extra[iter.key()] = claim;
- }
-
- break;
- }
- if(!foundUHS) {
- qWarning() << "Missing uhs";
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << name << "is valid.";
- return true;
-}
-
-}
-
void AuthContext::onUserAuthDone(
QNetworkReply::NetworkError error,
QByteArray replyData,
@@ -313,7 +176,7 @@ void AuthContext::onUserAuthDone(
}
Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp, "UToken")) {
+ if(!Parsers::parseXTokenResponse(replyData, temp, "UToken")) {
qWarning() << "Could not parse user authentication response...";
finishActivity();
changeState(STATE_FAILED_HARD, tr("XBox user authentication response could not be understood."));
@@ -365,7 +228,7 @@ void AuthContext::doSTSAuthMinecraft() {
void AuthContext::processSTSError(QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers) {
if(error == QNetworkReply::AuthenticationRequiredError) {
- QJsonParseError jsonError;
+ QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
if(jsonError.error) {
qWarning() << "Cannot parse error XSTS response as JSON: " << jsonError.errorString();
@@ -374,7 +237,7 @@ void AuthContext::processSTSError(QNetworkReply::NetworkError error, QByteArray
int64_t errorCode = -1;
auto obj = doc.object();
- if(!getNumber(obj.value("XErr"), errorCode)) {
+ if(!Parsers::getNumber(obj.value("XErr"), errorCode)) {
qWarning() << "XErr is not a number";
return;
}
@@ -400,7 +263,7 @@ void AuthContext::onSTSAuthMinecraftDone(
}
Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp, "STSAuthMinecraft")) {
+ if(!Parsers::parseXTokenResponse(replyData, temp, "STSAuthMinecraft")) {
qWarning() << "Could not parse authorization response for access to mojang services...";
failResult(m_mcAuthSucceeded);
return;
@@ -417,67 +280,33 @@ void AuthContext::onSTSAuthMinecraftDone(
}
void AuthContext::doMinecraftAuth() {
+ auto requestURL = "https://api.minecraftservices.com/launcher/login";
+ auto uhs = m_data->mojangservicesToken.extra["uhs"].toString();
+ auto xToken = m_data->mojangservicesToken.token;
+
QString mc_auth_template = R"XXX(
{
- "identityToken": "XBL3.0 x=%1;%2"
+ "xtoken": "XBL3.0 x=%1;%2",
+ "platform": "PC_LAUNCHER"
}
)XXX";
- auto data = mc_auth_template.arg(m_data->mojangservicesToken.extra["uhs"].toString(), m_data->mojangservicesToken.token);
+ auto requestBody = mc_auth_template.arg(uhs, xToken);
- QNetworkRequest request = QNetworkRequest(QUrl("https://api.minecraftservices.com/authentication/login_with_xbox"));
+ QNetworkRequest request = QNetworkRequest(QUrl(requestURL));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onMinecraftAuthDone);
- requestor->post(request, data.toUtf8());
+ requestor->post(request, requestBody.toUtf8());
qDebug() << "Getting Minecraft access token...";
}
-namespace {
-bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
- QJsonParseError jsonError;
- qDebug() << "Parsing Mojang response...";
-#ifndef NDEBUG
- qDebug() << data;
-#endif
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from api.minecraftservices.com/authentication/login_with_xbox as JSON: " << jsonError.errorString();
- return false;
- }
-
- auto obj = doc.object();
- double expires_in = 0;
- if(!getNumber(obj.value("expires_in"), expires_in)) {
- qWarning() << "expires_in is not a valid number";
- return false;
- }
- auto currentTime = QDateTime::currentDateTimeUtc();
- output.issueInstant = currentTime;
- output.notAfter = currentTime.addSecs(expires_in);
-
- QString username;
- if(!getString(obj.value("username"), username)) {
- qWarning() << "username is not valid";
- return false;
- }
-
- // TODO: it's a JWT... validate it?
- if(!getString(obj.value("access_token"), output.token)) {
- qWarning() << "access_token is not valid";
- return false;
- }
- output.validity = Katabasis::Validity::Certain;
- qDebug() << "Mojang response is valid.";
- return true;
-}
-}
-
void AuthContext::onMinecraftAuthDone(
QNetworkReply::NetworkError error,
QByteArray replyData,
QList<QNetworkReply::RawHeaderPair> headers
) {
+ qDebug() << replyData;
if (error != QNetworkReply::NoError) {
qWarning() << "Reply error:" << error;
#ifndef NDEBUG
@@ -487,7 +316,7 @@ void AuthContext::onMinecraftAuthDone(
return;
}
- if(!parseMojangResponse(replyData, m_data->yggdrasilToken)) {
+ if(!Parsers::parseMojangResponse(replyData, m_data->yggdrasilToken)) {
qWarning() << "Could not parse login_with_xbox response...";
#ifndef NDEBUG
qDebug() << replyData;
@@ -539,7 +368,7 @@ void AuthContext::onSTSAuthGenericDone(
}
Katabasis::Token temp;
- if(!parseXTokenResponse(replyData, temp, "STSAuthGeneric")) {
+ if(!Parsers::parseXTokenResponse(replyData, temp, "STSAuthGeneric")) {
qWarning() << "Could not parse authorization response for access to xbox API...";
failResult(m_xboxProfileSucceeded);
return;
@@ -619,7 +448,7 @@ void AuthContext::checkResult() {
return;
}
if(m_mcAuthSucceeded && m_xboxProfileSucceeded) {
- doMinecraftProfile();
+ doEntitlements();
}
else {
finishActivity();
@@ -662,84 +491,33 @@ void AuthContext::checkResult() {
}
}
-namespace {
-bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
- qDebug() << "Parsing Minecraft profile...";
+void AuthContext::doEntitlements() {
+ auto uuid = QUuid::createUuid();
+ entitlementsRequestId = uuid.toString().remove('{').remove('}');
+ auto url = "https://api.minecraftservices.com/entitlements/license?requestId=" + entitlementsRequestId;
+ QNetworkRequest request = QNetworkRequest(url);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setRawHeader("Accept", "application/json");
+ request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());
+ AuthRequest *requestor = new AuthRequest(this);
+ connect(requestor, &AuthRequest::finished, this, &AuthContext::onEntitlementsDone);
+ requestor->get(request);
+ qDebug() << "Getting Xbox profile...";
+}
+
+
+void AuthContext::onEntitlementsDone(
+ QNetworkReply::NetworkError error,
+ QByteArray data,
+ QList<QNetworkReply::RawHeaderPair> headers
+) {
#ifndef NDEBUG
qDebug() << data;
#endif
-
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
- return false;
- }
-
- auto obj = doc.object();
- if(!getString(obj.value("id"), output.id)) {
- qWarning() << "Minecraft profile id is not a string";
- return false;
- }
-
- if(!getString(obj.value("name"), output.name)) {
- qWarning() << "Minecraft profile name is not a string";
- return false;
- }
-
- auto skinsArray = obj.value("skins").toArray();
- for(auto skin: skinsArray) {
- auto skinObj = skin.toObject();
- Skin skinOut;
- if(!getString(skinObj.value("id"), skinOut.id)) {
- continue;
- }
- QString state;
- if(!getString(skinObj.value("state"), state)) {
- continue;
- }
- if(state != "ACTIVE") {
- continue;
- }
- if(!getString(skinObj.value("url"), skinOut.url)) {
- continue;
- }
- if(!getString(skinObj.value("variant"), skinOut.variant)) {
- continue;
- }
- // we deal with only the active skin
- output.skin = skinOut;
- break;
- }
- auto capesArray = obj.value("capes").toArray();
-
- QString currentCape;
- for(auto cape: capesArray) {
- auto capeObj = cape.toObject();
- Cape capeOut;
- if(!getString(capeObj.value("id"), capeOut.id)) {
- continue;
- }
- QString state;
- if(!getString(capeObj.value("state"), state)) {
- continue;
- }
- if(state == "ACTIVE") {
- currentCape = capeOut.id;
- }
- if(!getString(capeObj.value("url"), capeOut.url)) {
- continue;
- }
- if(!getString(capeObj.value("alias"), capeOut.alias)) {
- continue;
- }
-
- output.capes[capeOut.id] = capeOut;
- }
- output.currentCape = currentCape;
- output.validity = Katabasis::Validity::Certain;
- return true;
-}
+ // TODO: check presence of same entitlementsRequestId?
+ // TODO: validate JWTs?
+ Parsers::parseMinecraftEntitlements(data, m_data->minecraftEntitlement);
+ doMinecraftProfile();
}
void AuthContext::doMinecraftProfile() {
@@ -766,9 +544,13 @@ void AuthContext::onMinecraftProfileDone(
qDebug() << data;
#endif
if (error == QNetworkReply::ContentNotFoundError) {
+ // NOTE: Succeed even if we do not have a profile. This is a valid account state.
+ if(m_data->type == AccountType::Mojang) {
+ m_data->minecraftEntitlement.canPlayMinecraft = false;
+ m_data->minecraftEntitlement.ownsMinecraft = false;
+ }
m_data->minecraftProfile = MinecraftProfile();
- finishActivity();
- changeState(STATE_FAILED_HARD, tr("Account is missing a Minecraft Java profile.\n\nWhile the Microsoft account is valid, it does not own the game.\n\nYou might own Bedrock on this account, but that does not give you access to Java currently."));
+ succeed();
return;
}
if (error != QNetworkReply::NoError) {
@@ -776,7 +558,7 @@ void AuthContext::onMinecraftProfileDone(
changeState(STATE_FAILED_HARD, tr("Minecraft Java profile acquisition failed."));
return;
}
- if(!parseMinecraftProfile(data, m_data->minecraftProfile)) {
+ if(!Parsers::parseMinecraftProfile(data, m_data->minecraftProfile)) {
m_data->minecraftProfile = MinecraftProfile();
finishActivity();
changeState(STATE_FAILED_HARD, tr("Minecraft Java profile response could not be parsed"));
@@ -784,6 +566,9 @@ void AuthContext::onMinecraftProfileDone(
}
if(m_data->type == AccountType::Mojang) {
+ auto validProfile = m_data->minecraftProfile.validity == Katabasis::Validity::Certain;
+ m_data->minecraftEntitlement.canPlayMinecraft = validProfile;
+ m_data->minecraftEntitlement.ownsMinecraft = validProfile;
doMigrationEligibilityCheck();
}
else {
@@ -805,43 +590,13 @@ void AuthContext::doMigrationEligibilityCheck() {
requestor->get(request);
}
-bool parseRolloutResponse(QByteArray & data, bool& result) {
- qDebug() << "Parsing Rollout response...";
-#ifndef NDEBUG
- qDebug() << data;
-#endif
-
- QJsonParseError jsonError;
- QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
- if(jsonError.error) {
- qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " << jsonError.errorString();
- return false;
- }
-
- auto obj = doc.object();
- QString feature;
- if(!getString(obj.value("feature"), feature)) {
- qWarning() << "Rollout feature is not a string";
- return false;
- }
- if(feature != "msamigration") {
- qWarning() << "Rollout feature is not what we expected (msamigration), but is instead \"" << feature << "\"";
- return false;
- }
- if(!getBool(obj.value("rollout"), result)) {
- qWarning() << "Rollout feature is not a string";
- return false;
- }
- return true;
-}
-
void AuthContext::onMigrationEligibilityCheckDone(
QNetworkReply::NetworkError error,
QByteArray data,
QList<QNetworkReply::RawHeaderPair> headers
) {
if (error == QNetworkReply::NoError) {
- parseRolloutResponse(data, m_data->canMigrateToMSA);
+ Parsers::parseRolloutResponse(data, m_data->canMigrateToMSA);
}
doGetSkin();
}
@@ -865,6 +620,11 @@ void AuthContext::onSkinDone(
if (error == QNetworkReply::NoError) {
m_data->minecraftProfile.skin.data = data;
}
+ succeed();
+
+}
+
+void AuthContext::succeed() {
m_data->validity_ = Katabasis::Validity::Certain;
finishActivity();
changeState(STATE_SUCCEEDED, tr("Finished all authentication steps"));
diff --git a/launcher/minecraft/auth/flows/AuthContext.h b/launcher/minecraft/auth/flows/AuthContext.h
index dc7552ac..5e4e9edc 100644
--- a/launcher/minecraft/auth/flows/AuthContext.h
+++ b/launcher/minecraft/auth/flows/AuthContext.h
@@ -7,7 +7,7 @@
#include <QNetworkReply>
#include <QImage>
-#include <katabasis/OAuth2.h>
+#include <katabasis/DeviceFlow.h>
#include "Yggdrasil.h"
#include "../AccountData.h"
#include "../AccountTask.h"
@@ -35,9 +35,6 @@ signals:
private slots:
// OAuth-specific callbacks
- void onOAuthLinkingSucceeded();
- void onOAuthLinkingFailed();
-
void onOAuthActivityChanged(Katabasis::Activity activity);
// Yggdrasil specific callbacks
@@ -63,6 +60,9 @@ protected:
void doXBoxProfile();
Q_SLOT void onXBoxProfileDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
+ void doEntitlements();
+ Q_SLOT void onEntitlementsDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
+
void doMinecraftProfile();
Q_SLOT void onMinecraftProfileDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
@@ -72,6 +72,8 @@ protected:
void doGetSkin();
Q_SLOT void onSkinDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
+ void succeed();
+
void failResult(bool & flag);
void succeedResult(bool & flag);
void checkResult();
@@ -82,12 +84,13 @@ protected:
void clearTokens();
protected:
- Katabasis::OAuth2 *m_oauth2 = nullptr;
+ Katabasis::DeviceFlow *m_oauth2 = nullptr;
Yggdrasil *m_yggdrasil = nullptr;
int m_requestsDone = 0;
bool m_xboxProfileSucceeded = false;
bool m_mcAuthSucceeded = false;
+ QString entitlementsRequestId;
QSet<int64_t> stsErrors;
bool stsFailed = false;
diff --git a/launcher/minecraft/auth/flows/AuthRequest.cpp b/launcher/minecraft/auth/flows/AuthRequest.cpp
index 77558fd3..82dba591 100644
--- a/launcher/minecraft/auth/flows/AuthRequest.cpp
+++ b/launcher/minecraft/auth/flows/AuthRequest.cpp
@@ -5,9 +5,9 @@
#include <QBuffer>
#include <QUrlQuery>
+#include "Application.h"
#include "AuthRequest.h"
#include "katabasis/Globals.h"
-#include "Env.h"
AuthRequest::AuthRequest(QObject *parent): QObject(parent) {
}
@@ -17,7 +17,7 @@ AuthRequest::~AuthRequest() {
void AuthRequest::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) {
setup(req, QNetworkAccessManager::GetOperation);
- reply_ = ENV.qnam().get(request_);
+ reply_ = APPLICATION->network()->get(request_);
status_ = Requesting;
timedReplies_.add(new Katabasis::Reply(reply_, timeout));
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
@@ -29,7 +29,7 @@ void AuthRequest::post(const QNetworkRequest &req, const QByteArray &data, int t
setup(req, QNetworkAccessManager::PostOperation);
data_ = data;
status_ = Requesting;
- reply_ = ENV.qnam().post(request_, data_);
+ reply_ = APPLICATION->network()->post(request_, data_);
timedReplies_.add(new Katabasis::Reply(reply_, timeout));
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
diff --git a/launcher/minecraft/auth/flows/AuthRequest.h b/launcher/minecraft/auth/flows/AuthRequest.h
index 6a45a0bd..a547aea4 100644
--- a/launcher/minecraft/auth/flows/AuthRequest.h
+++ b/launcher/minecraft/auth/flows/AuthRequest.h
@@ -5,7 +5,6 @@
#include <QNetworkAccessManager>
#include <QUrl>
#include <QByteArray>
-#include <QHttpMultiPart>
#include "katabasis/Reply.h"
diff --git a/launcher/minecraft/auth/flows/MSAInteractive.cpp b/launcher/minecraft/auth/flows/MSAInteractive.cpp
index 03beb279..525aaf88 100644
--- a/launcher/minecraft/auth/flows/MSAInteractive.cpp
+++ b/launcher/minecraft/auth/flows/MSAInteractive.cpp
@@ -1,6 +1,9 @@
#include "MSAInteractive.h"
-MSAInteractive::MSAInteractive(AccountData* data, QObject* parent) : AuthContext(data, parent) {}
+MSAInteractive::MSAInteractive(
+ AccountData* data,
+ QObject* parent
+) : AuthContext(data, parent) {}
void MSAInteractive::executeTask() {
m_requestsDone = 0;
@@ -14,7 +17,6 @@ void MSAInteractive::executeTask() {
m_oauth2->setExtraRequestParams(extraOpts);
beginActivity(Katabasis::Activity::LoggingIn);
- m_oauth2->unlink();
*m_data = AccountData();
- m_oauth2->link();
+ m_oauth2->login();
}
diff --git a/launcher/minecraft/auth/flows/MSAInteractive.h b/launcher/minecraft/auth/flows/MSAInteractive.h
index 9556f254..6654e0d6 100644
--- a/launcher/minecraft/auth/flows/MSAInteractive.h
+++ b/launcher/minecraft/auth/flows/MSAInteractive.h
@@ -5,6 +5,9 @@ class MSAInteractive : public AuthContext
{
Q_OBJECT
public:
- explicit MSAInteractive(AccountData * data, QObject *parent = 0);
+ explicit MSAInteractive(
+ AccountData *data,
+ QObject *parent = 0
+ );
void executeTask() override;
};
diff --git a/launcher/minecraft/auth/flows/MSASilent.h b/launcher/minecraft/auth/flows/MSASilent.h
index e1b3d43d..a442b49e 100644
--- a/launcher/minecraft/auth/flows/MSASilent.h
+++ b/launcher/minecraft/auth/flows/MSASilent.h
@@ -5,6 +5,9 @@ class MSASilent : public AuthContext
{
Q_OBJECT
public:
- explicit MSASilent(AccountData * data, QObject *parent = 0);
+ explicit MSASilent(
+ AccountData * data,
+ QObject *parent = 0
+ );
void executeTask() override;
};
diff --git a/launcher/minecraft/auth/flows/MojangLogin.cpp b/launcher/minecraft/auth/flows/MojangLogin.cpp
index cca911b5..6c217cd1 100644
--- a/launcher/minecraft/auth/flows/MojangLogin.cpp
+++ b/launcher/minecraft/auth/flows/MojangLogin.cpp
@@ -1,6 +1,10 @@
#include "MojangLogin.h"
-MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthContext(data, parent), m_password(password) {}
+MojangLogin::MojangLogin(
+ AccountData *data,
+ QString password,
+ QObject *parent
+): AuthContext(data, parent), m_password(password) {}
void MojangLogin::executeTask() {
m_requestsDone = 0;
diff --git a/launcher/minecraft/auth/flows/MojangLogin.h b/launcher/minecraft/auth/flows/MojangLogin.h
index 2e765ae8..5f33752f 100644
--- a/launcher/minecraft/auth/flows/MojangLogin.h
+++ b/launcher/minecraft/auth/flows/MojangLogin.h
@@ -5,7 +5,11 @@ class MojangLogin : public AuthContext
{
Q_OBJECT
public:
- explicit MojangLogin(AccountData * data, QString password, QObject *parent = 0);
+ explicit MojangLogin(
+ AccountData *data,
+ QString password,
+ QObject *parent = 0
+ );
void executeTask() override;
private:
diff --git a/launcher/minecraft/auth/flows/MojangRefresh.cpp b/launcher/minecraft/auth/flows/MojangRefresh.cpp
index af99175c..008c0453 100644
--- a/launcher/minecraft/auth/flows/MojangRefresh.cpp
+++ b/launcher/minecraft/auth/flows/MojangRefresh.cpp
@@ -1,6 +1,9 @@
#include "MojangRefresh.h"
-MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthContext(data, parent) {}
+MojangRefresh::MojangRefresh(
+ AccountData *data,
+ QObject *parent
+) : AuthContext(data, parent) {}
void MojangRefresh::executeTask() {
m_requestsDone = 0;
diff --git a/launcher/minecraft/auth/flows/MojangRefresh.h b/launcher/minecraft/auth/flows/MojangRefresh.h
index fb4facd5..06e4e4ce 100644
--- a/launcher/minecraft/auth/flows/MojangRefresh.h
+++ b/launcher/minecraft/auth/flows/MojangRefresh.h
@@ -5,6 +5,6 @@ class MojangRefresh : public AuthContext
{
Q_OBJECT
public:
- explicit MojangRefresh(AccountData * data, QObject *parent = 0);
+ explicit MojangRefresh(AccountData *data, QObject *parent = 0);
void executeTask() override;
};
diff --git a/launcher/minecraft/auth/flows/Parsers.cpp b/launcher/minecraft/auth/flows/Parsers.cpp
new file mode 100644
index 00000000..ecb11cf9
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Parsers.cpp
@@ -0,0 +1,316 @@
+#include "Parsers.h"
+
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QDebug>
+
+namespace Parsers {
+
+bool getDateTime(QJsonValue value, QDateTime & out) {
+ if(!value.isString()) {
+ return false;
+ }
+ out = QDateTime::fromString(value.toString(), Qt::ISODate);
+ return out.isValid();
+}
+
+bool getString(QJsonValue value, QString & out) {
+ if(!value.isString()) {
+ return false;
+ }
+ out = value.toString();
+ return true;
+}
+
+bool getNumber(QJsonValue value, double & out) {
+ if(!value.isDouble()) {
+ return false;
+ }
+ out = value.toDouble();
+ return true;
+}
+
+bool getNumber(QJsonValue value, int64_t & out) {
+ if(!value.isDouble()) {
+ return false;
+ }
+ out = (int64_t) value.toDouble();
+ return true;
+}
+
+bool getBool(QJsonValue value, bool & out) {
+ if(!value.isBool()) {
+ return false;
+ }
+ out = value.toBool();
+ return true;
+}
+
+/*
+{
+ "IssueInstant":"2020-12-07T19:52:08.4463796Z",
+ "NotAfter":"2020-12-21T19:52:08.4463796Z",
+ "Token":"token",
+ "DisplayClaims":{
+ "xui":[
+ {
+ "uhs":"userhash"
+ }
+ ]
+ }
+ }
+*/
+// TODO: handle error responses ...
+/*
+{
+ "Identity":"0",
+ "XErr":2148916238,
+ "Message":"",
+ "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
+}
+// 2148916233 = missing XBox account
+// 2148916238 = child account not linked to a family
+*/
+
+bool parseXTokenResponse(QByteArray & data, Katabasis::Token &output, const char * name) {
+ qDebug() << "Parsing" << name <<":";
+#ifndef NDEBUG
+ qDebug() << data;
+#endif
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if(jsonError.error) {
+ qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
+ return false;
+ }
+
+ auto obj = doc.object();
+ if(!getDateTime(obj.value("IssueInstant"), output.issueInstant)) {
+ qWarning() << "User IssueInstant is not a timestamp";
+ return false;
+ }
+ if(!getDateTime(obj.value("NotAfter"), output.notAfter)) {
+ qWarning() << "User NotAfter is not a timestamp";
+ return false;
+ }
+ if(!getString(obj.value("Token"), output.token)) {
+ qWarning() << "User Token is not a timestamp";
+ return false;
+ }
+ auto arrayVal = obj.value("DisplayClaims").toObject().value("xui");
+ if(!arrayVal.isArray()) {
+ qWarning() << "Missing xui claims array";
+ return false;
+ }
+ bool foundUHS = false;
+ for(auto item: arrayVal.toArray()) {
+ if(!item.isObject()) {
+ continue;
+ }
+ auto obj = item.toObject();
+ if(obj.contains("uhs")) {
+ foundUHS = true;
+ } else {
+ continue;
+ }
+ // consume all 'display claims' ... whatever that means
+ for(auto iter = obj.begin(); iter != obj.end(); iter++) {
+ QString claim;
+ if(!getString(obj.value(iter.key()), claim)) {
+ qWarning() << "display claim " << iter.key() << " is not a string...";
+ return false;
+ }
+ output.extra[iter.key()] = claim;
+ }
+
+ break;
+ }
+ if(!foundUHS) {
+ qWarning() << "Missing uhs";
+ return false;
+ }
+ output.validity = Katabasis::Validity::Certain;
+ qDebug() << name << "is valid.";
+ return true;
+}
+
+bool parseMinecraftProfile(QByteArray & data, MinecraftProfile &output) {
+ qDebug() << "Parsing Minecraft profile...";
+#ifndef NDEBUG
+ qDebug() << data;
+#endif
+
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if(jsonError.error) {
+ qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
+ return false;
+ }
+
+ auto obj = doc.object();
+ if(!getString(obj.value("id"), output.id)) {
+ qWarning() << "Minecraft profile id is not a string";
+ return false;
+ }
+
+ if(!getString(obj.value("name"), output.name)) {
+ qWarning() << "Minecraft profile name is not a string";
+ return false;
+ }
+
+ auto skinsArray = obj.value("skins").toArray();
+ for(auto skin: skinsArray) {
+ auto skinObj = skin.toObject();
+ Skin skinOut;
+ if(!getString(skinObj.value("id"), skinOut.id)) {
+ continue;
+ }
+ QString state;
+ if(!getString(skinObj.value("state"), state)) {
+ continue;
+ }
+ if(state != "ACTIVE") {
+ continue;
+ }
+ if(!getString(skinObj.value("url"), skinOut.url)) {
+ continue;
+ }
+ if(!getString(skinObj.value("variant"), skinOut.variant)) {
+ continue;
+ }
+ // we deal with only the active skin
+ output.skin = skinOut;
+ break;
+ }
+ auto capesArray = obj.value("capes").toArray();
+
+ QString currentCape;
+ for(auto cape: capesArray) {
+ auto capeObj = cape.toObject();
+ Cape capeOut;
+ if(!getString(capeObj.value("id"), capeOut.id)) {
+ continue;
+ }
+ QString state;
+ if(!getString(capeObj.value("state"), state)) {
+ continue;
+ }
+ if(state == "ACTIVE") {
+ currentCape = capeOut.id;
+ }
+ if(!getString(capeObj.value("url"), capeOut.url)) {
+ continue;
+ }
+ if(!getString(capeObj.value("alias"), capeOut.alias)) {
+ continue;
+ }
+
+ output.capes[capeOut.id] = capeOut;
+ }
+ output.currentCape = currentCape;
+ output.validity = Katabasis::Validity::Certain;
+ return true;
+}
+
+bool parseMinecraftEntitlements(QByteArray & data, MinecraftEntitlement &output) {
+ qDebug() << "Parsing Minecraft entitlements...";
+#ifndef NDEBUG
+ qDebug() << data;
+#endif
+
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if(jsonError.error) {
+ qWarning() << "Failed to parse response from user.auth.xboxlive.com as JSON: " << jsonError.errorString();
+ return false;
+ }
+
+ auto obj = doc.object();
+
+ auto itemsArray = obj.value("items").toArray();
+ for(auto item: itemsArray) {
+ auto itemObj = item.toObject();
+ QString name;
+ if(!getString(itemObj.value("name"), name)) {
+ continue;
+ }
+ if(name == "game_minecraft") {
+ output.canPlayMinecraft = true;
+ }
+ if(name == "product_minecraft") {
+ output.ownsMinecraft = true;
+ }
+ }
+ output.validity = Katabasis::Validity::Certain;
+ return true;
+}
+
+bool parseRolloutResponse(QByteArray & data, bool& result) {
+ qDebug() << "Parsing Rollout response...";
+#ifndef NDEBUG
+ qDebug() << data;
+#endif
+
+ QJsonParseError jsonError;
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if(jsonError.error) {
+ qWarning() << "Failed to parse response from https://api.minecraftservices.com/rollout/v1/msamigration as JSON: " << jsonError.errorString();
+ return false;
+ }
+
+ auto obj = doc.object();
+ QString feature;
+ if(!getString(obj.value("feature"), feature)) {
+ qWarning() << "Rollout feature is not a string";
+ return false;
+ }
+ if(feature != "msamigration") {
+ qWarning() << "Rollout feature is not what we expected (msamigration), but is instead \"" << feature << "\"";
+ return false;
+ }
+ if(!getBool(obj.value("rollout"), result)) {
+ qWarning() << "Rollout feature is not a string";
+ return false;
+ }
+ return true;
+}
+
+bool parseMojangResponse(QByteArray & data, Katabasis::Token &output) {
+ QJsonParseError jsonError;
+ qDebug() << "Parsing Mojang response...";
+#ifndef NDEBUG
+ qDebug() << data;
+#endif
+ QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
+ if(jsonError.error) {
+ qWarning() << "Failed to parse response from api.minecraftservices.com/launcher/login as JSON: " << jsonError.errorString();
+ return false;
+ }
+
+ auto obj = doc.object();
+ double expires_in = 0;
+ if(!getNumber(obj.value("expires_in"), expires_in)) {
+ qWarning() << "expires_in is not a valid number";
+ return false;
+ }
+ auto currentTime = QDateTime::currentDateTimeUtc();
+ output.issueInstant = currentTime;
+ output.notAfter = currentTime.addSecs(expires_in);
+
+ QString username;
+ if(!getString(obj.value("username"), username)) {
+ qWarning() << "username is not valid";
+ return false;
+ }
+
+ // TODO: it's a JWT... validate it?
+ if(!getString(obj.value("access_token"), output.token)) {
+ qWarning() << "access_token is not valid";
+ return false;
+ }
+ output.validity = Katabasis::Validity::Certain;
+ qDebug() << "Mojang response is valid.";
+ return true;
+}
+
+}
diff --git a/launcher/minecraft/auth/flows/Parsers.h b/launcher/minecraft/auth/flows/Parsers.h
new file mode 100644
index 00000000..b484a073
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Parsers.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "../AccountData.h"
+
+namespace Parsers
+{
+ bool getDateTime(QJsonValue value, QDateTime & out);
+ bool getString(QJsonValue value, QString & out);
+ bool getNumber(QJsonValue value, double & out);
+ bool getNumber(QJsonValue value, int64_t & out);
+ bool getBool(QJsonValue value, bool & out);
+
+ bool parseXTokenResponse(QByteArray &data, Katabasis::Token &output, const char * name);
+ bool parseMojangResponse(QByteArray &data, Katabasis::Token &output);
+
+ bool parseMinecraftProfile(QByteArray &data, MinecraftProfile &output);
+ bool parseMinecraftEntitlements(QByteArray &data, MinecraftEntitlement &output);
+ bool parseRolloutResponse(QByteArray &data, bool& result);
+}
diff --git a/launcher/minecraft/auth/flows/Yggdrasil.cpp b/launcher/minecraft/auth/flows/Yggdrasil.cpp
index 20ca63d0..5ea168e8 100644
--- a/launcher/minecraft/auth/flows/Yggdrasil.cpp
+++ b/launcher/minecraft/auth/flows/Yggdrasil.cpp
@@ -23,12 +23,10 @@
#include <QNetworkReply>
#include <QByteArray>
-#include <Env.h>
-
-#include <BuildConfig.h>
-
#include <QDebug>
+#include "Application.h"
+
Yggdrasil::Yggdrasil(AccountData *data, QObject *parent)
: AccountTask(data, parent)
{
@@ -40,7 +38,7 @@ void Yggdrasil::sendRequest(QUrl endpoint, QByteArray content) {
QNetworkRequest netRequest(endpoint);
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- m_netReply = ENV.qnam().post(netRequest, content);
+ m_netReply = APPLICATION->network()->post(netRequest, content);
connect(m_netReply, &QNetworkReply::finished, this, &Yggdrasil::processReply);
connect(m_netReply, &QNetworkReply::uploadProgress, this, &Yggdrasil::refreshTimers);
connect(m_netReply, &QNetworkReply::downloadProgress, this, &Yggdrasil::refreshTimers);
@@ -86,7 +84,7 @@ void Yggdrasil::refresh() {
req.insert("requestUser", false);
QJsonDocument doc(req);
- QUrl reqUrl(BuildConfig.AUTH_BASE + "refresh");
+ QUrl reqUrl("https://authserver.mojang.com/refresh");
QByteArray requestData = doc.toJson();
sendRequest(reqUrl, requestData);
@@ -131,7 +129,7 @@ void Yggdrasil::login(QString password) {
QJsonDocument doc(req);
- QUrl reqUrl(BuildConfig.AUTH_BASE + "authenticate");
+ QUrl reqUrl("https://authserver.mojang.com/authenticate");
QNetworkRequest netRequest(reqUrl);
QByteArray requestData = doc.toJson();
@@ -140,20 +138,18 @@ void Yggdrasil::login(QString password) {
-void Yggdrasil::refreshTimers(qint64, qint64)
-{
+void Yggdrasil::refreshTimers(qint64, qint64) {
timeout_keeper.stop();
timeout_keeper.start(timeout_max);
progress(count = 0, timeout_max);
}
-void Yggdrasil::heartbeat()
-{
+
+void Yggdrasil::heartbeat() {
count += time_step;
progress(count, timeout_max);
}
-bool Yggdrasil::abort()
-{
+bool Yggdrasil::abort() {
progress(timeout_max, timeout_max);
// TODO: actually use this in a meaningful way
m_aborted = Yggdrasil::BY_USER;
@@ -161,19 +157,16 @@ bool Yggdrasil::abort()
return true;
}
-void Yggdrasil::abortByTimeout()
-{
+void Yggdrasil::abortByTimeout() {
progress(timeout_max, timeout_max);
// TODO: actually use this in a meaningful way
m_aborted = Yggdrasil::BY_TIMEOUT;
m_netReply->abort();
}
-void Yggdrasil::sslErrors(QList<QSslError> errors)
-{
+void Yggdrasil::sslErrors(QList<QSslError> errors) {
int i = 1;
- for (auto error : errors)
- {
+ for (auto error : errors) {
qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
qCritical() << "Certificate in question:\n" << cert.toText();
@@ -181,8 +174,7 @@ void Yggdrasil::sslErrors(QList<QSslError> errors)
}
}
-void Yggdrasil::processResponse(QJsonObject responseData)
-{
+void Yggdrasil::processResponse(QJsonObject responseData) {
// Read the response data. We need to get the client token, access token, and the selected
// profile.
qDebug() << "Processing authentication response.";
@@ -191,8 +183,7 @@ void Yggdrasil::processResponse(QJsonObject responseData)
// If we already have a client token, make sure the one the server gave us matches our
// existing one.
QString clientToken = responseData.value("clientToken").toString("");
- if (clientToken.isEmpty())
- {
+ if (clientToken.isEmpty()) {
// Fail if the server gave us an empty client token
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send a client token."));
return;
@@ -208,8 +199,7 @@ void Yggdrasil::processResponse(QJsonObject responseData)
// Now, we set the access token.
qDebug() << "Getting access token.";
QString accessToken = responseData.value("accessToken").toString("");
- if (accessToken.isEmpty())
- {
+ if (accessToken.isEmpty()) {
// Fail if the server didn't give us an access token.
changeState(STATE_FAILED_HARD, tr("Authentication server didn't send an access token."));
return;
@@ -217,6 +207,7 @@ void Yggdrasil::processResponse(QJsonObject responseData)
// Set the access token.
m_data->yggdrasilToken.token = accessToken;
m_data->yggdrasilToken.validity = Katabasis::Validity::Certain;
+ m_data->yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
// We've made it through the minefield of possible errors. Return true to indicate that
// we've succeeded.
@@ -224,8 +215,7 @@ void Yggdrasil::processResponse(QJsonObject responseData)
changeState(STATE_SUCCEEDED);
}
-void Yggdrasil::processReply()
-{
+void Yggdrasil::processReply() {
changeState(STATE_WORKING);
switch (m_netReply->error())
@@ -247,9 +237,9 @@ void Yggdrasil::processReply()
"<li>You use Windows and need to update your root certificates, please install any outstanding updates.</li>"
"<li>Some device on your network is interfering with SSL traffic. In that case, "
"you have bigger worries than Minecraft not starting.</li>"
- "<li>Possibly something else. Check the %1 log file for details</li>"
+ "<li>Possibly something else. Check the log file for details</li>"
"</ul>"
- ).arg(BuildConfig.LAUNCHER_NAME)
+ )
);
return;
// used for invalid credentials and similar errors. Fall through.
@@ -278,19 +268,16 @@ void Yggdrasil::processReply()
// Check the response code.
int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (responseCode == 200)
- {
+ if (responseCode == 200) {
// If the response code was 200, then there shouldn't be an error. Make sure
// anyways.
// Also, sometimes an empty reply indicates success. If there was no data received,
// pass an empty json object to the processResponse function.
- if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
- {
+ if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) {
processResponse(replyData.size() > 0 ? doc.object() : QJsonObject());
return;
}
- else
- {
+ else {
changeState(
STATE_FAILED_SOFT,
tr("Failed to parse authentication server response JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset)
@@ -304,16 +291,14 @@ void Yggdrasil::processReply()
// about the error.
// If we can parse the response, then get information from it. Otherwise just say
// there was an unknown error.
- if (jsonError.error == QJsonParseError::NoError)
- {
+ if (jsonError.error == QJsonParseError::NoError) {
// We were able to parse the server's response. Woo!
// Call processError. If a subclass has overridden it then they'll handle their
// stuff there.
qDebug() << "The request failed, but the server gave us an error message. Processing error.";
processError(doc.object());
}
- else
- {
+ else {
// The server didn't say anything regarding the error. Give the user an unknown
// error.
qDebug() << "The request failed and the server gave no error message. Unknown error.";
@@ -324,14 +309,12 @@ void Yggdrasil::processReply()
}
}
-void Yggdrasil::processError(QJsonObject responseData)
-{
+void Yggdrasil::processError(QJsonObject responseData) {
QJsonValue errorVal = responseData.value("error");
QJsonValue errorMessageValue = responseData.value("errorMessage");
QJsonValue causeVal = responseData.value("cause");
- if (errorVal.isString() && errorMessageValue.isString())
- {
+ if (errorVal.isString() && errorMessageValue.isString()) {
m_error = std::shared_ptr<Error>(
new Error {
errorVal.toString(""),
@@ -341,8 +324,7 @@ void Yggdrasil::processError(QJsonObject responseData)
);
changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
}
- else
- {
+ else {
// Error is not in standard format. Don't set m_error and return unknown error.
changeState(STATE_FAILED_HARD, tr("An unknown Yggdrasil error occurred."));
}
diff --git a/launcher/minecraft/auth/flows/Yggdrasil.h b/launcher/minecraft/auth/flows/Yggdrasil.h
index e709cb9f..b9670ec7 100644
--- a/launcher/minecraft/auth/flows/Yggdrasil.h
+++ b/launcher/minecraft/auth/flows/Yggdrasil.h
@@ -24,6 +24,7 @@
#include "../MinecraftAccount.h"
+class QNetworkAccessManager;
class QNetworkReply;
/**
@@ -33,7 +34,10 @@ class Yggdrasil : public AccountTask
{
Q_OBJECT
public:
- explicit Yggdrasil(AccountData * data, QObject *parent = 0);
+ explicit Yggdrasil(
+ AccountData *data,
+ QObject *parent = 0
+ );
virtual ~Yggdrasil() {};
void refresh();
diff --git a/launcher/minecraft/launch/ClaimAccount.cpp b/launcher/minecraft/launch/ClaimAccount.cpp
index a1180f0a..bb4f6806 100644
--- a/launcher/minecraft/launch/ClaimAccount.cpp
+++ b/launcher/minecraft/launch/ClaimAccount.cpp
@@ -1,11 +1,15 @@
#include "ClaimAccount.h"
#include <launch/LaunchTask.h>
+#include "Application.h"
+#include "minecraft/auth/AccountList.h"
+
ClaimAccount::ClaimAccount(LaunchTask* parent, AuthSessionPtr session): LaunchStep(parent)
{
if(session->status == AuthSession::Status::PlayableOnline)
{
- m_account = session->m_accountPtr;
+ auto accounts = APPLICATION->accounts();
+ m_account = accounts->getAccountByProfileName(session->player_name);
}
}
diff --git a/launcher/minecraft/launch/LauncherPartLaunch.cpp b/launcher/minecraft/launch/LauncherPartLaunch.cpp
index ff022c40..8fd11eca 100644
--- a/launcher/minecraft/launch/LauncherPartLaunch.cpp
+++ b/launcher/minecraft/launch/LauncherPartLaunch.cpp
@@ -14,13 +14,14 @@
*/
#include "LauncherPartLaunch.h"
-#include <QCoreApplication>
-#include <launch/LaunchTask.h>
-#include <minecraft/MinecraftInstance.h>
-#include <FileSystem.h>
-#include <Commandline.h>
+
#include <QStandardPaths>
-#include "Env.h"
+
+#include "launch/LaunchTask.h"
+#include "minecraft/MinecraftInstance.h"
+#include "FileSystem.h"
+#include "Commandline.h"
+#include "Application.h"
LauncherPartLaunch::LauncherPartLaunch(LaunchTask *parent) : LaunchStep(parent)
{
@@ -72,7 +73,7 @@ void LauncherPartLaunch::executeTask()
m_process.setDetachable(true);
auto classPath = minecraftInstance->getClassPath();
- classPath.prepend(FS::PathCombine(ENV.getJarsPath(), "NewLaunch.jar"));
+ classPath.prepend(FS::PathCombine(APPLICATION->getJarsPath(), "NewLaunch.jar"));
auto natPath = minecraftInstance->getNativePath();
#ifdef Q_OS_WIN
diff --git a/launcher/minecraft/launch/VerifyJavaInstall.cpp b/launcher/minecraft/launch/VerifyJavaInstall.cpp
index 657669af..d9f7ecdc 100644
--- a/launcher/minecraft/launch/VerifyJavaInstall.cpp
+++ b/launcher/minecraft/launch/VerifyJavaInstall.cpp
@@ -11,8 +11,17 @@ void VerifyJavaInstall::executeTask() {
auto javaVersion = m_inst->getJavaVersion();
auto minecraftComponent = m_inst->getPackProfile()->getComponent("net.minecraft");
+ // Java 17 requirement
+ if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java17BeginsDate) {
+ if (javaVersion.major() < 17) {
+ emit logLine("Minecraft 1.18 Pre Release 2 and above require the use of Java 17",
+ MessageLevel::Fatal);
+ emitFailed(tr("Minecraft 1.18 Pre Release 2 and above require the use of Java 17"));
+ return;
+ }
+ }
// Java 16 requirement
- if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
+ else if (minecraftComponent->getReleaseDateTime() >= g_VersionFilterData.java16BeginsDate) {
if (javaVersion.major() < 16) {
emit logLine("Minecraft 21w19a and above require the use of Java 16",
MessageLevel::Fatal);
diff --git a/launcher/minecraft/legacy/LegacyInstance.cpp b/launcher/minecraft/legacy/LegacyInstance.cpp
index 9f9bda5a..c2b4309c 100644
--- a/launcher/minecraft/legacy/LegacyInstance.cpp
+++ b/launcher/minecraft/legacy/LegacyInstance.cpp
@@ -90,7 +90,7 @@ bool LegacyInstance::shouldUseCustomBaseJar() const
}
-shared_qobject_ptr<Task> LegacyInstance::createUpdateTask(Net::Mode)
+Task::Ptr LegacyInstance::createUpdateTask(Net::Mode)
{
return nullptr;
}
diff --git a/launcher/minecraft/legacy/LegacyInstance.h b/launcher/minecraft/legacy/LegacyInstance.h
index ac2a8543..c6680fd0 100644
--- a/launcher/minecraft/legacy/LegacyInstance.h
+++ b/launcher/minecraft/legacy/LegacyInstance.h
@@ -93,7 +93,7 @@ public:
};
virtual bool shouldUpdate() const;
- virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
+ virtual Task::Ptr createUpdateTask(Net::Mode mode) override;
virtual QString typeName() const override;
diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp
index c1d88d14..d411965a 100644
--- a/launcher/minecraft/services/CapeChange.cpp
+++ b/launcher/minecraft/services/CapeChange.cpp
@@ -1,7 +1,9 @@
#include "CapeChange.h"
+
#include <QNetworkRequest>
#include <QHttpMultiPart>
-#include <Env.h>
+
+#include "Application.h"
CapeChange::CapeChange(QObject *parent, AuthSessionPtr session, QString cape)
: Task(parent), m_capeId(cape), m_session(session)
@@ -12,11 +14,11 @@ void CapeChange::setCape(QString& cape) {
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"));
auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId);
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QNetworkReply *rep = ENV.qnam().put(request, requestString.toUtf8());
+ QNetworkReply *rep = APPLICATION->network()->put(request, requestString.toUtf8());
setStatus(tr("Equipping cape"));
- m_reply = std::shared_ptr<QNetworkReply>(rep);
+ m_reply = shared_qobject_ptr<QNetworkReply>(rep);
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
@@ -26,11 +28,11 @@ void CapeChange::clearCape() {
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"));
auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId);
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QNetworkReply *rep = ENV.qnam().deleteResource(request);
+ QNetworkReply *rep = APPLICATION->network()->deleteResource(request);
setStatus(tr("Removing cape"));
- m_reply = std::shared_ptr<QNetworkReply>(rep);
+ m_reply = shared_qobject_ptr<QNetworkReply>(rep);
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(downloadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished()));
diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h
index 1b6f2f72..c04ad8c7 100644
--- a/launcher/minecraft/services/CapeChange.h
+++ b/launcher/minecraft/services/CapeChange.h
@@ -5,6 +5,7 @@
#include <memory>
#include <minecraft/auth/AuthSession.h>
#include "tasks/Task.h"
+#include "QObjectPtr.h"
class CapeChange : public Task
{
@@ -20,7 +21,7 @@ private:
private:
QString m_capeId;
AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
+ shared_qobject_ptr<QNetworkReply> m_reply;
protected:
virtual void executeTask();
diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp
index 34977257..a0b0330c 100644
--- a/launcher/minecraft/services/SkinDelete.cpp
+++ b/launcher/minecraft/services/SkinDelete.cpp
@@ -1,7 +1,9 @@
#include "SkinDelete.h"
+
#include <QNetworkRequest>
#include <QHttpMultiPart>
-#include <Env.h>
+
+#include "Application.h"
SkinDelete::SkinDelete(QObject *parent, AuthSessionPtr session)
: Task(parent), m_session(session)
@@ -12,8 +14,8 @@ void SkinDelete::executeTask()
{
QNetworkRequest request(QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"));
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_session->access_token).toLocal8Bit());
- QNetworkReply *rep = ENV.qnam().deleteResource(request);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
+ QNetworkReply *rep = APPLICATION->network()->deleteResource(request);
+ m_reply = shared_qobject_ptr<QNetworkReply>(rep);
setStatus(tr("Deleting skin"));
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h
index 839bf9bc..6048b33a 100644
--- a/launcher/minecraft/services/SkinDelete.h
+++ b/launcher/minecraft/services/SkinDelete.h
@@ -2,11 +2,10 @@
#include <QFile>
#include <QtNetwork/QtNetwork>
-#include <memory>
#include <minecraft/auth/AuthSession.h>
#include "tasks/Task.h"
-typedef std::shared_ptr<class SkinDelete> SkinDeletePtr;
+typedef shared_qobject_ptr<class SkinDelete> SkinDeletePtr;
class SkinDelete : public Task
{
@@ -17,7 +16,7 @@ public:
private:
AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
+ shared_qobject_ptr<QNetworkReply> m_reply;
protected:
virtual void executeTask();
diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp
index 4e5a1698..e58d32d7 100644
--- a/launcher/minecraft/services/SkinUpload.cpp
+++ b/launcher/minecraft/services/SkinUpload.cpp
@@ -1,7 +1,9 @@
#include "SkinUpload.h"
+
#include <QNetworkRequest>
#include <QHttpMultiPart>
-#include <Env.h>
+
+#include "Application.h"
QByteArray getVariant(SkinUpload::Model model) {
switch (model) {
@@ -37,8 +39,8 @@ void SkinUpload::executeTask()
multiPart->append(skin);
multiPart->append(model);
- QNetworkReply *rep = ENV.qnam().post(request, multiPart);
- m_reply = std::shared_ptr<QNetworkReply>(rep);
+ QNetworkReply *rep = APPLICATION->network()->post(request, multiPart);
+ m_reply = shared_qobject_ptr<QNetworkReply>(rep);
setStatus(tr("Uploading skin"));
connect(rep, &QNetworkReply::uploadProgress, this, &Task::setProgress);
diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h
index ec859699..2c782e11 100644
--- a/launcher/minecraft/services/SkinUpload.h
+++ b/launcher/minecraft/services/SkinUpload.h
@@ -6,7 +6,7 @@
#include <minecraft/auth/AuthSession.h>
#include "tasks/Task.h"
-typedef std::shared_ptr<class SkinUpload> SkinUploadPtr;
+typedef shared_qobject_ptr<class SkinUpload> SkinUploadPtr;
class SkinUpload : public Task
{
@@ -26,7 +26,7 @@ private:
Model m_model;
QByteArray m_skin;
AuthSessionPtr m_session;
- std::shared_ptr<QNetworkReply> m_reply;
+ shared_qobject_ptr<QNetworkReply> m_reply;
protected:
virtual void executeTask();
diff --git a/launcher/minecraft/update/AssetUpdateTask.cpp b/launcher/minecraft/update/AssetUpdateTask.cpp
index e26ab4ef..096e1719 100644
--- a/launcher/minecraft/update/AssetUpdateTask.cpp
+++ b/launcher/minecraft/update/AssetUpdateTask.cpp
@@ -1,10 +1,12 @@
-#include "Env.h"
#include "AssetUpdateTask.h"
+
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "net/ChecksumValidator.h"
#include "minecraft/AssetsUtils.h"
+#include "Application.h"
+
AssetUpdateTask::AssetUpdateTask(MinecraftInstance * inst)
{
m_inst = inst;
@@ -24,7 +26,7 @@ void AssetUpdateTask::executeTask()
QString localPath = assets->id + ".json";
auto job = new NetJob(tr("Asset index for %1").arg(m_inst->name()));
- auto metacache = ENV.metacache();
+ auto metacache = APPLICATION->metacache();
auto entry = metacache->resolveEntry("asset_indexes", localPath);
entry->setStale(true);
auto hexSha1 = assets->sha1.toLatin1();
@@ -41,7 +43,7 @@ void AssetUpdateTask::executeTask()
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
qDebug() << m_inst->name() << ": Starting asset index download";
- downloadJob->start();
+ downloadJob->start(APPLICATION->network());
}
bool AssetUpdateTask::canAbort() const
@@ -62,7 +64,7 @@ void AssetUpdateTask::assetIndexFinished()
// FIXME: this looks like a job for a generic validator based on json schema?
if (!AssetsUtils::loadAssetsIndexJson(assets->id, asset_fname, index))
{
- auto metacache = ENV.metacache();
+ auto metacache = APPLICATION->metacache();
auto entry = metacache->resolveEntry("asset_indexes", assets->id + ".json");
metacache->evictEntry(entry);
emitFailed(tr("Failed to read the assets index!"));
@@ -76,7 +78,7 @@ void AssetUpdateTask::assetIndexFinished()
connect(downloadJob.get(), &NetJob::succeeded, this, &AssetUpdateTask::emitSucceeded);
connect(downloadJob.get(), &NetJob::failed, this, &AssetUpdateTask::assetsFailed);
connect(downloadJob.get(), &NetJob::progress, this, &AssetUpdateTask::progress);
- downloadJob->start();
+ downloadJob->start(APPLICATION->network());
return;
}
emitSucceeded();
diff --git a/launcher/minecraft/update/AssetUpdateTask.h b/launcher/minecraft/update/AssetUpdateTask.h
index fdfa8f1c..6d7356f3 100644
--- a/launcher/minecraft/update/AssetUpdateTask.h
+++ b/launcher/minecraft/update/AssetUpdateTask.h
@@ -24,5 +24,5 @@ public slots:
private:
MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
+ NetJob::Ptr downloadJob;
};
diff --git a/launcher/minecraft/update/FMLLibrariesTask.cpp b/launcher/minecraft/update/FMLLibrariesTask.cpp
index 8f1a43ff..a5c6b1e3 100644
--- a/launcher/minecraft/update/FMLLibrariesTask.cpp
+++ b/launcher/minecraft/update/FMLLibrariesTask.cpp
@@ -1,10 +1,12 @@
-#include "Env.h"
-#include <FileSystem.h>
-#include <minecraft/VersionFilterData.h>
#include "FMLLibrariesTask.h"
+
+#include "FileSystem.h"
+#include "minecraft/VersionFilterData.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
+
#include "BuildConfig.h"
+#include "Application.h"
FMLLibrariesTask::FMLLibrariesTask(MinecraftInstance * inst)
{
@@ -60,7 +62,7 @@ void FMLLibrariesTask::executeTask()
// download missing libs to our place
setStatus(tr("Downloading FML libraries..."));
auto dljob = new NetJob("FML libraries");
- auto metacache = ENV.metacache();
+ auto metacache = APPLICATION->metacache();
for (auto &lib : fmlLibsToProcess)
{
auto entry = metacache->resolveEntry("fmllibs", lib.filename);
@@ -72,7 +74,7 @@ void FMLLibrariesTask::executeTask()
connect(dljob, &NetJob::failed, this, &FMLLibrariesTask::fmllibsFailed);
connect(dljob, &NetJob::progress, this, &FMLLibrariesTask::progress);
downloadJob.reset(dljob);
- downloadJob->start();
+ downloadJob->start(APPLICATION->network());
}
bool FMLLibrariesTask::canAbort() const
@@ -87,7 +89,7 @@ void FMLLibrariesTask::fmllibsFinished()
{
setStatus(tr("Copying FML libraries into the instance..."));
MinecraftInstance *inst = (MinecraftInstance *)m_inst;
- auto metacache = ENV.metacache();
+ auto metacache = APPLICATION->metacache();
int index = 0;
for (auto &lib : fmlLibsToProcess)
{
diff --git a/launcher/minecraft/update/FMLLibrariesTask.h b/launcher/minecraft/update/FMLLibrariesTask.h
index a1e70ed4..2e5ad83a 100644
--- a/launcher/minecraft/update/FMLLibrariesTask.h
+++ b/launcher/minecraft/update/FMLLibrariesTask.h
@@ -25,7 +25,7 @@ public slots:
private:
MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
+ NetJob::Ptr downloadJob;
QList<FMLlib> fmlLibsToProcess;
};
diff --git a/launcher/minecraft/update/LibrariesTask.cpp b/launcher/minecraft/update/LibrariesTask.cpp
index 7f66a651..065b4e06 100644
--- a/launcher/minecraft/update/LibrariesTask.cpp
+++ b/launcher/minecraft/update/LibrariesTask.cpp
@@ -1,8 +1,10 @@
-#include "Env.h"
#include "LibrariesTask.h"
+
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
+#include "Application.h"
+
LibrariesTask::LibrariesTask(MinecraftInstance * inst)
{
m_inst = inst;
@@ -21,7 +23,7 @@ void LibrariesTask::executeTask()
auto job = new NetJob(tr("Libraries for instance %1").arg(inst->name()));
downloadJob.reset(job);
- auto metacache = ENV.metacache();
+ auto metacache = APPLICATION->metacache();
auto processArtifactPool = [&](const QList<LibraryPtr> & pool, QStringList & errors, const QString & localPath)
{
@@ -63,7 +65,7 @@ void LibrariesTask::executeTask()
connect(downloadJob.get(), &NetJob::succeeded, this, &LibrariesTask::emitSucceeded);
connect(downloadJob.get(), &NetJob::failed, this, &LibrariesTask::jarlibFailed);
connect(downloadJob.get(), &NetJob::progress, this, &LibrariesTask::progress);
- downloadJob->start();
+ downloadJob->start(APPLICATION->network());
}
bool LibrariesTask::canAbort() const
diff --git a/launcher/minecraft/update/LibrariesTask.h b/launcher/minecraft/update/LibrariesTask.h
index 49f76932..b966ad6c 100644
--- a/launcher/minecraft/update/LibrariesTask.h
+++ b/launcher/minecraft/update/LibrariesTask.h
@@ -22,5 +22,5 @@ public slots:
private:
MinecraftInstance *m_inst;
- NetJobPtr downloadJob;
+ NetJob::Ptr downloadJob;
};