Files

105 lines
6.5 KiB
Markdown
Raw Permalink Normal View History

2026-09-03 09:16:56 +03:00
# BuildSwitcher
2026-10-11 22:32:13 +03:00
`buildswitcher.h`, `buildswitcher.cpp`
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Смена активной сборки и раскатка пака сезонной сборки. Файлы двигает
[BuildArchiveWorker](BuildArchiveWorker.md) в отдельном потоке, а этот класс решает, в каком порядке
это делать, и ведёт журнал, по которому можно продолжить после падения.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Экземпляр держит [LauncherBackend](LauncherBackend.md).
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
## Как устроены сборки на диске
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
В `.minecraft` в каждый момент разложена одна сборка, активная. Содержимое остальных лежит в
архивах `<root>/builds/<id>/<дата>_<имя>.zip`, по одному на сборку.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Общие для всех сборок папки и файлы никогда не архивируются и не удаляются (`sharedTopLevel()`):
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
- `assets`
- `libraries`
- `versions`
- `launcher_profiles.json`
- `runtime`
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Всё остальное (моды, конфиги, миры, скриншоты, `options.txt`) принадлежит сборке.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
В папке сборки могут лежать и другие zip, но файлы с точкой в начале имени не считаются архивом
сборки. Это временные архивы (`.tmp-*.zip`) и скачанные паки сезонных сборок (`.pack-r*.zip`).
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
## switchTo(fromId, fromName, toId, toName, gameDir)
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
1. **Архивация.** Если прежней сборки нет (`fromId < 0`) или папки игры нет, шаг пропускается.
Иначе сначала проверяется свободное место: размер всего, что уйдёт в архив, плюс 256 МБ. Потом
архив пишется во временный `builds/<from>/.tmp-<uuid>.zip`. Если на этом шаге что-то пошло не
так, `.minecraft` не тронута.
2. **Фиксация архива.** Временный файл переименовывается в `<гггг-мм-дд>_<имя>.zip`, остальные
архивы этой сборки удаляются. В `index.json` записывается `state: "switching"` с `from` и `to`.
3. **Очистка.** Из `.minecraft` удаляется всё, кроме общего.
4. **Распаковка.** Если у новой сборки есть архив, он распаковывается. Если нет (новая сборка),
`.minecraft` остаётся пустой, только с общими папками.
5. В `index.json` записываются `state: "idle"` и `active`, испускается `finished(toId)`.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
При ошибке приходит `failed(toId, message, gameDirIntact)`. `gameDirIntact` равен `true`, если
ошибка случилась до очистки. Бэкенд по нему выбирает текст: «ничего не изменилось» или «папка
игры разобрана».
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
`cancel()` работает только на шаге архивации. После него отступать уже некуда.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
## applyPack(buildId, buildName, packZip, removeRelative, note, gameDir)
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Раскатка пака поверх активной сборки. Сначала в `index.json` пишется `state: "applying"` со всем,
что нужно для повтора: имя файла пака, имя сборки, список на удаление и `note`. Потом работник
удаляет файлы прошлой ревизии и распаковывает пак.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
После успеха пак удаляется, `index.json` возвращается в `idle`, а `lastPackEntries()` и
`lastPackNote()` содержат список разложенных файлов и `note`. Бэкенд забирает их в обработчике
`finished` и пишет `season.json`.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
`gameDirIntact` при ошибке раскатки равен `true`, только если работник не успел обработать ни одного
файла.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
## После падения
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
При старте бэкенд вызывает `interruptedSwitchWarning()`. Если в `index.json` осталось
`switching` или `applying`, метод возвращает текст для пользователя, а `resumeInterrupted(gameDir)`
продолжает операцию:
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
- `switching` — архив прежней сборки уже записан целиком, поэтому начинаем с очистки и
распаковываем сборку `to`. Начинать с архивации нельзя: заархивировалась бы полуразобранная
`.minecraft`;
- `applying` — раскатка повторяется с тем же паком и тем же списком на удаление. Если файла пака
уже нет, журнал просто сбрасывается в `idle`.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
Если программа упала во время самой архивации, в журнале ещё ничего нет, и продолжать нечего:
`.minecraft` цела, а временный архив удалится при следующем старте (`sweepTempArchives()` в
конструкторе).
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
## Прочее
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
- `forgetBuild(id)` — удаляет запись о сборке из `index.json` и папку `builds/<id>` целиком.
- `hasArchive(id)` — есть ли у сборки архив.
- `isRunning()`, `stage()`, `status()` (текущий файл), `fraction()` — для панели прогресса.
Сигнал `progressChanged()` не ограничен по частоте и приходит на каждый обработанный файл.
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
### index.json
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
`<root>/builds/index.json`:
2026-09-03 09:16:56 +03:00
2026-10-11 22:32:13 +03:00
```json
{
"state": "idle",
"active": 3,
"builds": {
"2": { "archive": "2026-10-09_Vanilla.zip", "name": "Vanilla", "savedAt": "..." }
}
2026-09-03 09:16:56 +03:00
}
```
2026-10-11 22:32:13 +03:00
Во время работы к нему добавляются `from`, `to`, а для раскатки ещё `pack`, `packName`,
`packRemove`, `packNote`. Пишется через `QSaveFile`.
Деструктор просит работника остановиться и ждёт поток до 5 секунд.
2026-09-03 09:16:56 +03:00
---
При создании этого документа использовался ИИ.