This commit is contained in:
2026-09-29 09:40:37 +03:00
parent 441021b9a6
commit 6567e731d1
34 changed files with 57 additions and 709 deletions
+1 -16
View File
@@ -21,9 +21,6 @@ namespace {
const QString kManifestUrl =
QStringLiteral("https://launchermeta.mojang.com/mc/game/version_manifest_v2.json");
// Снапшоты выходят примерно раз в неделю, манифест весит около 250 КБ: шесть
// часов — это не больше четырёх запросов в сутки, новая версия видна в тот же
// день, а десяток открытий списка за сессию не стоит ни одного обращения к сети.
constexpr qint64 kCacheTtlSeconds = 6 * 60 * 60;
constexpr int kTransferTimeoutMs = 20000;
@@ -45,13 +42,11 @@ QList<RemoteVersionEntry> parseManifest(const QJsonObject &root)
entry.releaseTime = QDateTime::fromString(
object.value(QStringLiteral("releaseTime")).toString(), Qt::ISODate);
// Описание версии качается по этой ссылке, поэтому чужие схемы не берём.
if (entry.id.isEmpty() || entry.url.scheme() != QLatin1String("https"))
continue;
list << entry;
}
// Mojang и так отдаёт новые сверху, но зависеть от этого не стоит.
std::stable_sort(list.begin(), list.end(),
[](const RemoteVersionEntry &a, const RemoteVersionEntry &b) {
if (a.releaseTime != b.releaseTime)
@@ -61,7 +56,7 @@ QList<RemoteVersionEntry> parseManifest(const QJsonObject &root)
return list;
}
} // namespace
}
VersionManifestService::VersionManifestService(QObject *parent)
: QObject(parent)
@@ -97,8 +92,6 @@ void VersionManifestService::ensureLoaded(Callback callback, bool forceRefresh)
const bool fresh = hasData() && m_fetchedAt.isValid()
&& m_fetchedAt.secsTo(QDateTime::currentDateTimeUtc()) < kCacheTtlSeconds;
if (!forceRefresh && fresh) {
// Колбэк, который то синхронный, то нет, ломает вызывающий код —
// на попадании в кэш отвечаем через событийный цикл.
if (callback)
QTimer::singleShot(0, this, [callback = std::move(callback)] { callback(true, {}); });
return;
@@ -133,7 +126,6 @@ void VersionManifestService::startRefresh()
QList<RemoteVersionEntry> parsed =
parseManifest(QJsonDocument::fromJson(reply->readAll()).object());
// Пустой разбор — испорченный ответ; хороший кэш им не затираем.
if (parsed.isEmpty()) {
finishRefresh(false, Loc::text("version.error.manifestUnparsable"));
return;
@@ -152,10 +144,8 @@ void VersionManifestService::finishRefresh(bool ok, const QString &warning)
m_refreshing = false;
emit refreshingChanged();
// Колбэк может снова позвать ensureLoaded — список забираем себе заранее.
const QList<Callback> pending = std::exchange(m_pending, {});
for (const Callback &callback : pending) {
// Сеть недоступна, но старые данные есть — работаем на них.
if (!ok && hasData())
callback(true, warning);
else
@@ -182,7 +172,6 @@ bool VersionManifestService::readCache()
void VersionManifestService::writeCache()
{
// Папку кэша могли удалить уже после запуска лаунчера.
if (!LauncherPaths::ensureRootExists())
return;
@@ -196,13 +185,9 @@ void VersionManifestService::writeCache()
entry.releaseTime.toString(Qt::ISODate)}};
}
// Своя обёртка: в манифесте Mojang нет отметки времени, по которой можно
// судить о свежести кэша.
const QJsonObject root{{QStringLiteral("fetchedAt"), m_fetchedAt.toString(Qt::ISODate)},
{QStringLiteral("versions"), versions}};
// QSaveFile: оборванная запись не оставит обрезанный кэш, который потом
// пришлось бы отличать от настоящего.
QSaveFile file(LauncherPaths::versionManifestFile());
if (!file.open(QIODevice::WriteOnly))
return;