cleanups
This commit is contained in:
+1
-54
@@ -22,13 +22,8 @@ const QString kIndexFile = QStringLiteral("index.json");
|
||||
const QString kStateSwitching = QStringLiteral("switching");
|
||||
const QString kStateApplying = QStringLiteral("applying");
|
||||
const QString kStateIdle = QStringLiteral("idle");
|
||||
|
||||
// Запас на архив: точный размер заранее неизвестен, но упасть на середине
|
||||
// записи из-за полного диска хуже, чем отказаться заранее.
|
||||
constexpr qint64 kFreeSpaceSlackBytes = 256LL * 1024 * 1024;
|
||||
|
||||
// Имя файла из названия сборки: оно попадает в файловую систему, поэтому всё
|
||||
// небезопасное заменяется подчёркиванием.
|
||||
QString slug(const QString &name)
|
||||
{
|
||||
QString result;
|
||||
@@ -68,14 +63,10 @@ qint64 directorySize(const QString &path, const QStringList &excludeTop)
|
||||
return total;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
}
|
||||
|
||||
QStringList BuildSwitcher::sharedTopLevel()
|
||||
{
|
||||
// Общий кэш, одинаковый для всех сборок и всегда восстановимый загрузкой.
|
||||
// versions/ здесь не случайно: без libraries/ описания версий бесполезны, а
|
||||
// две сборки на одной версии игры иначе таскали бы по копии клиентского jar.
|
||||
// Чем сборка запускается, помнит resolvedVersionId, а не набор папок.
|
||||
return {QStringLiteral("assets"),
|
||||
QStringLiteral("libraries"),
|
||||
QStringLiteral("versions"),
|
||||
@@ -118,13 +109,9 @@ QString BuildSwitcher::buildDir(int buildId) const
|
||||
return LauncherPaths::buildDir(buildId);
|
||||
}
|
||||
|
||||
// На сборку — ровно один архив: имя с датой меняется от переключения к
|
||||
// переключению, поэтому ищем по расширению, а не по имени.
|
||||
QString BuildSwitcher::archivePathOf(int buildId) const
|
||||
{
|
||||
const QDir dir(buildDir(buildId));
|
||||
// Рядом лежат служебные .tmp-*.zip и .pack-*.zip. На Unix они скрытые и в
|
||||
// выборку не попадают, на Windows — попадают, поэтому отсекаем по имени.
|
||||
const QStringList zips = dir.entryList({QStringLiteral("*.zip")}, QDir::Files, QDir::Time);
|
||||
for (const QString &name : zips) {
|
||||
if (!name.startsWith(u'.'))
|
||||
@@ -165,7 +152,6 @@ void BuildSwitcher::switchTo(int fromBuildId,
|
||||
m_total = 0;
|
||||
m_worker->clearCancel();
|
||||
|
||||
// Первая сборка за всё время: архивировать нечего, сразу разворачиваем цель.
|
||||
if (fromBuildId < 0 || !QDir(gameDir).exists()) {
|
||||
m_state = Clearing;
|
||||
beginClearing();
|
||||
@@ -201,8 +187,6 @@ void BuildSwitcher::switchTo(int fromBuildId,
|
||||
|
||||
void BuildSwitcher::cancel()
|
||||
{
|
||||
// Отмена имеет смысл только на архивации: после очистки папки отступать
|
||||
// уже некуда, там нужно доводить дело до конца.
|
||||
if (m_state == Archiving)
|
||||
m_worker->requestCancel();
|
||||
}
|
||||
@@ -260,8 +244,6 @@ void BuildSwitcher::onWorkerFinished(bool ok, const QString &error)
|
||||
return;
|
||||
case Clearing:
|
||||
if (!ok) {
|
||||
// Папка осталась разобранной: активной сборкой считается прежняя,
|
||||
// отметка о незавершённом переключении в index.json остаётся.
|
||||
fail(error, /*gameDirIntact=*/false);
|
||||
return;
|
||||
}
|
||||
@@ -276,10 +258,6 @@ void BuildSwitcher::onWorkerFinished(bool ok, const QString &error)
|
||||
return;
|
||||
case Applying:
|
||||
if (!ok) {
|
||||
// Архив проверяется целиком до первой записи, поэтому нулевой
|
||||
// счётчик означает, что в папке игры ничего не тронуто. Иначе
|
||||
// часть файлов уже заменена: отметка в index.json остаётся, и
|
||||
// следующий запуск доиграет раскатку из сохранённого пака.
|
||||
fail(error, /*gameDirIntact=*/m_done == 0);
|
||||
return;
|
||||
}
|
||||
@@ -294,7 +272,6 @@ void BuildSwitcher::commitArchive()
|
||||
+ QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd"))
|
||||
+ u'_' + slug(m_fromName) + QStringLiteral(".zip");
|
||||
|
||||
// На сборку — один архив: новый встаёт на место, все прежние уходят.
|
||||
if (QFile::exists(target) && !QFile::remove(target)) {
|
||||
QFile::remove(m_tempZip);
|
||||
fail(Loc::text("switch.error.archiveReplaceFailed").arg(QDir::toNativeSeparators(target)), true);
|
||||
@@ -309,14 +286,10 @@ void BuildSwitcher::commitArchive()
|
||||
const QDir dir(buildDir(m_fromId));
|
||||
const QStringList zips = dir.entryList({QStringLiteral("*.zip")}, QDir::Files);
|
||||
for (const QString &name : zips) {
|
||||
// Служебные файлы под удаление не попадают: в .pack-*.zip может лежать
|
||||
// пак недоигранной раскатки, без которого её нечем довести до конца.
|
||||
if (!name.startsWith(u'.') && dir.absoluteFilePath(name) != target)
|
||||
QFile::remove(dir.absoluteFilePath(name));
|
||||
}
|
||||
|
||||
// Отметка ставится до первого разрушающего действия: по ней следующий
|
||||
// запуск поймёт, что переключение оборвалось на середине.
|
||||
QJsonObject index = readIndex();
|
||||
index.insert(QStringLiteral("state"), kStateSwitching);
|
||||
index.insert(QStringLiteral("from"), m_fromId);
|
||||
@@ -344,8 +317,6 @@ void BuildSwitcher::beginRestoring()
|
||||
{
|
||||
const QString archive = archivePathOf(m_toId);
|
||||
if (archive.isEmpty()) {
|
||||
// Сборка ещё ни разу не запускалась: пустая .minecraft рядом с общими
|
||||
// кэшами — это и есть её правильное начальное состояние.
|
||||
m_state = Restoring;
|
||||
finish();
|
||||
return;
|
||||
@@ -356,8 +327,6 @@ void BuildSwitcher::beginRestoring()
|
||||
requestRestore(archive, m_gameDir);
|
||||
}
|
||||
|
||||
// ── Раскатка пака сезонной сборки ──────────────────────────────────────────
|
||||
|
||||
void BuildSwitcher::applyPack(int buildId,
|
||||
const QString &buildName,
|
||||
const QString &packZipPath,
|
||||
@@ -396,8 +365,6 @@ void BuildSwitcher::applyPack(int buildId,
|
||||
m_total = 0;
|
||||
m_worker->clearCancel();
|
||||
|
||||
// Отметка ставится до первого разрушающего действия — как и при смене
|
||||
// сборки. Пак лежит рядом и переживёт перезапуск вместе с ней.
|
||||
QJsonObject index = readIndex();
|
||||
index.insert(QStringLiteral("state"), kStateApplying);
|
||||
index.insert(QStringLiteral("to"), buildId);
|
||||
@@ -419,8 +386,6 @@ void BuildSwitcher::beginApplying()
|
||||
|
||||
void BuildSwitcher::finishApplying()
|
||||
{
|
||||
// Пак сделал своё дело: держать сотни мегабайт рядом с архивом сборки не
|
||||
// за чем, а его наличие означало бы недоигранную раскатку.
|
||||
QFile::remove(m_packPath);
|
||||
|
||||
QJsonObject index = readIndex();
|
||||
@@ -477,16 +442,11 @@ void BuildSwitcher::fail(const QString &message, bool gameDirIntact)
|
||||
emit failed(toId, message, gameDirIntact);
|
||||
}
|
||||
|
||||
// ── Удаление сборки ────────────────────────────────────────────────────────
|
||||
|
||||
bool BuildSwitcher::forgetBuild(int buildId)
|
||||
{
|
||||
if (buildId < 0)
|
||||
return true;
|
||||
|
||||
// Запись убираем в любом случае: даже если файл не удалось стереть, ссылаться
|
||||
// на архив несуществующей сборки нельзя — иначе он «воскреснет» под новой
|
||||
// сборкой с тем же id.
|
||||
QJsonObject index = readIndex();
|
||||
QJsonObject builds = index.value(QStringLiteral("builds")).toObject();
|
||||
builds.remove(QString::number(buildId));
|
||||
@@ -499,8 +459,6 @@ bool BuildSwitcher::forgetBuild(int buildId)
|
||||
return !dir.exists() || dir.removeRecursively();
|
||||
}
|
||||
|
||||
// ── Незавершённое переключение ─────────────────────────────────────────────
|
||||
|
||||
QString BuildSwitcher::interruptedSwitchWarning() const
|
||||
{
|
||||
const QJsonObject index = readIndex();
|
||||
@@ -540,8 +498,6 @@ void BuildSwitcher::resumeInterrupted(const QString &gameDir)
|
||||
if (toId < 0 || m_state != Idle)
|
||||
return;
|
||||
|
||||
// Архив исходной сборки уже записан целиком — доигрываем только вторую
|
||||
// половину: очистку и распаковку цели.
|
||||
m_fromId = -1;
|
||||
m_toId = toId;
|
||||
m_toName = index.value(QStringLiteral("builds"))
|
||||
@@ -566,10 +522,6 @@ void BuildSwitcher::resumeApplying(const QJsonObject &index, const QString &game
|
||||
const QString packName = index.value(QStringLiteral("pack")).toString();
|
||||
const QString packPath = buildDir(toId) + u'/' + packName;
|
||||
if (packName.isEmpty() || packName.contains(u'/') || !QFile::exists(packPath)) {
|
||||
// Пак не сохранился — доигрывать нечем. Отметку снимаем, иначе
|
||||
// предупреждение о незавершённой установке будет всплывать вечно;
|
||||
// сборка при этом остаётся наполовину обновлённой, и лаунчер покажет
|
||||
// её как требующую обновления.
|
||||
QJsonObject cleaned = index;
|
||||
cleaned.insert(QStringLiteral("state"), kStateIdle);
|
||||
cleaned.remove(QStringLiteral("pack"));
|
||||
@@ -601,8 +553,6 @@ void BuildSwitcher::resumeApplying(const QJsonObject &index, const QString &game
|
||||
beginApplying();
|
||||
}
|
||||
|
||||
// ── Реестр архивов ─────────────────────────────────────────────────────────
|
||||
|
||||
QJsonObject BuildSwitcher::readIndex() const
|
||||
{
|
||||
QFile file(LauncherPaths::buildStorageDir() + u'/' + kIndexFile);
|
||||
@@ -626,9 +576,6 @@ void BuildSwitcher::writeIndex(const QJsonObject &index) const
|
||||
file.commit();
|
||||
}
|
||||
|
||||
// Оборванная запись архива оставляет .tmp-*.zip: он никому не нужен и может
|
||||
// занимать гигабайты. Пакеты .pack-*.zip не трогаем: по ним доигрывается
|
||||
// прерванная раскатка сезонной сборки, а лишний тут удаляет resumeApplying.
|
||||
void BuildSwitcher::sweepTempArchives() const
|
||||
{
|
||||
QDir root(LauncherPaths::buildStorageDir());
|
||||
|
||||
Reference in New Issue
Block a user