Merge branch 'MultiMC:develop' into develop

This commit is contained in:
Sebastian
2021-09-24 16:25:17 -05:00
committed by GitHub
14 changed files with 150 additions and 297 deletions

View File

@@ -15,6 +15,7 @@ MultiMC is a portable application and is not supposed to be installed into any s
That would be anything outside your home folder. Before running `make install`, make sure
you set the install path to something you have write access to. Never build this under
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
Also note that this guide is for development purposes only. No support is given for building your own fork or special build for any reason whatsoever.
# Getting the source

View File

@@ -653,6 +653,7 @@ SET(MULTIMC_SOURCES
pages/instance/VersionPage.h
pages/instance/TexturePackPage.h
pages/instance/ResourcePackPage.h
pages/instance/ShaderPackPage.h
pages/instance/ModFolderPage.cpp
pages/instance/ModFolderPage.h
pages/instance/NotesPage.cpp

View File

@@ -9,6 +9,7 @@
#include "pages/instance/ModFolderPage.h"
#include "pages/instance/ResourcePackPage.h"
#include "pages/instance/TexturePackPage.h"
#include "pages/instance/ShaderPackPage.h"
#include "pages/instance/NotesPage.h"
#include "pages/instance/ScreenshotsPage.h"
#include "pages/instance/InstanceSettingsPage.h"
@@ -44,6 +45,7 @@ public:
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
values.append(new ResourcePackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get()));
values.append(new ShaderPackPage(onesix.get()));
values.append(new NotesPage(onesix.get()));
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
values.append(new ServersPage(onesix));

View File

