Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into import_zip

This commit is contained in:
Trial97 2023-10-24 10:32:09 +03:00
commit 694840937b
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
22 changed files with 341 additions and 194 deletions

View File

@ -501,7 +501,7 @@ jobs:
export SIGN=1
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
mkdir -p ~/.gnupg/
printf "$GPG_PRIVATE_KEY" | base64 --decode > ~/.gnupg/private.key
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
gpg --import ~/.gnupg/private.key
else
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY

View File

@ -347,6 +347,11 @@ add_subdirectory(program_info)
####################################### Install layout #######################################
set(Launcher_ENABLE_UPDATER NO)
set(Launcher_BUILD_UPDATER NO)
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
set(Launcher_BUILD_UPDATER YES)
endif()
if(NOT (UNIX AND APPLE))
# Install "portable.txt" if selected component is "portable"

View File

@ -30,7 +30,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**,
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
These packages are also availiable to all the distributions based on the ones mentioned above.
These packages are also available to all the distributions based on the ones mentioned above.
## Community & Support

12
flake.lock generated
View File

@ -106,11 +106,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1697009197,
"narHash": "sha256-viVRhBTFT8fPJTb1N3brQIpFZnttmwo3JVKNuWRVc3s=",
"lastModified": 1697886341,
"narHash": "sha256-AdE67xPty9M9wn36nPVp6aDntIdigrs7UbyaGv1VAaM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "01441e14af5e29c9d27ace398e6dd0b293e25a54",
"rev": "44881e03af1c730cbb1d72a4d41274a2c957813a",
"type": "github"
},
"original": {
@ -153,11 +153,11 @@
]
},
"locked": {
"lastModified": 1696846637,
"narHash": "sha256-0hv4kbXxci2+pxhuXlVgftj/Jq79VSmtAyvfabCCtYk=",
"lastModified": 1697746376,
"narHash": "sha256-gu77VkgdfaHgNCVufeb6WP9oqFLjwK4jHcoPZmBVF3E=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "42e1b6095ef80a51f79595d9951eb38e91c4e6ca",
"rev": "8cc349bfd082da8782b989cad2158c9ad5bd70fd",
"type": "github"
},
"original": {

View File

@ -388,7 +388,7 @@ QString BaseInstance::name() const
QString BaseInstance::windowTitle() const
{
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name();
}
// FIXME: why is this here? move it to MinecraftInstance!!!

View File

@ -139,6 +139,7 @@ set(NET_SOURCES
net/HeaderProxy.h
net/RawHeaderProxy.h
net/ApiHeaderProxy.h
net/StaticHeaderProxy.h
net/ApiDownload.h
net/ApiDownload.cpp
net/ApiUpload.cpp
@ -1306,7 +1307,7 @@ install(TARGETS ${Launcher_Name}
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
)
if(NOT APPLE OR (DEFINED Launcher_BUILD_UPDATER AND Launcher_BUILD_UPDATER))
if(Launcher_BUILD_UPDATER)
# Updater
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -106,7 +106,7 @@ void LaunchController::decideAccount()
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
if (instanceAccountIndex == -1) {
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
m_accountToUse = accounts->defaultAccount();
} else {
m_accountToUse = accounts->at(instanceAccountIndex);

View File

@ -48,6 +48,7 @@
#include "FileSystem.h"
#include "net/ApiDownload.h"
#include "net/ChecksumValidator.h"
#include "net/Download.h"
#include "Application.h"

View File

@ -710,7 +710,7 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
{
QString windowParams;
if (settings()->get("LaunchMaximized").toBool())
windowParams = "max";
windowParams = "maximized";
else
windowParams =
QString("%1x%2").arg(settings()->get("MinecraftWinWidth").toInt()).arg(settings()->get("MinecraftWinHeight").toInt());
@ -718,6 +718,19 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS
launchScript += "windowParams " + windowParams + "\n";
}
// launcher info
{
launchScript += "launcherBrand " + BuildConfig.LAUNCHER_NAME + "\n";
launchScript += "launcherVersion " + BuildConfig.printableVersionString() + "\n";
}
// instance info
{
launchScript += "instanceName " + name() + "\n";
launchScript += "instanceIconKey " + name() + "\n";
launchScript += "instanceIconPath icon.png\n"; // we already save a copy here
}
// legacy auth
if (session) {
launchScript += "userName " + session->player_name + "\n";

View File

@ -112,6 +112,8 @@ void NetRequest::executeTask()
m_last_progress_bytes = 0;
QNetworkReply* rep = getReply(request);
if (rep == nullptr) // it failed
return;
m_reply.reset(rep);
connect(rep, &QNetworkReply::downloadProgress, this, &NetRequest::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &NetRequest::downloadFinished);

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "net/HeaderProxy.h"
namespace Net {
class StaticHeaderProxy : public HeaderProxy {
public:
StaticHeaderProxy(QList<HeaderPair> hdrs = {}) : HeaderProxy(), m_hdrs(hdrs){};
virtual ~StaticHeaderProxy() = default;
public:
virtual QList<HeaderPair> headers(const QNetworkRequest&) const override { return m_hdrs; };
void setHeaders(QList<HeaderPair> hdrs) { m_hdrs = hdrs; };
private:
QList<HeaderPair> m_hdrs;
};
} // namespace Net

View File

@ -39,87 +39,76 @@
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QList>
#include <QNetworkRequest>
#include <QStringList>
#include <QUrl>
#include <memory>
#include "Application.h"
#include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
ImgurAlbumCreation::ImgurAlbumCreation(QList<ScreenShot::Ptr> screenshots) : NetAction(), m_screenshots(screenshots)
Net::NetRequest::Ptr ImgurAlbumCreation::make(std::shared_ptr<ImgurAlbumCreation::Result> output, QList<ScreenShot::Ptr> screenshots)
{
m_url = BuildConfig.IMGUR_BASE_URL + "album.json";
m_state = State::Inactive;
auto up = makeShared<ImgurAlbumCreation>();
up->m_url = BuildConfig.IMGUR_BASE_URL + "album.json";
up->m_sink.reset(new Sink(output));
up->m_screenshots = screenshots;
return up;
}
void ImgurAlbumCreation::executeTask()
QNetworkReply* ImgurAlbumCreation::getReply(QNetworkRequest& request)
{
m_state = State::Running;
QNetworkRequest request(m_url);
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
request.setRawHeader("Accept", "application/json");
QStringList hashes;
for (auto shot : m_screenshots) {
hashes.append(shot->m_imgurDeleteHash);
}
const QByteArray data = "deletehashes=" + hashes.join(',').toUtf8() + "&title=Minecraft%20Screenshots&privacy=hidden";
return m_network->post(request, data);
};
QNetworkReply* rep = APPLICATION->network()->post(request, data);
m_reply.reset(rep);
connect(rep, &QNetworkReply::uploadProgress, this, &ImgurAlbumCreation::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &ImgurAlbumCreation::downloadFinished);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
connect(rep, &QNetworkReply::errorOccurred, this, &ImgurAlbumCreation::downloadError);
#else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &ImgurAlbumCreation::downloadError);
#endif
connect(rep, &QNetworkReply::sslErrors, this, &ImgurAlbumCreation::sslErrors);
void ImgurAlbumCreation::init()
{
qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy(
QList<Net::HeaderPair>{ { "Content-Type", "application/x-www-form-urlencoded" },
{ "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
{ "Accept", "application/json" } });
addHeaderProxy(api_headers);
}
void ImgurAlbumCreation::downloadError([[maybe_unused]] QNetworkReply::NetworkError error)
auto ImgurAlbumCreation::Sink::init(QNetworkRequest& request) -> Task::State
{
qDebug() << m_reply->errorString();
m_state = State::Failed;
m_output.clear();
return Task::State::Running;
};
auto ImgurAlbumCreation::Sink::write(QByteArray& data) -> Task::State
{
m_output.append(data);
return Task::State::Running;
}
void ImgurAlbumCreation::downloadFinished()
auto ImgurAlbumCreation::Sink::abort() -> Task::State
{
m_output.clear();
return Task::State::Failed;
}
auto ImgurAlbumCreation::Sink::finalize(QNetworkReply&) -> Task::State
{
if (m_state != State::Failed) {
QByteArray data = m_reply->readAll();
m_reply.reset();
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
qDebug() << jsonError.errorString();
emitFailed();
return;
return Task::State::Failed;
}
auto object = doc.object();
if (!object.value("success").toBool()) {
qDebug() << doc.toJson();
emitFailed();
return;
return Task::State::Failed;
}
m_deleteHash = object.value("data").toObject().value("deletehash").toString();
m_id = object.value("data").toObject().value("id").toString();
m_state = State::Succeeded;
emit succeeded();
return;
} else {
qDebug() << m_reply->readAll();
m_reply.reset();
emitFailed();
return;
}
}
void ImgurAlbumCreation::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
setProgress(bytesReceived, bytesTotal);
emit progress(bytesReceived, bytesTotal);
m_result->deleteHash = object.value("data").toObject().value("deletehash").toString();
m_result->id = object.value("data").toObject().value("id").toString();
return Task::State::Succeeded;
}

View File

@ -36,34 +36,39 @@
#pragma once
#include "Screenshot.h"
#include "net/NetAction.h"
#include "net/NetRequest.h"
typedef shared_qobject_ptr<class ImgurAlbumCreation> ImgurAlbumCreationPtr;
class ImgurAlbumCreation : public NetAction {
class ImgurAlbumCreation : public Net::NetRequest {
public:
explicit ImgurAlbumCreation(QList<ScreenShot::Ptr> screenshots);
static ImgurAlbumCreationPtr make(QList<ScreenShot::Ptr> screenshots)
{
return ImgurAlbumCreationPtr(new ImgurAlbumCreation(screenshots));
}
virtual ~ImgurAlbumCreation() = default;
QString deleteHash() const { return m_deleteHash; }
QString id() const { return m_id; }
struct Result {
QString deleteHash;
QString id;
};
void init() override{};
class Sink : public Net::Sink {
public:
Sink(std::shared_ptr<Result> res) : m_result(res){};
virtual ~Sink() = default;
protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
void downloadFinished() override;
void downloadReadyRead() override {}
public:
auto init(QNetworkRequest& request) -> Task::State override;
auto write(QByteArray& data) -> Task::State override;
auto abort() -> Task::State override;
auto finalize(QNetworkReply& reply) -> Task::State override;
auto hasLocalData() -> bool override { return false; }
public slots:
void executeTask() override;
private:
std::shared_ptr<Result> m_result;
QByteArray m_output;
};
static NetRequest::Ptr make(std::shared_ptr<Result> output, QList<ScreenShot::Ptr> screenshots);
QNetworkReply* getReply(QNetworkRequest& request) override;
void init() override;
private:
QList<ScreenShot::Ptr> m_screenshots;
QString m_deleteHash;
QString m_id;
};

View File

@ -35,8 +35,8 @@
*/
#include "ImgurUpload.h"
#include "Application.h"
#include "BuildConfig.h"
#include "net/StaticHeaderProxy.h"
#include <QDebug>
#include <QFile>
@ -47,104 +47,84 @@
#include <QNetworkRequest>
#include <QUrl>
ImgurUpload::ImgurUpload(ScreenShot::Ptr shot) : NetAction(), m_shot(shot)
void ImgurUpload::init()
{
m_url = BuildConfig.IMGUR_BASE_URL + "upload.json";
m_state = State::Inactive;
qDebug() << "Setting up imgur upload";
auto api_headers = new Net::StaticHeaderProxy(
QList<Net::HeaderPair>{ { "Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str() },
{ "Accept", "application/json" } });
addHeaderProxy(api_headers);
}
void ImgurUpload::executeTask()
QNetworkReply* ImgurUpload::getReply(QNetworkRequest& request)
{
finished = false;
m_state = Task::State::Running;
QNetworkRequest request(m_url);
request.setHeader(QNetworkRequest::UserAgentHeader, APPLICATION->getUserAgentUncached().toUtf8());
request.setRawHeader("Authorization", QString("Client-ID %1").arg(BuildConfig.IMGUR_CLIENT_ID).toStdString().c_str());
request.setRawHeader("Accept", "application/json");
auto file = new QFile(m_fileInfo.absoluteFilePath());
QFile f(m_shot->m_file.absoluteFilePath());
if (!f.open(QFile::ReadOnly)) {
if (!file->open(QFile::ReadOnly)) {
emitFailed();
return;
return nullptr;
}
QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
file->setParent(multipart);
QHttpPart filePart;
filePart.setBody(f.readAll().toBase64());
filePart.setBodyDevice(file);
filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"image\"");
multipart->append(filePart);
QHttpPart typePart;
typePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"type\"");
typePart.setBody("base64");
typePart.setBody("file");
multipart->append(typePart);
QHttpPart namePart;
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"name\"");
namePart.setBody(m_shot->m_file.baseName().toUtf8());
namePart.setBody(m_fileInfo.baseName().toUtf8());
multipart->append(namePart);
QNetworkReply* rep = m_network->post(request, multipart);
return m_network->post(request, multipart);
};
m_reply.reset(rep);
connect(rep, &QNetworkReply::uploadProgress, this, &ImgurUpload::downloadProgress);
connect(rep, &QNetworkReply::finished, this, &ImgurUpload::downloadFinished);
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15
connect(rep, &QNetworkReply::errorOccurred, this, &ImgurUpload::downloadError);
#else
connect(rep, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &ImgurUpload::downloadError);
#endif
connect(rep, &QNetworkReply::sslErrors, this, &ImgurUpload::sslErrors);
}
void ImgurUpload::downloadError([[maybe_unused]] QNetworkReply::NetworkError error)
auto ImgurUpload::Sink::init(QNetworkRequest& request) -> Task::State
{
qCritical() << "ImgurUpload failed with error" << m_reply->errorString() << "Server reply:\n" << m_reply->readAll();
if (finished) {
qCritical() << "Double finished ImgurUpload!";
return;
}
m_state = Task::State::Failed;
finished = true;
m_reply.reset();
emitFailed();
m_output.clear();
return Task::State::Running;
};
auto ImgurUpload::Sink::write(QByteArray& data) -> Task::State
{
m_output.append(data);
return Task::State::Running;
}
void ImgurUpload::downloadFinished()
auto ImgurUpload::Sink::abort() -> Task::State
{
if (finished) {
qCritical() << "Double finished ImgurUpload!";
return;
m_output.clear();
return Task::State::Failed;
}
QByteArray data = m_reply->readAll();
m_reply.reset();
auto ImgurUpload::Sink::finalize(QNetworkReply&) -> Task::State
{
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError);
QJsonDocument doc = QJsonDocument::fromJson(m_output, &jsonError);
if (jsonError.error != QJsonParseError::NoError) {
qDebug() << "imgur server did not reply with JSON" << jsonError.errorString();
finished = true;
m_reply.reset();
emitFailed();
return;
return Task::State::Failed;
}
auto object = doc.object();
if (!object.value("success").toBool()) {
qDebug() << "Screenshot upload not successful:" << doc.toJson();
finished = true;
m_reply.reset();
emitFailed();
return;
return Task::State::Failed;
}
m_shot->m_imgurId = object.value("data").toObject().value("id").toString();
m_shot->m_url = object.value("data").toObject().value("link").toString();
m_shot->m_imgurDeleteHash = object.value("data").toObject().value("deletehash").toString();
m_state = Task::State::Succeeded;
finished = true;
emit succeeded();
return;
return Task::State::Succeeded;
}
void ImgurUpload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Net::NetRequest::Ptr ImgurUpload::make(ScreenShot::Ptr m_shot)
{
setProgress(bytesReceived, bytesTotal);
emit progress(bytesReceived, bytesTotal);
auto up = makeShared<ImgurUpload>(m_shot->m_file);
up->m_url = std::move(BuildConfig.IMGUR_BASE_URL + "upload.json");
up->m_sink.reset(new Sink(m_shot));
return up;
}

View File

@ -35,27 +35,36 @@
#pragma once
#include <QFileInfo>
#include "Screenshot.h"
#include "net/NetAction.h"
#include "net/NetRequest.h"
class ImgurUpload : public NetAction {
class ImgurUpload : public Net::NetRequest {
public:
using Ptr = shared_qobject_ptr<ImgurUpload>;
class Sink : public Net::Sink {
public:
Sink(ScreenShot::Ptr shot) : m_shot(shot){};
virtual ~Sink() = default;
explicit ImgurUpload(ScreenShot::Ptr shot);
static Ptr make(ScreenShot::Ptr shot) { return Ptr(new ImgurUpload(shot)); }
void init() override{};
protected slots:
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) override;
void downloadError(QNetworkReply::NetworkError error) override;
void downloadFinished() override;
void downloadReadyRead() override {}
public slots:
void executeTask() override;
public:
auto init(QNetworkRequest& request) -> Task::State override;
auto write(QByteArray& data) -> Task::State override;
auto abort() -> Task::State override;
auto finalize(QNetworkReply& reply) -> Task::State override;
auto hasLocalData() -> bool override { return false; }
private:
ScreenShot::Ptr m_shot;
bool finished = true;
QByteArray m_output;
};
ImgurUpload(QFileInfo info) : m_fileInfo(info) {}
virtual ~ImgurUpload() = default;
static NetRequest::Ptr make(ScreenShot::Ptr m_shot);
void init() override;
private:
virtual QNetworkReply* getReply(QNetworkRequest&) override;
const QFileInfo m_fileInfo;
};

View File

@ -85,6 +85,7 @@ QString getCreditsHtml()
stream << QString("<p>TayouVR %1</p>\n").arg(getGitHub("TayouVR"));
stream << QString("<p>TheKodeToad %1</p>\n").arg(getGitHub("TheKodeToad"));
stream << QString("<p>getchoo %1</p>\n").arg(getGitHub("getchoo"));
stream << QString("<p>Alexandru Tripon (Trial97) %1</p>\n").arg(getGitHub("Trial97"));
stream << "<br />\n";
// TODO: possibly retrieve from git history at build time?

View File

@ -39,6 +39,7 @@
#include <QApplication>
#include <QCache>
#include <QDrag>
#include <QFont>
#include <QListView>
#include <QMimeData>
#include <QMouseEvent>
@ -478,6 +479,38 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event)
#endif
option.widget = this;
if (model()->rowCount() == 0) {
painter.save();
const QString line1 = tr("Welcome!");
const QString line2 = tr("Click \"Add Instance\" to get started.");
auto rect = this->viewport()->rect();
auto font = option.font;
font.setPointSize(37);
painter.setFont(font);
auto fm = painter.fontMetrics();
if (rect.height() <= (fm.height() * 5) || rect.width() <= fm.horizontalAdvance(line2)) {
auto s = rect.height() / (5. * fm.height());
auto sx = rect.width() * 1. / fm.horizontalAdvance(line2);
if (s >= sx)
s = sx;
auto ps = font.pointSize() * s;
if (ps <= 0)
ps = 1;
font.setPointSize(ps);
painter.setFont(font);
fm = painter.fontMetrics();
}
// text
rect.setTop(rect.top() + fm.height() * 1.5);
painter.drawText(rect, Qt::AlignHCenter, line1);
rect.setTop(rect.top() + fm.height());
painter.drawText(rect, Qt::AlignHCenter, line2);
painter.restore();
return;
}
int wpWidth = viewport()->width();
option.rect.setWidth(wpWidth);
for (int i = 0; i < m_groups.size(); ++i) {

View File

@ -383,20 +383,31 @@ void ScreenshotsPage::on_actionUpload_triggered()
QList<ScreenShot::Ptr> uploaded;
auto job = NetJob::Ptr(new NetJob("Screenshot Upload", APPLICATION->network()));
ProgressDialog dialog(this);
dialog.setSkipButton(true, tr("Abort"));
if (selection.size() < 2) {
auto item = selection.at(0);
auto info = m_model->fileInfo(item);
auto screenshot = std::make_shared<ScreenShot>(info);
job->addNetAction(ImgurUpload::make(screenshot));
m_uploadActive = true;
ProgressDialog dialog(this);
connect(job.get(), &Task::failed, [this](QString reason) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show();
});
connect(job.get(), &Task::aborted, [this] {
CustomMessageBox::selectable(this, tr("Screenshots upload aborted"), tr("The task has been aborted by the user."),
QMessageBox::Information)
->show();
});
if (dialog.execWithTask(job.get()) != QDialog::Accepted) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec();
} else {
m_uploadActive = true;
if (dialog.execWithTask(job.get()) == QDialog::Accepted) {
auto link = screenshot->m_url;
QClipboard* clipboard = QApplication::clipboard();
qDebug() << "ImgurUpload link" << link;
clipboard->setText(link);
CustomMessageBox::selectable(
this, tr("Upload finished"),
@ -417,23 +428,37 @@ void ScreenshotsPage::on_actionUpload_triggered()
}
SequentialTask task;
auto albumTask = NetJob::Ptr(new NetJob("Imgur Album Creation", APPLICATION->network()));
auto imgurAlbum = ImgurAlbumCreation::make(uploaded);
auto imgurResult = std::make_shared<ImgurAlbumCreation::Result>();
auto imgurAlbum = ImgurAlbumCreation::make(imgurResult, uploaded);
albumTask->addNetAction(imgurAlbum);
task.addTask(job);
task.addTask(albumTask);
connect(&task, &Task::failed, [this](QString reason) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), reason, QMessageBox::Critical)->show();
});
connect(&task, &Task::aborted, [this] {
CustomMessageBox::selectable(this, tr("Screenshots upload aborted"), tr("The task has been aborted by the user."),
QMessageBox::Information)
->show();
});
m_uploadActive = true;
ProgressDialog prog(this);
if (prog.execWithTask(&task) != QDialog::Accepted) {
if (dialog.execWithTask(&task) == QDialog::Accepted) {
if (imgurResult->id.isEmpty()) {
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"), tr("Unknown error"), QMessageBox::Warning)->exec();
} else {
auto link = QString("https://imgur.com/a/%1").arg(imgurAlbum->id());
auto link = QString("https://imgur.com/a/%1").arg(imgurResult->id);
qDebug() << "ImgurUpload link" << link;
QClipboard* clipboard = QApplication::clipboard();
clipboard->setText(link);
CustomMessageBox::selectable(this, tr("Upload finished"),
CustomMessageBox::selectable(
this, tr("Upload finished"),
tr("The <a href=\"%1\">link to the uploaded album</a> has been placed in your clipboard.").arg(link),
QMessageBox::Information)
->exec();
}
}
m_uploadActive = false;
}

View File

@ -90,7 +90,7 @@ void ImportFTBPage::suggestCurrent()
}
dialog->setSuggestedPack(selected.name, new PackInstallTask(selected));
QString editedLogoName = QString("ftb_%1_%2,jpg").arg(selected.name, selected.id);
QString editedLogoName = QString("ftb_%1_%2.jpg").arg(selected.name, QString::number(selected.id));
dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName);
}

