# 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) берётся ссылка на `.json`, файл скачивается, sha1 сверяется с манифестом, и файл сохраняется в `versions//`. Если версии в манифесте нет (профиль модлоадера) или сеть недоступна, берётся файл, который уже лежит на диске. Если нет и его, установка падает. 2. **Родитель.** Если в описании есть `inheritsFrom`, а родителя на диске нет, текущая версия уступает место: в начало очереди встают сначала родитель, потом она сама. Глубина цепочки — до 10 звеньев. 3. **Список файлов.** `VersionLoader::load()` сводит цепочку, и из результата составляется список: клиентский jar, все библиотеки, индекс ресурсов и конфиг логов. Ссылки не по `https` и библиотеки без адреса пропускаются с предупреждением. 4. **Загрузка.** До 8 файлов одновременно. Каждый пишется потоком в `QSaveFile`, sha1 считается на ходу. При временной ошибке (таймаут, обрыв, HTTP 429 или 5xx) и при несовпадении хеша файл ставится обратно в начало очереди с паузой 400 мс, потом 800 мс. После третьей неудачи установка падает. Другие ошибки сразу её прерывают. Таймаут на файл 30 секунд. 5. **Ресурсы.** Как только индекс ресурсов на диске, он разворачивается в задачи на загрузку из `resources.download.minecraft.net//`. До этого момента в общем объёме учитывается `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()` отвечает отказом. Деструктор блокирует сигналы и отменяет установку. --- При создании этого документа использовался ИИ.