@@ -223,6 +223,11 @@ QString MinecraftInstance::texturePacksDir() const
return FS::PathCombine(gameRoot(), "texturepacks");
}
QString MinecraftInstance::shaderPacksDir() const
{
return FS::PathCombine(gameRoot(), "shaderpacks");
}
QString MinecraftInstance::instanceConfigFolder() const
{
return FS::PathCombine(gameRoot(), "config");
@@ -1027,6 +1032,17 @@ std::shared_ptr<ModFolderModel> MinecraftInstance::texturePackList() const
return m_texture_pack_list;
}
std::shared_ptr<ModFolderModel> MinecraftInstance::shaderPackList() const
{
if (!m_shader_pack_list)
{
m_shader_pack_list.reset(new ResourcePackFolderModel(shaderPacksDir()));
m_shader_pack_list->disableInteraction(isRunning());
connect(this, &BaseInstance::runningStatusChanged, m_shader_pack_list.get(), &ModFolderModel::disableInteraction);
}
return m_shader_pack_list;
}
std::shared_ptr<WorldList> MinecraftInstance::worldList() const
{
if (!m_world_list)

View File

@@ -40,6 +40,7 @@ public:
QString jarModsDir() const;
QString resourcePacksDir() const;
QString texturePacksDir() const;
QString shaderPacksDir() const;
QString loaderModsDir() const;
QString coreModsDir() const;
QString modsCacheLocation() const;
@@ -72,6 +73,7 @@ public:
std::shared_ptr<ModFolderModel> coreModList() const;
std::shared_ptr<ModFolderModel> resourcePackList() const;
std::shared_ptr<ModFolderModel> texturePackList() const;
std::shared_ptr<ModFolderModel> shaderPackList() const;
std::shared_ptr<WorldList> worldList() const;
std::shared_ptr<GameOptions> gameOptionsModel() const;
@@ -125,6 +127,7 @@ protected: // data
mutable std::shared_ptr<ModFolderModel> m_loader_mod_list;
mutable std::shared_ptr<ModFolderModel> m_core_mod_list;
mutable std::shared_ptr<ModFolderModel> m_resource_pack_list;
mutable std::shared_ptr<ModFolderModel> m_shader_pack_list;
mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list;
mutable std::shared_ptr<GameOptions> m_game_options;

View File

@@ -23,7 +23,6 @@
#include "Env.h"
using OAuth2 = Katabasis::OAuth2;
using Requestor = AuthRequest;
using Activity = Katabasis::Activity;
AuthContext::AuthContext(AccountData * data, QObject *parent) :
@@ -164,8 +163,8 @@ void AuthContext::doUserAuth() {
QNetworkRequest request = QNetworkRequest(QUrl("https://user.auth.xboxlive.com/user/authenticate"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
auto *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onUserAuthDone);
auto *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onUserAuthDone);
requestor->post(request, xbox_auth_data.toUtf8());
qDebug() << "First layer of XBox auth ... commencing.";
}
@@ -358,8 +357,8 @@ void AuthContext::doSTSAuthMinecraft() {
QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onSTSAuthMinecraftDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onSTSAuthMinecraftDone);
requestor->post(request, xbox_auth_data.toUtf8());
qDebug() << "Getting Minecraft services STS token...";
}
@@ -428,8 +427,8 @@ void AuthContext::doMinecraftAuth() {
QNetworkRequest request = QNetworkRequest(QUrl("https://api.minecraftservices.com/authentication/login_with_xbox"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onMinecraftAuthDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onMinecraftAuthDone);
requestor->post(request, data.toUtf8());
qDebug() << "Getting Minecraft access token...";
}
@@ -518,8 +517,8 @@ void AuthContext::doSTSAuthGeneric() {
QNetworkRequest request = QNetworkRequest(QUrl("https://xsts.auth.xboxlive.com/xsts/authorize"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Accept", "application/json");
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onSTSAuthGenericDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onSTSAuthGenericDone);
requestor->post(request, xbox_auth_data.toUtf8());
qDebug() << "Getting generic STS token...";
}
@@ -574,8 +573,8 @@ void AuthContext::doXBoxProfile() {
request.setRawHeader("Accept", "application/json");
request.setRawHeader("x-xbl-contract-version", "3");
request.setRawHeader("Authorization", QString("XBL3.0 x=%1;%2").arg(m_data->userToken.extra["uhs"].toString(), m_data->xboxApiToken.token).toUtf8());
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onXBoxProfileDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onXBoxProfileDone);
requestor->get(request);
qDebug() << "Getting Xbox profile...";
}
@@ -753,8 +752,8 @@ void AuthContext::doMinecraftProfile() {
// request.setRawHeader("Accept", "application/json");
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onMinecraftProfileDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onMinecraftProfileDone);
requestor->get(request);
}
@@ -801,8 +800,8 @@ void AuthContext::doMigrationEligibilityCheck() {
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onMigrationEligibilityCheckDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onMigrationEligibilityCheckDone);
requestor->get(request);
}
@@ -853,8 +852,8 @@ void AuthContext::doGetSkin() {
auto url = QUrl(m_data->minecraftProfile.skin.url);
QNetworkRequest request = QNetworkRequest(url);
Requestor *requestor = new Requestor(this);
connect(requestor, &Requestor::finished, this, &AuthContext::onSkinDone);
AuthRequest *requestor = new AuthRequest(this);
connect(requestor, &AuthRequest::finished, this, &AuthContext::onSkinDone);
requestor->get(request);
}

View File

@@ -108,6 +108,10 @@ void PackInstallTask::downloadPack()
auto relpath = FS::PathCombine("minecraft", file.path, file.name);
auto path = FS::PathCombine(m_stagingPath, relpath);
if (filesToCopy.contains(entry->getFullPath())) {
qWarning() << "Ignoring" << file.url << "as a file of that path is already downloading.";
continue;
}
qDebug() << "Will download" << file.url << "to" << path;
filesToCopy[entry->getFullPath()] = path;

View File