View File

@ -165,11 +165,15 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest
QString path = FS::PathCombine("themes", m_id);
QString pathResources = FS::PathCombine("themes", m_id, "resources");
if (!FS::ensureFolderPathExists(path) || !FS::ensureFolderPathExists(pathResources)) {
themeWarningLog() << "couldn't create folder for theme!";
if (!FS::ensureFolderPathExists(path)) {
themeWarningLog() << "Theme directory for" << m_id << "could not be created. This theme might be invalid";
return;
}
if (!FS::ensureFolderPathExists(pathResources)) {
themeWarningLog() << "Resources directory for" << m_id << "could not be created";
}
auto themeFilePath = FS::PathCombine(path, themeFile);
bool jsonDataIncomplete = false;
@ -230,7 +234,11 @@ CustomTheme::CustomTheme(ITheme* baseTheme, QFileInfo& fileInfo, bool isManifest
QStringList CustomTheme::searchPaths()
{
return { FS::PathCombine("themes", m_id, "resources") };
QString pathResources = FS::PathCombine("themes", m_id, "resources");
if (QFileInfo::exists(pathResources))
return { pathResources };
return {};
}
QString CustomTheme::id()

View File

@ -105,13 +105,16 @@ public final class EntryPoint {
return ExitCode.ABORT;
}
setProperties(params);
String launcherType = params.getString("launcher");
try {
LegacyProxy.applyOnlineFixes(params);
Launcher launcher;
String type = params.getString("launcher");
switch (type) {
switch (launcherType) {
case "standard":
launcher = new StandardLauncher(params);
break;
@ -121,7 +124,7 @@ public final class EntryPoint {
break;
default:
throw new IllegalArgumentException("Invalid launcher type: " + type);
throw new IllegalArgumentException("Invalid launcher type: " + launcherType);
}
launcher.launch();
@ -138,6 +141,39 @@ public final class EntryPoint {
}
}
private static void setProperties(Parameters params) {
String launcherBrand = params.getString("launcherBrand", null);
String launcherVersion = params.getString("launcherVersion", null);
String name = params.getString("instanceName", null);
String iconId = params.getString("instanceIconKey", null);
String iconPath = params.getString("instanceIconPath", null);
String windowTitle = params.getString("windowTitle", null);
String windowDimensions = params.getString("windowParams", null);
if (launcherBrand != null)
System.setProperty("minecraft.launcher.brand", launcherBrand);
if (launcherVersion != null)
System.setProperty("minecraft.launcher.version", launcherVersion);
// set useful properties for mods
if (name != null)
System.setProperty("org.prismlauncher.instance.name", name);
if (iconId != null)
System.setProperty("org.prismlauncher.instance.icon.id", iconId);
if (iconPath != null)
System.setProperty("org.prismlauncher.instance.icon.path", iconPath);
if (windowTitle != null)
System.setProperty("org.prismlauncher.window.title", windowTitle);
if (windowDimensions != null)
System.setProperty("org.prismlauncher.window.dimensions", windowDimensions);
// set multimc properties for compatibility
if (name != null)
System.setProperty("multimc.instance.title", name);
if (iconId != null)
System.setProperty("multimc.instance.icon", iconId);
}
private static PreLaunchAction parseLine(String input, Parameters params) throws ParseException {
switch (input) {
case "":

View File

@ -83,7 +83,7 @@ public abstract class AbstractLauncher implements Launcher {
String windowParams = params.getString("windowParams", null);
if ("max".equals(windowParams) || windowParams == null) {
if ("maximized".equals(windowParams) || windowParams == null) {
maximize = windowParams != null;
width = DEFAULT_WINDOW_WIDTH;