Merge branch 'develop' of https://github.com/MultiMC/MultiMC5 into develop

This commit is contained in:
Sebastian-byte
2021-01-29 19:34:45 -05:00
24 changed files with 399 additions and 60 deletions

View File

@@ -124,8 +124,10 @@ SET(MULTIMC_SOURCES
# GUI - platform pages
pages/modplatform/VanillaPage.cpp
pages/modplatform/VanillaPage.h
pages/modplatform/ftb/FtbModel.cpp
pages/modplatform/ftb/FtbModel.h
pages/modplatform/ftb/FtbFilterModel.cpp
pages/modplatform/ftb/FtbFilterModel.h
pages/modplatform/ftb/FtbListModel.cpp
pages/modplatform/ftb/FtbListModel.h
pages/modplatform/ftb/FtbPage.cpp
pages/modplatform/ftb/FtbPage.h
pages/modplatform/legacy_ftb/Page.cpp

View File

@@ -0,0 +1,45 @@
Name: MultiMC5
Version: 1.4
Release: 1%{?dist}
Summary: A local install wrapper for MultiMC
License: ASL 2.0
URL: https://multimc.org
BuildArch: x86_64
Requires: zenity qt5-qtbase wget
Provides: multimc MultiMC multimc5
%description
A local install wrapper for MultiMC
%prep
%build
%install
mkdir -p %{buildroot}/opt/multimc
install -m 0644 ../ubuntu/multimc/opt/multimc/icon.svg %{buildroot}/opt/multimc/icon.svg
install -m 0755 ../ubuntu/multimc/opt/multimc/run.sh %{buildroot}/opt/multimc/run.sh
mkdir -p %{buildroot}/%{_datadir}/applications
install -m 0644 ../ubuntu/multimc/usr/share/applications/multimc.desktop %{buildroot}/%{_datadir}/applications/multimc.desktop
mkdir -p %{buildroot}/%{_metainfodir}
install -m 0644 ../ubuntu/multimc/usr/share/metainfo/multimc.metainfo.xml %{buildroot}/%{_metainfodir}/multimc.metainfo.xml
%files
%dir /opt/multimc
/opt/multimc/icon.svg
/opt/multimc/run.sh
%{_datadir}/applications/multimc.desktop
%{_metainfodir}/multimc.metainfo.xml
%changelog
* Tue Dec 08 00:34:35 CET 2020 joshua-stone <joshua.gage.stone@gmail.com>
- Add metainfo.xml for improving package metadata
* Wed Nov 25 22:53:59 CET 2020 kb1000 <fedora@kb1000.de>
- Initial version of the RPM package, based on the Ubuntu package

View File

@@ -0,0 +1,12 @@
# What is this?
A simple RPM package for MultiMC that contains a script that downloads and installs real MultiMC on Red Hat based systems.
It contains a `.desktop` file, a `.metainfo.xml` file, an icon, and a simple script that does the heavy lifting.
# How to build this?
You need the `rpm-build` package. Switch into this directory, then run:
```
rpmbuild --build-in-place -bb MultiMC5.spec
```
Replace the version with whatever is appropriate.

View File

@@ -1,8 +1,10 @@
# What is this?
A simple ubuntu package for MultiMC that wraps the contains a script that downloads and installs real MultiMC on ubuntu based systems.
A simple Ubuntu package for MultiMC that contains a script that downloads and installs real MultiMC on Ubuntu based systems.
It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting.
This is also the source for the files in the [RPM package](../rpm). If you rename, create or delete files here, you'll likely also have to update the RPM spec file there.
# How to build this?
You need dpkg utils. Rename the `multimc` folder to `multimc_1.3-1` and then run:
```

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>multimc</id>
<launchable type="desktop-id">multimc.desktop</launchable>
<name>MultiMC</name>
<summary>Manage Minecraft instances with ease</summary>
<description>
<p>Overview</p>
<p>MultiMC is a free, open source launcher for Minecraft. It allows you to have multiple, cleanly separated instances of Minecraft (each with their own mods, texture packs, saves, etc) and helps you manage them and their associated options with a simple and powerful interface.</p>
<p>Features</p>
<ul>
<li>Manage multiple instances of Minecraft at once</li>
<li>Start Minecraft with a custom resolution</li>
<li>Change Java's runtime options (including memory options)</li>
<li>Shows Minecraft's console output in a colour coded window</li>
<li>Kill Minecraft easily if it crashes / freezes</li>
<li>Custom icons and groups for instances</li>
<li>Forge integration (automatic installation, version downloads, mod management)</li>
<li>Minecraft world management</li>
<li>Import and export Minecraft instances to share them with anyone</li>
<li>Supports every version of Minecraft that the vanilla launcher does</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<image type="source" width="936" height="921">https://multimc.org/images/screenshots/main.png</image>
</screenshot>
<screenshot>
<image type="source" width="936" height="998">https://multimc.org/images/screenshots/editmods.png</image>
</screenshot>
<screenshot>
<image type="source" width="936" height="998">https://multimc.org/images/screenshots/version.png</image>
</screenshot>
<screenshot>
<image type="source" width="936" height="998">https://multimc.org/images/screenshots/console.png</image>
</screenshot>
<screenshot>
<image type="source" width="936" height="921">https://multimc.org/images/screenshots/settings.png</image>
</screenshot>
</screenshots>
<releases>
<release date="2021-01-07" version="5"/>
</releases>
<url type="homepage">https://multimc.org/</url>
<url type="help">https://discord.com/invite/0k2zsXGNHs0fE4Wm</url>
<url type="faq">https://github.com/MultiMC/MultiMC5/wiki/FAQ</url>
<url type="bugtracker">https://github.com/MultiMC/MultiMC5/issues</url>
<url type="translate">https://translate.multimc.org/</url>
<url type="donation">https://www.patreon.com/multimc</url>
<developer_name>The MultiMC Team</developer_name>
<metadata_license>CC0-1.0</metadata_license>
<project_license>Apache-2.0</project_license>
<update_contact>peterix_at_gmail.com</update_contact>
</component>

View File

@@ -170,6 +170,24 @@ void WorldListPage::on_actionView_Folder_triggered()
DesktopServices::openDirectory(m_worlds->dir().absolutePath(), true);
}
void WorldListPage::on_actionDatapacks_triggered()
{
QModelIndex index = getSelectedWorld();
if (!index.isValid())
{
return;
}
if(!worldSafetyNagQuestion())
return;
auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString();
DesktopServices::openDirectory(FS::PathCombine(fullPath, "datapacks"), true);
}
void WorldListPage::on_actionReset_Icon_triggered()
{
auto proxiedIndex = getSelectedWorld();

View File

@@ -90,6 +90,7 @@ private slots:
void on_actionRename_triggered();
void on_actionRefresh_triggered();
void on_actionView_Folder_triggered();
void on_actionDatapacks_triggered();
void on_actionReset_Icon_triggered();
void worldChanged(const QModelIndex &current, const QModelIndex &previous);
void mceditState(LoggedProcess::State state);

View File

@@ -85,6 +85,7 @@
<addaction name="actionCopy"/>
<addaction name="actionRemove"/>
<addaction name="actionMCEdit"/>
<addaction name="actionDatapacks"/>
<addaction name="actionReset_Icon"/>
<addaction name="separator"/>
<addaction name="actionCopy_Seed"/>
@@ -139,6 +140,14 @@
<string>Remove world icon to make the game re-generate it on next load.</string>
</property>
</action>
<action name="actionDatapacks">
<property name="text">
<string>Datapacks</string>
</property>
<property name="toolTip">
<string>Manage datapacks inside the world.</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -0,0 +1,64 @@
#include "FtbFilterModel.h"
#include <QDebug>
#include "modplatform/modpacksch/FTBPackManifest.h"
#include <MMCStrings.h>
namespace Ftb {
FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
{
currentSorting = Sorting::ByPlays;
sortings.insert(tr("Sort by plays"), Sorting::ByPlays);
sortings.insert(tr("Sort by installs"), Sorting::ByInstalls);
sortings.insert(tr("Sort by name"), Sorting::ByName);
}
const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings()
{
return sortings;
}
QString FilterModel::translateCurrentSorting()
{
return sortings.key(currentSorting);
}
void FilterModel::setSorting(Sorting sorting)
{
currentSorting = sorting;
invalidate();
}
FilterModel::Sorting FilterModel::getCurrentSorting()
{
return currentSorting;
}
bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
return true;
}
bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<ModpacksCH::Modpack>();
ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<ModpacksCH::Modpack>();
if (currentSorting == ByPlays) {
return leftPack.plays < rightPack.plays;
}
else if (currentSorting == ByInstalls) {
return leftPack.installs < rightPack.installs;
}
else if (currentSorting == ByName) {
return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
}
// Invalid sorting set, somehow...
qWarning() << "Invalid sorting set!";
return true;
}
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <QtCore/QSortFilterProxyModel>
namespace Ftb {
class FilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilterModel(QObject* parent = Q_NULLPTR);
enum Sorting {
ByPlays,
ByInstalls,
ByName,
};
const QMap<QString, Sorting> getAvailableSortings();
QString translateCurrentSorting();
void setSorting(Sorting sorting);
Sorting getCurrentSorting();
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
QMap<QString, Sorting> sortings;
Sorting currentSorting;
};
}

View File

@@ -1,4 +1,4 @@
#include "FtbModel.h"
#include "FtbListModel.h"
#include "BuildConfig.h"
#include "Env.h"
@@ -79,7 +79,7 @@ void ListModel::performSearch()
auto *netJob = new NetJob("Ftb::Search");
QString searchUrl;
if(currentSearchTerm.isEmpty()) {
searchUrl = BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/popular/plays/100";
searchUrl = BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all";
}
else {
searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/search/25?term=%1")
@@ -206,9 +206,18 @@ void ListModel::packRequestFinished()
return;
}
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
modpacks.append(pack);
endInsertRows();
// Since there is no guarantee that packs have a version, this will just
// ignore those "dud" packs.
if (pack.versions.empty())
{
qWarning() << "FTB Pack " << pack.id << " ignored. reason: lacking any versions";
}
else
{
beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
modpacks.append(pack);
endInsertRows();
}
if(!remainingPacks.isEmpty()) {
currentPack = remainingPacks.at(0);

View File

@@ -10,12 +10,26 @@ FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
{
ui->setupUi(this);
connect(ui->searchButton, &QPushButton::clicked, this, &FtbPage::triggerSearch);
ui->searchEdit->installEventFilter(this);
model = new Ftb::ListModel(this);
ui->packView->setModel(model);
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
filterModel = new Ftb::FilterModel(this);
listModel = new Ftb::ListModel(this);
filterModel->setSourceModel(listModel);
ui->packView->setModel(filterModel);
ui->packView->setSortingEnabled(true);
ui->packView->header()->hide();
ui->packView->setIndentation(0);
ui->searchEdit->installEventFilter(this);
for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
{
ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
}
ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
connect(ui->searchButton, &QPushButton::clicked, this, &FtbPage::triggerSearch);
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
}
@@ -59,7 +73,7 @@ void FtbPage::suggestCurrent()
QString editedLogoName;
editedLogoName = selected.name;
model->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
});
@@ -70,7 +84,13 @@ void FtbPage::suggestCurrent()
void FtbPage::triggerSearch()
{
model->searchWithTerm(ui->searchEdit->text());
listModel->searchWithTerm(ui->searchEdit->text());
}
void FtbPage::onSortingSelectionChanged(QString data)
{
auto toSet = filterModel->getAvailableSortings().value(data);
filterModel->setSorting(toSet);
}
void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
@@ -86,7 +106,7 @@ void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
return;
}
selected = model->data(first, Qt::UserRole).value<ModpacksCH::Modpack>();
selected = filterModel->data(first, Qt::UserRole).value<ModpacksCH::Modpack>();
// reverse foreach, so that the newest versions are first
for (auto i = selected.versions.size(); i--;) {

View File

@@ -15,7 +15,8 @@
#pragma once
#include "FtbModel.h"
#include "FtbFilterModel.h"
#include "FtbListModel.h"
#include <QWidget>
@@ -64,13 +65,15 @@ private:
private slots:
void triggerSearch();
void onSortingSelectionChanged(QString data);
void onSelectionChanged(QModelIndex first, QModelIndex second);
void onVersionSelectionChanged(QString data);
private:
Ui::FtbPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
Ftb::ListModel* model = nullptr;
Ftb::ListModel* listModel = nullptr;
Ftb::FilterModel* filterModel = nullptr;
ModpacksCH::Modpack selected;
QString selectedVersion;

View File

@@ -11,22 +11,6 @@
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QListView" name="packView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="searchButton">
<property name="text">
@@ -38,19 +22,38 @@
<widget class="QLineEdit" name="searchEdit"/>
</item>
<item row="2" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0" rowminimumheight="0" columnminimumwidth="0,0">
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0">
<item row="0" column="2">
<widget class="QComboBox" name="versionSelectionBox"/>
</item>
<item row="0" column="0">
<item row="0" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>Version selected:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="sortByBox"/>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTreeView" name="packView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>48</width>
<height>48</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>

View File

@@ -33,6 +33,9 @@ struct Modpack {
QString minecraftVersion;
bool metadataLoaded = false;
QString websiteUrl;
QString author;
QString description;
};
}

View File

@@ -158,6 +158,9 @@ void TechnicPage::suggestCurrent()
}
current.minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__");
current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__");
current.author = Json::ensureString(obj, "user", QString(), "__placeholder__");
current.description = Json::ensureString(obj, "description", QString(), "__placeholder__");
current.metadataLoaded = true;
metadataLoaded();
});
@@ -168,29 +171,22 @@ void TechnicPage::suggestCurrent()
// expects current.metadataLoaded to be true
void TechnicPage::metadataLoaded()
{
/*QString text = "";
QString text = "";
QString name = current.name;
if (current.websiteUrl.isEmpty())
// This allows injecting HTML here.
text = name;
else
// URL not properly escaped for inclusion in HTML. The name allows for injecting HTML.
text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>";
if (!current.authors.empty()) {
auto authorToStr = [](Technic::ModpackAuthor & author) {
if(author.url.isEmpty()) {
return author.name;
}
return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name);
};
QStringList authorStrs;
for(auto & author: current.authors) {
authorStrs.push_back(authorToStr(author));
}
text += tr(" by ") + authorStrs.join(", ");
if (!current.author.isEmpty()) {
// This allows injecting HTML here
text += tr(" by ") + current.author;
}
ui->frame->setModText(text);
ui->frame->setModDescription(current.description);*/
ui->frame->setModDescription(current.description);
if (!current.isSolder)
{
dialog->setSuggestedPack(current.name, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion));

View File

@@ -55,8 +55,37 @@
</property>
</widget>
</item>
<item>
<widget class="MCModInfoFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>MCModInfoFrame</class>
<extends>QFrame</extends>
<header>widgets/MCModInfoFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>searchButton</tabstop>
<tabstop>packView</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -135,6 +135,7 @@ void MCModInfoFrame::setModDescription(QString text)
ui->label_ModDescription->setOpenExternalLinks(false);
ui->label_ModDescription->setTextFormat(Qt::TextFormat::RichText);
desc = text;
// This allows injecting HTML here.
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
QObject::connect(ui->label_ModDescription, &QLabel::linkActivated, this, &MCModInfoFrame::modDescEllipsisHandler);
}

View File

@@ -65,7 +65,7 @@ ServerStatus::ServerStatus(QWidget *parent, Qt::WindowFlags f) : QWidget(parent,
addStatus("authserver.mojang.com", tr("Auth"));
addLine();
addStatus("sessionserver.mojang.com", tr("Session"));
addStatus("session.minecraft.net", tr("Session"));
addLine();
addStatus("textures.minecraft.net", tr("Skins"));
addLine();