@@ -22,7 +22,7 @@ deploy() {
runmmc() {
cd ${INSTDIR}
./MultiMC "$@"
exec ./MultiMC "$@"
}
if [[ ! -f ${INSTDIR}/MultiMC ]]; then

View File

@@ -0,0 +1,22 @@
#pragma once
#include "ModFolderPage.h"
#include "ui_ModFolderPage.h"
class ShaderPackPage : public ModFolderPage
{
Q_OBJECT
public:
explicit ShaderPackPage(MinecraftInstance *instance, QWidget *parent = 0)
: ModFolderPage(instance, instance->shaderPackList(), "shaderpacks",
"shaderpacks", tr("Shader packs"), "Resource-packs", parent)
{
ui->actionView_configs->setVisible(false);
}
virtual ~ShaderPackPage() {}
virtual bool shouldDisplay() const override
{
return true;
}
};

View File

@@ -30,6 +30,7 @@
<file>scalable/resourcepacks.svg</file>
<file>scalable/screenshots.svg</file>
<file>scalable/settings.svg</file>
<file>scalable/shaderpacks.svg</file>
<file>scalable/status-bad.svg</file>
<file>scalable/status-good.svg</file>
<file>scalable/status-yellow.svg</file>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xml:space="preserve"
enable-background="new 0 0 32 32"
viewBox="0 0 32 32"
y="0px"
x="0px"
id="Calque_1"
version="1.1"
sodipodi:docname="shaderpacks.svg"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="14.584077"
inkscape:cx="24.478752"
inkscape:cy="13.165043"
inkscape:window-width="2399"
inkscape:window-height="1183"
inkscape:window-x="2321"
inkscape:window-y="798"
inkscape:window-maximized="0"
inkscape:current-layer="Calque_1" /><metadata
id="metadata19"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs17"><linearGradient
id="linearGradient3249"
inkscape:swatch="solid"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3247" /></linearGradient>
</defs><g
id="g6"><path
id="path2"
d="M26,0H6C2.7,0,0,2.7,0,6v3h32V6C32,2.7,29.3,0,26,0z"
fill="#39B54A" /><path
id="path4"
d="M0,26c0,3.3,2.7,6,6,6h20c3.3,0,6-2.7,6-6V9H0V26z"
fill="#8C6239" /></g><path
fill="#F2F2F2"
fill-rule="evenodd"
clip-rule="evenodd"
id="path8"
d="M28,6c0-1.1-0.9-2-2-2H6C4.9,4,4,4.9,4,6v20c0,1.1,0.9,2,2,2h20 c1.1,0,2-0.9,2-2V6z" /><rect
x="6.0678372"
y="6.0678444"
fill-rule="evenodd"
clip-rule="evenodd"
fill="none"
width="19.864313"
height="19.864313"
id="rect838"
style="stroke-width:1.98049" /><path
id="polygon840"
style="clip-rule:evenodd;fill:#294eab;fill-rule:evenodd;stroke-width:3.19043"
transform="matrix(0.62075979,0,0,0.62075979,293.47962,-158.4335)"
d="m -433,272.1 v 17.8 l -14,7.1 v -17.8 z" /><path
id="polygon842"
style="clip-rule:evenodd;fill:#39b54a;fill-rule:evenodd;stroke-width:3.19043"
transform="matrix(0.62075979,0,0,0.62075979,293.47962,-158.4335)"
d="m -461,272.1 v 17.8 l 14,7.1 v -17.8 z" /><path
id="polygon844"
style="clip-rule:evenodd;fill:#ed4c31;fill-rule:evenodd;stroke-width:3.19043"
transform="matrix(0.62075979,0,0,0.62075979,293.47962,-158.4335)"
d="m -447,265 -14,7.1 14,7.1 14,-7.1 z" /></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -28,24 +28,19 @@ find_package(Qt5 COMPONENTS Core Network REQUIRED)
set( katabasis_PRIVATE
src/OAuth2.cpp
src/JsonResponse.cpp
src/JsonResponse.h
src/PollServer.cpp
src/Reply.cpp
src/ReplyServer.cpp
src/Requestor.cpp
)
set( katabasis_PUBLIC
include/katabasis/OAuth2.h
include/katabasis/Globals.h
include/katabasis/PollServer.h
include/katabasis/Reply.h
include/katabasis/ReplyServer.h
include/katabasis/Requestor.h
include/katabasis/RequestParameter.h
)

View File

@@ -1,79 +0,0 @@
#pragma once
#include <QObject>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QByteArray>
#include <QHttpMultiPart>
#include "Reply.h"
namespace Katabasis {
class OAuth2;
/// Makes authenticated requests.
class Requestor: public QObject {
Q_OBJECT
public:
explicit Requestor(QNetworkAccessManager *manager, OAuth2 *authenticator, QObject *parent = 0);
~Requestor();
public slots:
int get(const QNetworkRequest &req, int timeout = 60*1000);
int post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000);
signals:
/// Emitted when a request has been completed or failed.
void finished(int id, QNetworkReply::NetworkError error, QByteArray data, QList<QNetworkReply::RawHeaderPair> headers);
/// Emitted when an upload has progressed.
void uploadProgress(int id, qint64 bytesSent, qint64 bytesTotal);
protected slots:
/// Handle refresh completion.
void onRefreshFinished(QNetworkReply::NetworkError error);
/// Handle request finished.
void onRequestFinished();
/// Handle request error.
void onRequestError(QNetworkReply::NetworkError error);
/// Handle ssl errors.
void onSslErrors(QList<QSslError> errors);
/// Re-try request (after successful token refresh).
void retry();
/// Finish the request, emit finished() signal.
void finish();
/// Handle upload progress.
void onUploadProgress(qint64 uploaded, qint64 total);
protected:
int setup(const QNetworkRequest &request, QNetworkAccessManager::Operation operation, const QByteArray &verb = QByteArray());
enum Status {
Idle, Requesting, ReRequesting
};
QNetworkAccessManager *manager_;
OAuth2 *authenticator_;
QNetworkRequest request_;
QByteArray data_;
QNetworkReply *reply_;
Status status_;
int id_;
QNetworkAccessManager::Operation operation_;
QUrl url_;
ReplyList timedReplies_;
QNetworkReply::NetworkError error_;
};
}

