builds fixes

This commit is contained in:
2026-08-27 16:44:42 +03:00
parent 168884127b
commit 08e0dc4965
10 changed files with 788 additions and 360 deletions
+89 -61
View File
@@ -371,14 +371,71 @@ void LauncherBackend::removeProfile(int index)
emit profilesChanged();
}
QVariantMap LauncherBackend::customBuildRemovalInfo(int index) const
{
if (index < 0 || index >= m_builds.size())
return {};
const CustomBuild &build = m_builds[index];
const bool active = m_settings.value(QStringLiteral("activeBuildId")).toInt() == build.id
|| m_builds.size() == 1;
return {{QStringLiteral("name"), build.name},
{QStringLiteral("hasArchive"), m_switcher->hasArchive(build.id)},
{QStringLiteral("active"), active},
{QStringLiteral("lastOne"), m_builds.size() == 1}};
}
void LauncherBackend::removeCustomBuild(int index)
{
if (index < 0 || index >= m_builds.size())
return;
m_builds.removeAt(index);
if (m_switcher->isRunning() || m_busy || m_installer->isRunning()
|| m_loaderInstaller->isRunning()) {
emit launchError(tr("Дождитесь окончания текущей операции"));
return;
}
if (m_launcher->isRunning()) {
emit launchError(tr("Сначала закройте игру"));
return;
}
const CustomBuild removed = m_builds.takeAt(index);
const bool wasActive = m_settings.value(QStringLiteral("activeBuildId")).toInt() == removed.id
|| m_builds.isEmpty();
saveCustomBuilds();
// Раньше удалялась только запись, а архив сборки оставался лежать в
// galeonLauncher/builds вместе со ссылкой на него в index.json.
if (!m_switcher->forgetBuild(removed.id)) {
emit launchError(tr("Сборка «%1» удалена, но её архив в %2 стереть не удалось")
.arg(removed.name,
QDir::toNativeSeparators(LauncherPaths::buildStorageDir())));
}
emit customBuildsChanged();
emit activeBuildChanged();
if (!wasActive)
return;
// Удалили активную сборку: в .minecraft лежит её содержимое, а активной
// становится другая. Оставить как есть нельзя — при следующем переключении
// чужое содержимое уехало бы в архив новой сборки поверх её собственного.
m_settings.remove(QStringLiteral("activeBuildId"));
saveSettings();
if (m_builds.isEmpty()) {
emit activeBuildChanged();
return;
}
const CustomBuild &next = m_builds.first();
emit launchProgress(tr("Активна сборка «%1»…").arg(next.name));
// from = -1: архивировать нечего, содержимое удалённой сборки уходит вместе
// с ней. Дальше обычная очистка и распаковка новой активной сборки.
m_switcher->switchTo(-1, QString(), next.id, next.name, gameDir());
emit switchChanged();
emit busyChanged();
}
int LauncherBackend::indexOfBuildId(int buildId) const
@@ -525,75 +582,46 @@ void LauncherBackend::rebuildCatalog() const
{
m_catalog.clear();
// Плоский список в порядке манифеста — новые версии сверху. Раскладку по
// категориям и отбор делает окно выбора версии: ему нужен один список,
// который можно фильтровать, а не готовая структура с разделителями.
const QList<RemoteVersionEntry> remote = m_manifest->versions();
QHash<QString, int> remoteIndex;
remoteIndex.reserve(remote.size());
for (int i = 0; i < remote.size(); ++i)
remoteIndex.insert(remote.at(i).id, i);
const QStringList installed = VersionLoader::installedVersions(gameDir());
const QSet<QString> installedSet(installed.cbegin(), installed.cend());
const auto makeVersionRow = [](const QString &id, const QString &type, bool installed) {
return QVariantMap{{QStringLiteral("kind"),
installed ? QStringLiteral("installed") : QStringLiteral("remote")},
{QStringLiteral("id"), id},
const auto category = [](const QString &type) {
if (type == QLatin1String("release") || type == QLatin1String("snapshot")
|| type == QLatin1String("old_beta") || type == QLatin1String("old_alpha"))
return type;
return QStringLiteral("other");
};
const auto makeVersionRow = [&category](const QString &id, const QString &type, bool isInstalled) {
return QVariantMap{{QStringLiteral("id"), id},
{QStringLiteral("type"), type},
{QStringLiteral("category"), category(type)},
{QStringLiteral("label"), id},
{QStringLiteral("installed"), installed},
{QStringLiteral("selectable"), true},
{QStringLiteral("installed"), isInstalled},
{QStringLiteral("search"), (id + u' ' + type).toLower()}};
};
// Установленные: сначала знакомые манифесту, свежие сверху, затем сборки
// вроде fabric-loader-…, о которых манифест ничего не знает.
QStringList installed = VersionLoader::installedVersions(gameDir());
std::stable_sort(installed.begin(), installed.end(),
[&remoteIndex, &remote](const QString &a, const QString &b) {
const auto ia = remoteIndex.constFind(a);
const auto ib = remoteIndex.constFind(b);
const bool knownA = ia != remoteIndex.constEnd();
const bool knownB = ib != remoteIndex.constEnd();
if (knownA != knownB)
return knownA;
if (knownA)
return ia.value() < ib.value(); // манифест уже отсортирован
return a < b;
});
QSet<QString> installedSet;
for (const QString &id : std::as_const(installed)) {
const auto it = remoteIndex.constFind(id);
const QString type = it != remoteIndex.constEnd() ? remote.at(it.value()).type : QString();
installedSet.insert(id);
m_catalog.append(makeVersionRow(id, type, true));
}
m_catalog.append(QVariantMap{{QStringLiteral("kind"), QStringLiteral("separator")},
{QStringLiteral("id"), QString()},
{QStringLiteral("type"), QString()},
{QStringLiteral("label"), tr("Все версии Minecraft")},
{QStringLiteral("installed"), false},
{QStringLiteral("selectable"), false},
{QStringLiteral("search"), QString()}});
if (remote.isEmpty()) {
m_catalog.append(
QVariantMap{{QStringLiteral("kind"), QStringLiteral("status")},
{QStringLiteral("id"), QString()},
{QStringLiteral("type"), QString()},
{QStringLiteral("label"),
m_manifest->isRefreshing()
? tr("Загрузка списка версий…")
: tr("Список версий недоступен — проверьте соединение")},
{QStringLiteral("installed"), false},
{QStringLiteral("selectable"), false},
{QStringLiteral("search"), QString()}});
return;
}
m_catalog.reserve(remote.size() + installed.size());
QSet<QString> known;
known.reserve(remote.size());
for (const RemoteVersionEntry &entry : remote) {
if (installedSet.contains(entry.id))
continue;
m_catalog.append(makeVersionRow(entry.id, entry.type, false));
known.insert(entry.id);
m_catalog.append(makeVersionRow(entry.id, entry.type, installedSet.contains(entry.id)));
}
// Сборки модлоадеров манифест не знает — они попадают в «Прочие».
QStringList unknown;
for (const QString &id : installed) {
if (!known.contains(id))
unknown << id;
}
std::sort(unknown.begin(), unknown.end());
for (const QString &id : std::as_const(unknown))
m_catalog.append(makeVersionRow(id, QString(), true));
}
// ── Загрузка версий ────────────────────────────────────────────────────────