Implement asynchronous save manager with UI update optimizations and add tests for save functionality

This commit is contained in:
Kirill
2026-09-20 22:14:47 +03:00
parent a09e3df220
commit f25b77b605
12 changed files with 385 additions and 34 deletions
+16 -1
View File
@@ -1,5 +1,18 @@
# Проверки поэтапного переноса из Owniverse.U
## Фоновое сохранение и частота обновления интерфейса
```sh
godot --headless --path Owniverse.GI --script res://tests/test_async_save.gd
godot --headless --path Owniverse.GI --script res://tests/test_ui_updates.gd
```
Сохранение: основной поток создаёт глубокий снимок состояния и параметров. Рабочий поток проверяет данные, записывает зашифрованный временный файл, проверяет чтением и заменяет основное сохранение; диагностический JSON создаётся из того же снимка. Одновременно работает один писатель. Для каждого имени файла хранится только последний ожидающий запрос. `request_save()` подтверждает постановку в очередь; результат записи сообщает сигнал `save_completed`. При закрытии игра ждёт `saves_drained`, при приостановке приложения и уничтожении менеджера очередь завершается через `flush_saves()`. Копирование снимка остаётся в основном потоке.
Тест сохранений пишет только в уникальную временную папку `/tmp`, затем удаляет собственные файлы. Проверяются независимость снимка, работа кадров при записи, объединение запросов, диагностическая копия, сохранность предыдущего файла при ошибках и завершение очереди при уничтожении менеджера. Сообщения `Invalid save`, `Save blocked` и `Cannot write` в этом тесте ожидаемы.
Счётчики, время и кнопки производства обновляются раз в 0,1 секунды. Изменения симуляции помечают суммы для пересчёта; сами суммы вычисляются при обновлении интерфейса. Текст счётчиков применяется только к видимым элементам и только при изменении строки. Переход между страницами, выбор элемента и производство вызывают немедленное обновление. Описания сохраняют прежний интервал 0,25 секунды. Тест интерфейса использует игровую сцену с отключёнными загрузкой, сохранением и автоматической симуляцией, не обращаясь к сохранению игрока.
## Время: шаг 0,1 игрового года
```sh
@@ -15,7 +28,9 @@ godot --headless --path Owniverse.GI --script res://tests/test_time.gd
- `time.datetime` — накопленный возраст, включая необработанную дробную часть;
- `time.year` — последний рассчитанный возраст с точностью до десятой доли года.
Сохранения используют только актуальную структуру состояния и не поддерживают прежние форматы.
Сохранения используют формат 2 и актуальную структуру состояния. Проверяются целостность данных, фоновая запись и повторная загрузка; поддержка и миграция прежних форматов отсутствуют.
Файл с несовместимым номером формата при загрузке переносится в `*.adventure.incompatible.bak` (при совпадении имени добавляется числовой суффикс). Игра начинает новую сессию и может сохранять её в формате 2. Прогресс несовместимого файла не переносится, исходные байты сохраняются в архиве. Ошибка архивирования, расшифровки или повреждение данных текущего формата по-прежнему блокируют перезапись.
## Согласованные правила эволюции