View File

@@ -1,195 +0,0 @@
#include <cassert>
#include <QDebug>
#include <QTimer>
#include <QBuffer>
#include <QUrlQuery>
#include "katabasis/Requestor.h"
#include "katabasis/OAuth2.h"
#include "katabasis/Globals.h"
namespace Katabasis {
Requestor::Requestor(QNetworkAccessManager *manager, OAuth2 *authenticator, QObject *parent): QObject(parent), reply_(NULL), status_(Idle) {
manager_ = manager;
authenticator_ = authenticator;
if (authenticator) {
timedReplies_.setIgnoreSslErrors(authenticator->ignoreSslErrors());
}
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
connect(authenticator, &OAuth2::refreshFinished, this, &Requestor::onRefreshFinished);
}
Requestor::~Requestor() {
}
int Requestor::get(const QNetworkRequest &req, int timeout/* = 60*1000*/) {
if (-1 == setup(req, QNetworkAccessManager::GetOperation)) {
return -1;
}
reply_ = manager_->get(request_);
timedReplies_.add(new Reply(reply_, timeout));
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &Requestor::onSslErrors);
return id_;
}
int Requestor::post(const QNetworkRequest &req, const QByteArray &data, int timeout/* = 60*1000*/) {
if (-1 == setup(req, QNetworkAccessManager::PostOperation)) {
return -1;
}
data_ = data;
reply_ = manager_->post(request_, data_);
timedReplies_.add(new Reply(reply_, timeout));
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)));
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()));
connect(reply_, &QNetworkReply::sslErrors, this, &Requestor::onSslErrors);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
return id_;
}
void Requestor::onRefreshFinished(QNetworkReply::NetworkError error) {
if (status_ != Requesting) {
qWarning() << "O2Requestor::onRefreshFinished: No pending request";
return;
}
if (QNetworkReply::NoError == error) {
QTimer::singleShot(100, this, &Requestor::retry);
} else {
error_ = error;
QTimer::singleShot(10, this, &Requestor::finish);
}
}
void Requestor::onRequestFinished() {
if (status_ == Idle) {
return;
}
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
if (reply_->error() == QNetworkReply::NoError) {
QTimer::singleShot(10, this, SLOT(finish()));
}
}
void Requestor::onRequestError(QNetworkReply::NetworkError error) {
qWarning() << "O2Requestor::onRequestError: Error" << (int)error;
if (status_ == Idle) {
return;
}
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
qWarning() << "O2Requestor::onRequestError: Error string: " << reply_->errorString();
int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qWarning() << "O2Requestor::onRequestError: HTTP status" << httpStatus << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
if ((status_ == Requesting) && (httpStatus == 401)) {
// Call OAuth2::refresh. Note the O2 instance might live in a different thread
if (QMetaObject::invokeMethod(authenticator_, "refresh")) {
return;
}
qCritical() << "O2Requestor::onRequestError: Invoking remote refresh failed";
}
error_ = error;
QTimer::singleShot(10, this, SLOT(finish()));
}
void Requestor::onSslErrors(QList<QSslError> errors) {
int i = 1;
for (auto error : errors) {
qCritical() << "LOGIN SSL Error #" << i << " : " << error.errorString();
auto cert = error.certificate();
qCritical() << "Certificate in question:\n" << cert.toText();
i++;
}
}
void Requestor::onUploadProgress(qint64 uploaded, qint64 total) {
if (status_ == Idle) {
qWarning() << "O2Requestor::onUploadProgress: No pending request";
return;
}
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
// Restart timeout because request in progress
Reply *o2Reply = timedReplies_.find(reply_);
if(o2Reply)
o2Reply->start();
emit uploadProgress(id_, uploaded, total);
}
int Requestor::setup(const QNetworkRequest &req, QNetworkAccessManager::Operation operation, const QByteArray &verb) {
static int currentId;
if (status_ != Idle) {
qWarning() << "O2Requestor::setup: Another request pending";
return -1;
}
request_ = req;
operation_ = operation;
id_ = currentId++;
url_ = req.url();
QUrl url = url_;
request_.setUrl(url);
if (!verb.isEmpty()) {
request_.setRawHeader(HTTP_HTTP_HEADER, verb);
}
status_ = Requesting;
error_ = QNetworkReply::NoError;
return id_;
}
void Requestor::finish() {
QByteArray data;
if (status_ == Idle) {
qWarning() << "O2Requestor::finish: No pending request";
return;
}
data = reply_->readAll();
status_ = Idle;
timedReplies_.remove(reply_);
reply_->disconnect(this);
reply_->deleteLater();
QList<QNetworkReply::RawHeaderPair> headers = reply_->rawHeaderPairs();
emit finished(id_, error_, data, headers);
}
void Requestor::retry() {
if (status_ != Requesting) {
qWarning() << "O2Requestor::retry: No pending request";
return;
}
timedReplies_.remove(reply_);
reply_->disconnect(this);
reply_->deleteLater();
QUrl url = url_;
request_.setUrl(url);
status_ = ReRequesting;
switch (operation_) {
case QNetworkAccessManager::GetOperation:
reply_ = manager_->get(request_);
break;
case QNetworkAccessManager::PostOperation:
reply_ = manager_->post(request_, data_);
break;
default:
assert(!"Unspecified operation for request");
reply_ = manager_->get(request_);
break;
}
timedReplies_.add(reply_);
connect(reply_, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onRequestError(QNetworkReply::NetworkError)), Qt::QueuedConnection);
connect(reply_, SIGNAL(finished()), this, SLOT(onRequestFinished()), Qt::QueuedConnection);
connect(reply_, SIGNAL(uploadProgress(qint64,qint64)), this, SLOT(onUploadProgress(qint64,qint64)));
}
}