Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into import_zip
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
commit
42b06674bb
2
.github/workflows/backport.yml
vendored
2
.github/workflows/backport.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
- name: Create backport PRs
|
- name: Create backport PRs
|
||||||
uses: korthout/backport-action@v1.3.1
|
uses: korthout/backport-action@v1.4.0
|
||||||
with:
|
with:
|
||||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||||
pull_description: |-
|
pull_description: |-
|
||||||
|
@ -67,5 +67,16 @@
|
|||||||
<string>Alternate</string>
|
<string>Alternate</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>Curseforge</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>curseforge</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -194,8 +194,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
{ { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" },
|
||||||
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
{ { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" },
|
||||||
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
{ "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" },
|
||||||
{ { "I", "import" }, "Import instance from specified zip (local path or URL)", "file" },
|
{ { "I", "import" }, "Import instance or resource from specified local path or URL", "url" },
|
||||||
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
{ "show", "Opens the window for the specified instance (by instance ID)", "show" } });
|
||||||
|
// Has to be positional for some OS to handle that properly
|
||||||
|
parser.addPositionalArgument("URL", "Import the resource(s) at the given URL(s) (same as -I / --import)", "[URL...]");
|
||||||
|
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
|
|
||||||
@ -208,13 +211,13 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_instanceIdToShowWindowOf = parser.value("show");
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
|
|
||||||
for (auto zip_path : parser.values("import")) {
|
for (auto url : parser.values("import")) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// treat unspecified positional arguments as import urls
|
// treat unspecified positional arguments as import urls
|
||||||
for (auto zip_path : parser.positionalArguments()) {
|
for (auto url : parser.positionalArguments()) {
|
||||||
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
m_urlsToImport.append(normalizeImportUrl(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
@ -313,11 +316,11 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv)
|
|||||||
activate.command = "activate";
|
activate.command = "activate";
|
||||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||||
|
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
for (auto zip_url : m_zipsToImport) {
|
for (auto url : m_urlsToImport) {
|
||||||
ApplicationMessage import;
|
ApplicationMessage import;
|
||||||
import.command = "import";
|
import.command = "import";
|
||||||
import.args.insert("path", zip_url.toString());
|
import.args.insert("url", url.toString());
|
||||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -978,9 +981,9 @@ void Application::performMainStartupAction()
|
|||||||
showMainWindow(false);
|
showMainWindow(false);
|
||||||
qDebug() << "<> Main window shown.";
|
qDebug() << "<> Main window shown.";
|
||||||
}
|
}
|
||||||
if (!m_zipsToImport.isEmpty()) {
|
if (!m_urlsToImport.isEmpty()) {
|
||||||
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
qDebug() << "<> Importing from url:" << m_urlsToImport;
|
||||||
m_mainWindow->processURLs(m_zipsToImport);
|
m_mainWindow->processURLs(m_urlsToImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1022,12 +1025,12 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
if (command == "activate") {
|
if (command == "activate") {
|
||||||
showMainWindow();
|
showMainWindow();
|
||||||
} else if (command == "import") {
|
} else if (command == "import") {
|
||||||
QString path = received.args["path"];
|
QString url = received.args["url"];
|
||||||
if (path.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
m_mainWindow->processURLs({ normalizeImportUrl(url) });
|
||||||
} else if (command == "launch") {
|
} else if (command == "launch") {
|
||||||
QString id = received.args["id"];
|
QString id = received.args["id"];
|
||||||
QString server = received.args["server"];
|
QString server = received.args["server"];
|
||||||
@ -1590,3 +1593,13 @@ void Application::triggerUpdateCheck()
|
|||||||
qDebug() << "Updater not available.";
|
qDebug() << "Updater not available.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl Application::normalizeImportUrl(QString const& url)
|
||||||
|
{
|
||||||
|
auto local_file = QFileInfo(url);
|
||||||
|
if (local_file.exists()) {
|
||||||
|
return QUrl::fromLocalFile(local_file.absoluteFilePath());
|
||||||
|
} else {
|
||||||
|
return QUrl::fromUserInput(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -177,6 +177,8 @@ class Application : public QApplication {
|
|||||||
|
|
||||||
int suitableMaxMem();
|
int suitableMaxMem();
|
||||||
|
|
||||||
|
QUrl normalizeImportUrl(QString const& url);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
@ -279,7 +281,7 @@ class Application : public QApplication {
|
|||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QList<QUrl> m_zipsToImport;
|
QList<QUrl> m_urlsToImport;
|
||||||
QString m_instanceIdToShowWindowOf;
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
#include "net/ApiDownload.h"
|
#include "net/ApiDownload.h"
|
||||||
|
|
||||||
@ -83,25 +84,30 @@ void InstanceImportTask::executeTask()
|
|||||||
} else {
|
} else {
|
||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
|
|
||||||
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
downloadFromUrl();
|
||||||
|
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
|
||||||
entry->setStale(true);
|
|
||||||
m_archivePath = entry->getFullPath();
|
|
||||||
|
|
||||||
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
|
|
||||||
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
|
||||||
|
|
||||||
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
|
|
||||||
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
|
|
||||||
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
|
||||||
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
|
|
||||||
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
|
|
||||||
task.reset(filesNetJob);
|
|
||||||
filesNetJob->start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadFromUrl()
|
||||||
|
{
|
||||||
|
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||||
|
|
||||||
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
|
entry->setStale(true);
|
||||||
|
m_archivePath = entry->getFullPath();
|
||||||
|
|
||||||
|
auto filesNetJob = makeShared<NetJob>(tr("Modpack download"), APPLICATION->network());
|
||||||
|
filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||||
|
|
||||||
|
connect(filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::processZipPack);
|
||||||
|
connect(filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::setProgress);
|
||||||
|
connect(filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||||
|
connect(filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::emitFailed);
|
||||||
|
connect(filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::emitAborted);
|
||||||
|
task.reset(filesNetJob);
|
||||||
|
filesNetJob->start();
|
||||||
|
}
|
||||||
|
|
||||||
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
|
QString InstanceImportTask::getRootFromZip(QuaZip* zip, const QString& root)
|
||||||
{
|
{
|
||||||
if (!isRunning()) {
|
if (!isRunning()) {
|
||||||
|
@ -88,4 +88,5 @@ class InstanceImportTask : public InstanceTask {
|
|||||||
|
|
||||||
// FIXME: nuke
|
// FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
|
void downloadFromUrl();
|
||||||
};
|
};
|
||||||
|
@ -1005,15 +1005,30 @@ static Meta::Version::Ptr getComponentVersion(const QString& uid, const QString&
|
|||||||
if (!vlist)
|
if (!vlist)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!vlist->isLoaded())
|
if (!vlist->isLoaded()) {
|
||||||
vlist->load(Net::Mode::Online);
|
QEventLoop loadVersionLoop;
|
||||||
|
auto task = vlist->getLoadTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
auto ver = vlist->getVersion(version);
|
auto ver = vlist->getVersion(version);
|
||||||
if (!ver)
|
if (!ver)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!ver->isLoaded())
|
if (!ver->isLoaded()) {
|
||||||
|
QEventLoop loadVersionLoop;
|
||||||
ver->load(Net::Mode::Online);
|
ver->load(Net::Mode::Online);
|
||||||
|
auto task = ver->getCurrentTask();
|
||||||
|
QObject::connect(task.get(), &Task::finished, &loadVersionLoop, &QEventLoop::quit);
|
||||||
|
if (!task->isRunning())
|
||||||
|
task->start();
|
||||||
|
|
||||||
|
loadVersionLoop.exec();
|
||||||
|
}
|
||||||
|
|
||||||
return ver;
|
return ver;
|
||||||
}
|
}
|
||||||
|
@ -204,6 +204,17 @@ Task::Ptr FlameAPI::getFiles(const QStringList& fileIds, std::shared_ptr<QByteAr
|
|||||||
return netJob;
|
return netJob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task::Ptr FlameAPI::getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const
|
||||||
|
{
|
||||||
|
auto netJob = makeShared<NetJob>(QString("Flame::GetFile"), APPLICATION->network());
|
||||||
|
netJob->addNetAction(
|
||||||
|
Net::ApiDownload::makeByteArray(QUrl(QString("https://api.curseforge.com/v1/mods/%1/files/%2").arg(addonId, fileId)), response));
|
||||||
|
|
||||||
|
QObject::connect(netJob.get(), &NetJob::failed, [addonId, fileId] { qDebug() << "Flame API file failure" << addonId << fileId; });
|
||||||
|
|
||||||
|
return netJob;
|
||||||
|
}
|
||||||
|
|
||||||
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
// https://docs.curseforge.com/?python#tocS_ModsSearchSortField
|
||||||
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
static QList<ResourceAPI::SortingMethod> s_sorts = { { 1, "Featured", QObject::tr("Sort by Featured") },
|
||||||
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
{ 2, "Popularity", QObject::tr("Sort by Popularity") },
|
||||||
|
@ -20,6 +20,7 @@ class FlameAPI : public NetworkResourceAPI {
|
|||||||
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
Task::Ptr getProjects(QStringList addonIds, std::shared_ptr<QByteArray> response) const override;
|
||||||
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
Task::Ptr matchFingerprints(const QList<uint>& fingerprints, std::shared_ptr<QByteArray> response);
|
||||||
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
Task::Ptr getFiles(const QStringList& fileIds, std::shared_ptr<QByteArray> response) const;
|
||||||
|
Task::Ptr getFile(const QString& addonId, const QString& fileId, std::shared_ptr<QByteArray> response) const;
|
||||||
|
|
||||||
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
[[nodiscard]] auto getSortingMethods() const -> QList<ResourceAPI::SortingMethod> override;
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
#include <launch/LaunchTask.h>
|
#include <launch/LaunchTask.h>
|
||||||
#include <minecraft/MinecraftInstance.h>
|
#include <minecraft/MinecraftInstance.h>
|
||||||
#include <minecraft/auth/AccountList.h>
|
#include <minecraft/auth/AccountList.h>
|
||||||
#include <net/Download.h>
|
#include <net/ApiDownload.h>
|
||||||
#include <net/NetJob.h>
|
#include <net/NetJob.h>
|
||||||
#include <news/NewsChecker.h>
|
#include <news/NewsChecker.h>
|
||||||
#include <tools/BaseProfiler.h>
|
#include <tools/BaseProfiler.h>
|
||||||
@ -118,11 +118,15 @@
|
|||||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||||
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
#include "minecraft/mod/tasks/LocalResourceParse.h"
|
||||||
|
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
|
||||||
#include "KonamiCode.h"
|
#include "KonamiCode.h"
|
||||||
|
|
||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
|
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
#include "MMCTime.h"
|
#include "MMCTime.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -929,7 +933,7 @@ void MainWindow::finalizeInstance(InstancePtr inst)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addInstance(QString url)
|
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info)
|
||||||
{
|
{
|
||||||
QString groupName;
|
QString groupName;
|
||||||
do {
|
do {
|
||||||
@ -949,7 +953,7 @@ void MainWindow::addInstance(QString url)
|
|||||||
groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString();
|
groupName = APPLICATION->settings()->get("LastUsedGroupForNewInstance").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
NewInstanceDialog newInstDlg(groupName, url, this);
|
NewInstanceDialog newInstDlg(groupName, url, extra_info, this);
|
||||||
if (!newInstDlg.exec())
|
if (!newInstDlg.exec())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -976,18 +980,101 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||||||
if (url.scheme().isEmpty())
|
if (url.scheme().isEmpty())
|
||||||
url.setScheme("file");
|
url.setScheme("file");
|
||||||
|
|
||||||
if (!url.isLocalFile()) { // probably instance/modpack
|
QMap<QString, QString> extra_info;
|
||||||
addInstance(url.toString());
|
QUrl local_url;
|
||||||
break;
|
if (!url.isLocalFile()) { // download the remote resource and identify
|
||||||
|
QUrl dl_url;
|
||||||
|
if (url.scheme() == "curseforge") {
|
||||||
|
// need to find the download link for the modpack / resource
|
||||||
|
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
|
||||||
|
QUrlQuery query(url);
|
||||||
|
|
||||||
|
auto addonId = query.allQueryItemValues("addonId")[0];
|
||||||
|
auto fileId = query.allQueryItemValues("fileId")[0];
|
||||||
|
|
||||||
|
extra_info.insert("pack_id", addonId);
|
||||||
|
extra_info.insert("pack_version_id", fileId);
|
||||||
|
|
||||||
|
auto array = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
auto api = FlameAPI();
|
||||||
|
auto job = api.getFile(addonId, fileId, array);
|
||||||
|
|
||||||
|
QString resource_name;
|
||||||
|
|
||||||
|
connect(job.get(), &Task::failed, this,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
connect(job.get(), &Task::succeeded, this, [this, array, addonId, fileId, &dl_url, &resource_name] {
|
||||||
|
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
|
||||||
|
auto doc = Json::requireDocument(*array);
|
||||||
|
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
|
||||||
|
// No way to find out if it's a mod or a modpack before here
|
||||||
|
// And also we need to check if it ends with .zip, instead of any better way
|
||||||
|
auto fileName = Json::ensureString(data, "fileName");
|
||||||
|
|
||||||
|
// Have to use ensureString then use QUrl to get proper url encoding
|
||||||
|
dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
this, tr("Error"),
|
||||||
|
tr("The modpack, mod, or resource %1 is blocked for third-parties! Please download it manually.").arg(fileName),
|
||||||
|
QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo dl_file(dl_url.fileName());
|
||||||
|
resource_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
|
||||||
|
});
|
||||||
|
|
||||||
|
{ // drop stack
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(job.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dl_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
continue; // no valid url to download this resource
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString path = dl_url.host() + '/' + dl_url.path();
|
||||||
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
|
entry->setStale(true);
|
||||||
|
auto dl_job = unique_qobject_ptr<NetJob>(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
|
dl_job->addNetAction(Net::ApiDownload::makeCached(dl_url, entry));
|
||||||
|
auto archivePath = entry->getFullPath();
|
||||||
|
|
||||||
|
bool dl_success = false;
|
||||||
|
connect(dl_job.get(), &Task::failed, this,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
connect(dl_job.get(), &Task::succeeded, this, [&dl_success] { dl_success = true; });
|
||||||
|
|
||||||
|
{ // drop stack
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(dl_job.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dl_success) {
|
||||||
|
continue; // no local file to identify
|
||||||
|
}
|
||||||
|
local_url = QUrl::fromLocalFile(archivePath);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
local_url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto localFileName = QDir::toNativeSeparators(url.toLocalFile());
|
auto localFileName = QDir::toNativeSeparators(local_url.toLocalFile());
|
||||||
QFileInfo localFileInfo(localFileName);
|
QFileInfo localFileInfo(localFileName);
|
||||||
|
|
||||||
auto type = ResourceUtils::identify(localFileInfo);
|
auto type = ResourceUtils::identify(localFileInfo);
|
||||||
|
|
||||||
if (ResourceUtils::ValidResourceTypes.count(type) == 0) { // probably instance/modpack
|
if (ResourceUtils::ValidResourceTypes.count(type) == 0) { // probably instance/modpack
|
||||||
addInstance(localFileName);
|
addInstance(localFileName, extra_info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ class MainWindow : public QMainWindow {
|
|||||||
private:
|
private:
|
||||||
void retranslateUi();
|
void retranslateUi();
|
||||||
|
|
||||||
void addInstance(QString url = QString());
|
void addInstance(const QString& url = QString(), const QMap<QString, QString>& extra_info = {});
|
||||||
void activateInstance(InstancePtr instance);
|
void activateInstance(InstancePtr instance);
|
||||||
void setCatBackground(bool enabled);
|
void setCatBackground(bool enabled);
|
||||||
void updateInstanceToolIcon(QString new_icon);
|
void updateInstanceToolIcon(QString new_icon);
|
||||||
|
@ -62,8 +62,10 @@
|
|||||||
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
#include "ui/pages/modplatform/modrinth/ModrinthPage.h"
|
||||||
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
#include "ui/pages/modplatform/technic/TechnicPage.h"
|
||||||
#include "ui/widgets/PageContainer.h"
|
#include "ui/widgets/PageContainer.h"
|
||||||
|
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup,
|
||||||
NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString& url, QWidget* parent)
|
const QString& url,
|
||||||
|
const QMap<QString, QString>& extra_info,
|
||||||
|
QWidget* parent)
|
||||||
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
@ -125,6 +127,7 @@ NewInstanceDialog::NewInstanceDialog(const QString& initialGroup, const QString&
|
|||||||
QUrl actualUrl(url);
|
QUrl actualUrl(url);
|
||||||
m_container->selectPage("import");
|
m_container->selectPage("import");
|
||||||
importPage->setUrl(url);
|
importPage->setUrl(url);
|
||||||
|
importPage->setExtraInfo(extra_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDialogState();
|
updateDialogState();
|
||||||
|
@ -53,7 +53,10 @@ class NewInstanceDialog : public QDialog, public BasePageProvider {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NewInstanceDialog(const QString& initialGroup, const QString& url = QString(), QWidget* parent = 0);
|
explicit NewInstanceDialog(const QString& initialGroup,
|
||||||
|
const QString& url = QString(),
|
||||||
|
const QMap<QString, QString>& extra_info = {},
|
||||||
|
QWidget* parent = 0);
|
||||||
~NewInstanceDialog();
|
~NewInstanceDialog();
|
||||||
|
|
||||||
void updateDialogState();
|
void updateDialogState();
|
||||||
|
@ -35,14 +35,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ImportPage.h"
|
#include "ImportPage.h"
|
||||||
|
|
||||||
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui_ImportPage.h"
|
#include "ui_ImportPage.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include "ui/dialogs/NewInstanceDialog.h"
|
#include "ui/dialogs/NewInstanceDialog.h"
|
||||||
|
|
||||||
|
#include "modplatform/flame/FlameAPI.h"
|
||||||
|
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
|
|
||||||
class UrlValidator : public QValidator {
|
class UrlValidator : public QValidator {
|
||||||
@ -107,10 +115,61 @@ void ImportPage::updateState()
|
|||||||
bool isMRPack = fi.suffix() == "mrpack";
|
bool isMRPack = fi.suffix() == "mrpack";
|
||||||
|
|
||||||
if (fi.exists() && (isZip || isMRPack)) {
|
if (fi.exists() && (isZip || isMRPack)) {
|
||||||
QFileInfo file_info(url.fileName());
|
auto extra_info = QMap(m_extra_info);
|
||||||
dialog->setSuggestedPack(file_info.completeBaseName(), new InstanceImportTask(url, this));
|
qDebug() << "Pack Extra Info" << extra_info << m_extra_info;
|
||||||
|
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info)));
|
||||||
dialog->setSuggestedIcon("default");
|
dialog->setSuggestedIcon("default");
|
||||||
}
|
}
|
||||||
|
} else if (url.scheme() == "curseforge") {
|
||||||
|
// need to find the download link for the modpack
|
||||||
|
// format of url curseforge://install?addonId=IDHERE&fileId=IDHERE
|
||||||
|
QUrlQuery query(url);
|
||||||
|
auto addonId = query.allQueryItemValues("addonId")[0];
|
||||||
|
auto fileId = query.allQueryItemValues("fileId")[0];
|
||||||
|
auto array = std::make_shared<QByteArray>();
|
||||||
|
|
||||||
|
auto api = FlameAPI();
|
||||||
|
auto job = api.getFile(addonId, fileId, array);
|
||||||
|
|
||||||
|
connect(job.get(), &NetJob::failed, this,
|
||||||
|
[this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||||
|
connect(job.get(), &NetJob::succeeded, this, [this, array, addonId, fileId] {
|
||||||
|
qDebug() << "Returned CFURL Json:\n" << array->toStdString().c_str();
|
||||||
|
auto doc = Json::requireDocument(*array);
|
||||||
|
auto data = Json::ensureObject(Json::ensureObject(doc.object()), "data");
|
||||||
|
// No way to find out if it's a mod or a modpack before here
|
||||||
|
// And also we need to check if it ends with .zip, instead of any better way
|
||||||
|
auto fileName = Json::ensureString(data, "fileName");
|
||||||
|
if (fileName.endsWith(".zip")) {
|
||||||
|
// Have to use ensureString then use QUrl to get proper url encoding
|
||||||
|
auto dl_url = QUrl(Json::ensureString(data, "downloadUrl", "", "downloadUrl"));
|
||||||
|
if (!dl_url.isValid()) {
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
this, tr("Error"),
|
||||||
|
tr("The modpack %1 is blocked for third-parties! Please download it manually.").arg(fileName),
|
||||||
|
QMessageBox::Critical)
|
||||||
|
->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo dl_file(dl_url.fileName());
|
||||||
|
QString pack_name = Json::ensureString(data, "displayName", dl_file.completeBaseName(), "displayName");
|
||||||
|
|
||||||
|
QMap<QString, QString> extra_info;
|
||||||
|
extra_info.insert("pack_id", addonId);
|
||||||
|
extra_info.insert("pack_version_id", fileId);
|
||||||
|
|
||||||
|
dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, this, std::move(extra_info)));
|
||||||
|
dialog->setSuggestedIcon("default");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
CustomMessageBox::selectable(this, tr("Error"), tr("This url isn't a valid modpack !"), QMessageBox::Critical)->show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ProgressDialog dlUrlDialod(this);
|
||||||
|
dlUrlDialod.setSkipButton(true, tr("Abort"));
|
||||||
|
dlUrlDialod.execWithTask(job.get());
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (input.endsWith("?client=y")) {
|
if (input.endsWith("?client=y")) {
|
||||||
input.chop(9);
|
input.chop(9);
|
||||||
@ -119,7 +178,8 @@ void ImportPage::updateState()
|
|||||||
}
|
}
|
||||||
// hook, line and sinker.
|
// hook, line and sinker.
|
||||||
QFileInfo fi(url.fileName());
|
QFileInfo fi(url.fileName());
|
||||||
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this));
|
auto extra_info = QMap(m_extra_info);
|
||||||
|
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info)));
|
||||||
dialog->setSuggestedIcon("default");
|
dialog->setSuggestedIcon("default");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -133,6 +193,12 @@ void ImportPage::setUrl(const QString& url)
|
|||||||
updateState();
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImportPage::setExtraInfo(const QMap<QString, QString>& extra_info)
|
||||||
|
{
|
||||||
|
m_extra_info = extra_info;
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
void ImportPage::on_modpackBtn_clicked()
|
void ImportPage::on_modpackBtn_clicked()
|
||||||
{
|
{
|
||||||
auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString();
|
auto filter = QMimeDatabase().mimeTypeForName("application/zip").filterString();
|
||||||
|
@ -62,7 +62,7 @@ class ImportPage : public QWidget, public BasePage {
|
|||||||
|
|
||||||
void setUrl(const QString& url);
|
void setUrl(const QString& url);
|
||||||
void openedImpl() override;
|
void openedImpl() override;
|
||||||
|
void setExtraInfo(const QMap<QString, QString>& extra_info);
|
||||||
private slots:
|
private slots:
|
||||||
void on_modpackBtn_clicked();
|
void on_modpackBtn_clicked();
|
||||||
void updateState();
|
void updateState();
|
||||||
@ -73,4 +73,5 @@ class ImportPage : public QWidget, public BasePage {
|
|||||||
private:
|
private:
|
||||||
Ui::ImportPage* ui = nullptr;
|
Ui::ImportPage* ui = nullptr;
|
||||||
NewInstanceDialog* dialog = nullptr;
|
NewInstanceDialog* dialog = nullptr;
|
||||||
|
QMap<QString, QString> m_extra_info = {};
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>- CurseForge modpacks (ZIP)</string>
|
<string>- CurseForge modpacks (ZIP / curseforge:// URL)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignCenter</set>
|
<set>Qt::AlignCenter</set>
|
||||||
|
@ -10,4 +10,4 @@ Icon=org.prismlauncher.PrismLauncher
|
|||||||
Categories=Game;ActionGame;AdventureGame;Simulation;
|
Categories=Game;ActionGame;AdventureGame;Simulation;
|
||||||
Keywords=game;minecraft;mc;
|
Keywords=game;minecraft;mc;
|
||||||
StartupWMClass=PrismLauncher
|
StartupWMClass=PrismLauncher
|
||||||
MimeType=application/zip;application/x-modrinth-modpack+zip
|
MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge;
|
||||||
|
@ -363,6 +363,10 @@ Section "@Launcher_DisplayName@"
|
|||||||
; Write the installation path into the registry
|
; Write the installation path into the registry
|
||||||
WriteRegStr HKCU Software\@Launcher_CommonName@ "InstallDir" "$INSTDIR"
|
WriteRegStr HKCU Software\@Launcher_CommonName@ "InstallDir" "$INSTDIR"
|
||||||
|
|
||||||
|
; Write the URL Handler into registry for curseforge
|
||||||
|
WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" ""
|
||||||
|
WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||||
|
|
||||||
; Write the uninstall keys for Windows
|
; Write the uninstall keys for Windows
|
||||||
${GetParameters} $R0
|
${GetParameters} $R0
|
||||||
${GetOptions} $R0 "/NoUninstaller" $R1
|
${GetOptions} $R0 "/NoUninstaller" $R1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user