77 lines
6.4 KiB
Markdown
77 lines
6.4 KiB
Markdown
# VersionInstaller
|
||
|
||
`versioninstaller.h`, `versioninstaller.cpp`
|
||
|
||
Ставит версию игры в `.minecraft`: описание версии, клиентский jar, библиотеки, индекс ресурсов,
|
||
сами ресурсы и конфиг логирования. Одновременно ставится одна версия, остальные ждут в очереди.
|
||
Всё работает в GUI-потоке на асинхронной сети.
|
||
|
||
Экземпляр держит [LauncherBackend](LauncherBackend.md). Ещё им пользуется
|
||
[ModLoaderInstaller](ModLoaderInstaller.md): перед установкой модлоадера нужно поставить базовую
|
||
версию, а после установки докачать библиотеки нового профиля.
|
||
|
||
## Интерфейс
|
||
|
||
- `install(gameDir, versionId)` — поставить в очередь. Если эта версия уже ставится или ждёт,
|
||
ничего не происходит. Если id содержит слэши, `.` или `..`, сразу приходит `failed`.
|
||
- `cancel()` — отменить текущую установку и очистить очередь.
|
||
- `isRunning()`, `isQueued(id)`, `versionId()`.
|
||
- Для панели прогресса: `stage()`, `currentFile()`, `bytesDone()`, `bytesTotal()`, `fraction()`
|
||
(от 0 до 1; −1, если общий объём неизвестен).
|
||
|
||
Сигналы: `started(id)`, `progressChanged()` (не чаще раза в 100 мс), `finished(id)`,
|
||
`failed(id, message)`, `canceled(id)`.
|
||
|
||
`started` испускается один раз на всю серию установок из очереди. `finished` испускается для
|
||
каждой версии.
|
||
|
||
## Как идёт установка
|
||
|
||
1. **Описание версии.** У [VersionManifestService](VersionManifestService.md) берётся ссылка на
|
||
`<id>.json`, файл скачивается, sha1 сверяется с манифестом, и файл сохраняется в
|
||
`versions/<id>/`. Если версии в манифесте нет (профиль модлоадера) или сеть недоступна, берётся
|
||
файл, который уже лежит на диске. Если нет и его, установка падает.
|
||
2. **Родитель.** Если в описании есть `inheritsFrom`, а родителя на диске нет, текущая версия
|
||
уступает место: в начало очереди встают сначала родитель, потом она сама. Глубина цепочки — до
|
||
10 звеньев.
|
||
3. **Список файлов.** `VersionLoader::load()` сводит цепочку, и из результата составляется список:
|
||
клиентский jar, все библиотеки, индекс ресурсов и конфиг логов. Ссылки не по `https` и
|
||
библиотеки без адреса пропускаются с предупреждением.
|
||
4. **Загрузка.** До 8 файлов одновременно. Каждый пишется потоком в `QSaveFile`, sha1 считается на
|
||
ходу. При временной ошибке (таймаут, обрыв, HTTP 429 или 5xx) и при несовпадении хеша файл
|
||
ставится обратно в начало очереди с паузой 400 мс, потом 800 мс. После третьей неудачи
|
||
установка падает. Другие ошибки сразу её прерывают. Таймаут на файл 30 секунд.
|
||
5. **Ресурсы.** Как только индекс ресурсов на диске, он разворачивается в задачи на загрузку из
|
||
`resources.download.minecraft.net/<xx>/<hash>`. До этого момента в общем объёме учитывается
|
||
`totalSize` из описания версии, а потом он заменяется точной суммой.
|
||
6. **Старые ресурсы.** У версий до 1.6 (`virtual`, `map_to_resources`, `assets` = `legacy` или
|
||
`pre-1.6`) игра ищет ресурсы не в `assets/objects`, а в обычной папке с именами файлов. Они
|
||
копируются в `assets/virtual/legacy/` или в `resources/` по 64 файла за такт таймера, чтобы
|
||
окно не подвисало.
|
||
7. **Проверка.** `GameLauncher::missingFiles()`. Если чего-то нет, это ошибка.
|
||
|
||
## Уже скачанные файлы
|
||
|
||
Существующий файл считается целым, если его размер совпадает с ожидаемым (или размер неизвестен).
|
||
Хеш не пересчитывается: перехеширование сотен мегабайт ресурсов при каждой установке обходится
|
||
дороже, чем редкий битый файл. Для будущей кнопки «Проверить файлы» есть флаг `m_verifyExisting`,
|
||
сейчас он всегда `false`.
|
||
|
||
Пропуск файлов идёт пачками по 256 за такт. Иначе установка версии, у которой уже есть все
|
||
ресурсы, повесила бы окно на перебор десятков тысяч записей.
|
||
|
||
## Ошибки и отмена
|
||
|
||
При ошибке все активные загрузки обрываются, недописанные файлы откатываются (`QSaveFile`), а
|
||
**очередь очищается**: остальные версии из неё тоже не поставятся. К тексту ошибки добавляются до
|
||
трёх накопленных предупреждений.
|
||
|
||
Отмена обрывает активные запросы и ждёт, пока все они вернутся. Только после этого приходит
|
||
`canceled`. Пока отмена не закончилась, `install()` отвечает отказом.
|
||
|
||
Деструктор блокирует сигналы и отменяет установку.
|
||
|
||
---
|
||
|
||
При создании этого документа использовался ИИ.
|