builds fixes
This commit is contained in:
+89
-61
@@ -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));
|
||||
}
|
||||
|
||||
// ── Загрузка версий ────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user