mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 01:54:20 +03:00
Merge 92315147d6 into d2fa7cf7f7
This commit is contained in:
commit
3f08b621bb
14 changed files with 235 additions and 4 deletions
|
|
@ -85,6 +85,14 @@
|
|||
<string>curseforge</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Modrinth</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>modrinth</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>${Launcher_Name}</string>
|
||||
|
|
|
|||
|
|
@ -561,6 +561,8 @@ set(MODRINTH_SOURCES
|
|||
modplatform/modrinth/ModrinthInstanceCreationTask.h
|
||||
modplatform/modrinth/ModrinthPackExportTask.cpp
|
||||
modplatform/modrinth/ModrinthPackExportTask.h
|
||||
modplatform/modrinth/ModrinthUrl.cpp
|
||||
modplatform/modrinth/ModrinthUrl.h
|
||||
)
|
||||
|
||||
set(PACKWIZ_SOURCES
|
||||
|
|
|
|||
45
launcher/modplatform/modrinth/ModrinthUrl.cpp
Normal file
45
launcher/modplatform/modrinth/ModrinthUrl.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "ModrinthUrl.h"
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
auto parseModpackLink(const QUrl& url) -> std::optional<ParsedModpackLink>
|
||||
{
|
||||
if (url.scheme().compare("modrinth", Qt::CaseInsensitive) != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (url.host().compare("modpack", Qt::CaseInsensitive) != 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto segments = QUrl::fromPercentEncoding(url.path().toUtf8()).split('/', Qt::SkipEmptyParts);
|
||||
if (segments.size() != 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto slug = segments.constFirst().trimmed();
|
||||
if (slug.isEmpty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ParsedModpackLink{ slug };
|
||||
}
|
||||
|
||||
} // namespace Modrinth
|
||||
32
launcher/modplatform/modrinth/ModrinthUrl.h
Normal file
32
launcher/modplatform/modrinth/ModrinthUrl.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
*
|
||||
* 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 <QUrl>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace Modrinth {
|
||||
|
||||
struct ParsedModpackLink {
|
||||
QString slug;
|
||||
};
|
||||
|
||||
auto parseModpackLink(const QUrl& url) -> std::optional<ParsedModpackLink>;
|
||||
|
||||
} // namespace Modrinth
|
||||
|
|
@ -125,6 +125,7 @@
|
|||
#include "modplatform/ModIndex.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/flame/FlameModIndex.h"
|
||||
#include "modplatform/modrinth/ModrinthUrl.h"
|
||||
|
||||
#include "KonamiCode.h"
|
||||
|
||||
|
|
@ -896,7 +897,7 @@ void MainWindow::on_actionCopyInstance_triggered()
|
|||
runModalTask(task.get());
|
||||
}
|
||||
|
||||
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info)
|
||||
void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& extra_info, const QString& modrinthSlug)
|
||||
{
|
||||
QString groupName;
|
||||
do {
|
||||
|
|
@ -917,6 +918,9 @@ void MainWindow::addInstance(const QString& url, const QMap<QString, QString>& e
|
|||
}
|
||||
|
||||
NewInstanceDialog newInstDlg(groupName, url, extra_info, this);
|
||||
if (!modrinthSlug.isEmpty()) {
|
||||
newInstDlg.openModrinthModpack(modrinthSlug);
|
||||
}
|
||||
if (!newInstDlg.exec())
|
||||
return;
|
||||
|
||||
|
|
@ -950,6 +954,19 @@ void MainWindow::processURLs(QList<QUrl> urls)
|
|||
QMap<QString, QString> extra_info;
|
||||
QUrl local_url;
|
||||
if (!url.isLocalFile()) { // download the remote resource and identify
|
||||
if (url.scheme().compare("modrinth", Qt::CaseInsensitive) == 0) {
|
||||
if (auto modpackLink = Modrinth::parseModpackLink(url)) {
|
||||
addInstance(QString(), {}, modpackLink->slug);
|
||||
} else {
|
||||
CustomMessageBox::selectable(
|
||||
this, tr("Error"),
|
||||
tr("Unsupported Modrinth link.\n\nPrism Launcher currently only supports modpack links such as "
|
||||
"modrinth://modpack/fabulously-optimized."),
|
||||
QMessageBox::Critical)
|
||||
->show();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool isExternalURLImport = (url.host().toLower() == "import") || (url.path().startsWith("/import", Qt::CaseInsensitive));
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ class MainWindow : public QMainWindow {
|
|||
private:
|
||||
void retranslateUi();
|
||||
|
||||
void addInstance(const QString& url = QString(), const QMap<QString, QString>& extra_info = {});
|
||||
void addInstance(const QString& url = QString(), const QMap<QString, QString>& extra_info = {},
|
||||
const QString& modrinthSlug = QString());
|
||||
void activateInstance(BaseInstance* instance);
|
||||
void setCatBackground(bool enabled);
|
||||
void updateInstanceToolIcon(QString new_icon);
|
||||
|
|
|
|||
|
|
@ -250,6 +250,23 @@ void NewInstanceDialog::setSuggestedIcon(const QString& key)
|
|||
ui->iconButton->setIcon(icon);
|
||||
}
|
||||
|
||||
void NewInstanceDialog::openModrinthModpack(const QString& slug)
|
||||
{
|
||||
auto trimmedSlug = slug.trimmed();
|
||||
if (trimmedSlug.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* page = dynamic_cast<ModrinthPage*>(m_container->getPage("modrinth"));
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchTerm = trimmedSlug;
|
||||
page->openProjectBySlug(trimmedSlug);
|
||||
m_container->selectPage(page->id());
|
||||
}
|
||||
|
||||
InstanceTask* NewInstanceDialog::extractTask()
|
||||
{
|
||||
InstanceTask* extracted = creationTask.release();
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ class NewInstanceDialog : public QDialog, public BasePageProvider {
|
|||
void setSuggestedPack(const QString& name, QString version, InstanceTask* task = nullptr);
|
||||
void setSuggestedIconFromFile(const QString& path, const QString& name);
|
||||
void setSuggestedIcon(const QString& key);
|
||||
void openModrinthModpack(const QString& slug);
|
||||
|
||||
InstanceTask* extractTask();
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include <QComboBox>
|
||||
#include <QKeyEvent>
|
||||
#include <QPushButton>
|
||||
#include <utility>
|
||||
|
||||
ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent), m_ui(new Ui::ModrinthPage), m_dialog(dialog), m_fetch_progress(this, false)
|
||||
|
|
@ -328,6 +329,44 @@ void ModrinthPage::suggestCurrent()
|
|||
}
|
||||
}
|
||||
|
||||
void ModrinthPage::openProjectBySlug(QString slug)
|
||||
{
|
||||
m_requestedProjectSlug = std::move(slug).trimmed();
|
||||
|
||||
if (m_requestedProjectSlug.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSearchTerm(m_requestedProjectSlug);
|
||||
}
|
||||
|
||||
void ModrinthPage::selectRequestedProject()
|
||||
{
|
||||
if (m_requestedProjectSlug.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requestedSlug = m_requestedProjectSlug;
|
||||
m_requestedProjectSlug.clear();
|
||||
|
||||
for (int row = 0; row < m_model->rowCount({}); row++) {
|
||||
const auto index = m_model->index(row, 0);
|
||||
const auto pack = m_model->data(index, Qt::UserRole).value<ModPlatform::IndexedPack::Ptr>();
|
||||
|
||||
if (pack && pack->slug.compare(requestedSlug, Qt::CaseInsensitive) == 0) {
|
||||
m_ui->packView->setCurrentIndex(index);
|
||||
m_ui->packView->scrollTo(index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_model->rowCount({}) == 1) {
|
||||
const auto index = m_model->index(0, 0);
|
||||
m_ui->packView->setCurrentIndex(index);
|
||||
m_ui->packView->scrollTo(index);
|
||||
}
|
||||
}
|
||||
|
||||
void ModrinthPage::triggerSearch()
|
||||
{
|
||||
m_ui->packView->selectionModel()->setCurrentIndex({}, QItemSelectionModel::SelectionFlag::ClearAndSelect);
|
||||
|
|
@ -335,8 +374,17 @@ void ModrinthPage::triggerSearch()
|
|||
m_ui->packDescription->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
bool filterChanged = m_filterWidget->changed();
|
||||
m_model->searchWithTerm(m_ui->searchEdit->text(), m_ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
const auto searchTerm = m_requestedProjectSlug.isEmpty() ? m_ui->searchEdit->text() : "#" + m_requestedProjectSlug;
|
||||
m_model->searchWithTerm(searchTerm, m_ui->sortByBox->currentIndex(), m_filterWidget->getFilter(), filterChanged);
|
||||
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
||||
|
||||
if (!m_requestedProjectSlug.isEmpty()) {
|
||||
if (m_model->hasActiveSearchJob()) {
|
||||
connect(m_model->activeSearchJob().get(), &Task::finished, this, [this] { selectRequestedProject(); });
|
||||
} else {
|
||||
selectRequestedProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModrinthPage::onVersionSelectionChanged(int index)
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
|||
|
||||
ModPlatform::IndexedPack::Ptr getCurrent() { return m_current; }
|
||||
void suggestCurrent();
|
||||
void openProjectBySlug(QString slug);
|
||||
|
||||
void updateUI();
|
||||
|
||||
|
|
@ -91,12 +92,15 @@ class ModrinthPage : public QWidget, public ModpackProviderBasePage {
|
|||
void createFilterWidget();
|
||||
|
||||
private:
|
||||
void selectRequestedProject();
|
||||
|
||||
Ui::ModrinthPage* m_ui;
|
||||
NewInstanceDialog* m_dialog;
|
||||
Modrinth::ModpackListModel* m_model;
|
||||
|
||||
ModPlatform::IndexedPack::Ptr m_current;
|
||||
QString m_selectedVersion;
|
||||
QString m_requestedProjectSlug;
|
||||
|
||||
ProgressWidget m_fetch_progress;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ Icon=@Launcher_AppID@
|
|||
Categories=Game;ActionGame;AdventureGame;Simulation;PackageManager;
|
||||
Keywords=game;minecraft;mc;
|
||||
StartupWMClass=@Launcher_CommonName@
|
||||
MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge;x-scheme-handler/prismlauncher;x-scheme-handler/@Launcher_APP_BINARY_NAME@;
|
||||
MimeType=application/zip;application/x-modrinth-modpack+zip;x-scheme-handler/curseforge;x-scheme-handler/modrinth;x-scheme-handler/prismlauncher;x-scheme-handler/@Launcher_APP_BINARY_NAME@;
|
||||
|
|
|
|||
|
|
@ -394,6 +394,10 @@ Section "@Launcher_DisplayName@"
|
|||
WriteRegStr HKCU Software\Classes\curseforge "URL Protocol" ""
|
||||
WriteRegStr HKCU Software\Classes\curseforge\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||
|
||||
; Write the URL Handler into registry for modrinth
|
||||
WriteRegStr HKCU Software\Classes\modrinth "URL Protocol" ""
|
||||
WriteRegStr HKCU Software\Classes\modrinth\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||
|
||||
; Write the URL Handler into registry for prismlauncher
|
||||
WriteRegStr HKCU Software\Classes\@Launcher_APP_BINARY_NAME@ "URL Protocol" ""
|
||||
WriteRegStr HKCU Software\Classes\@Launcher_APP_BINARY_NAME@\shell\open\command "" '"$INSTDIR\@Launcher_APP_BINARY_NAME@.exe" "%1"'
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ ecm_add_test(Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}:
|
|||
ecm_add_test(Version_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME Version)
|
||||
|
||||
ecm_add_test(ModrinthUrl_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME ModrinthUrl)
|
||||
|
||||
ecm_add_test(MetaComponentParse_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
||||
TEST_NAME MetaComponentParse)
|
||||
|
||||
|
|
|
|||
49
tests/ModrinthUrl_test.cpp
Normal file
49
tests/ModrinthUrl_test.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <QTest>
|
||||
|
||||
#include <modplatform/modrinth/ModrinthUrl.h>
|
||||
|
||||
class ModrinthUrlTest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void parseModpackLink_data()
|
||||
{
|
||||
QTest::addColumn<QString>("link");
|
||||
QTest::addColumn<QString>("slug");
|
||||
|
||||
QTest::newRow("official") << "modrinth://modpack/fabulously-optimized" << "fabulously-optimized";
|
||||
QTest::newRow("trailing slash") << "modrinth://modpack/fabulously-optimized/" << "fabulously-optimized";
|
||||
QTest::newRow("case insensitive host") << "modrinth://MODPACK/fo" << "fo";
|
||||
}
|
||||
|
||||
void parseModpackLink()
|
||||
{
|
||||
QFETCH(QString, link);
|
||||
QFETCH(QString, slug);
|
||||
|
||||
auto parsed = Modrinth::parseModpackLink(QUrl(link));
|
||||
QVERIFY(parsed.has_value());
|
||||
QCOMPARE(parsed->slug, slug);
|
||||
}
|
||||
|
||||
void rejectInvalidLinks_data()
|
||||
{
|
||||
QTest::addColumn<QString>("link");
|
||||
|
||||
QTest::newRow("wrong scheme") << "https://modrinth.com/modpack/fabulously-optimized";
|
||||
QTest::newRow("missing slug") << "modrinth://modpack/";
|
||||
QTest::newRow("unsupported resource") << "modrinth://mod/sodium";
|
||||
QTest::newRow("extra path") << "modrinth://modpack/fabulously-optimized/version/latest";
|
||||
}
|
||||
|
||||
void rejectInvalidLinks()
|
||||
{
|
||||
QFETCH(QString, link);
|
||||
|
||||
QVERIFY(!Modrinth::parseModpackLink(QUrl(link)).has_value());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(ModrinthUrlTest)
|
||||
|
||||
#include "ModrinthUrl_test.moc"
|
||||
Loading…
Add table
Add a link
Reference in a new issue