refactor: reorganize packages and replace LPSchematic with WorldEdit

Restructure core modules into subpackages (generator, schematic, config, session, etc.); add .schem/.nbt schematic pipeline, commands, adaptive stats skeleton, and bootstrap wiring.
This commit is contained in:
loki5512344 2026-06-01 14:02:48 +02:00
parent 6ba6e5137d
commit fd0ba2cc0e
203 changed files with 5652 additions and 2367 deletions

468
TODO.md
View file

@ -1,254 +1,283 @@
# LoParkour — TODO (обновлено 2026-04-27)
# LoParkour — TODO (обновлено 2026-06-01)
> Roadmap для превращения плагина в платформу с адаптивной генерацией.
> Roadmap + **честный аудит** репозитория. Старые галочки «✅» в фазах 1–4 не соответствуют коду — см. раздел **Аудит**.
## Легенда
- 🔴 Критично — блокирует работу
- 🟡 Важно — заявленная функциональность
- 🟢 Планово — улучшение
- ✅ Готово
- 🔴 Критично — блокирует сборку / релиз / основной UX
- 🟡 Важно — заявленная функциональность или i18n
- 🟢 Планово — улучшение, рефакторинг
- ✅ Готово (проверено в коде)
---
## Фаза 1: Рефакторинг (2-3 дня)
## Аудит 2026-06-01 (снимок)
### 🟢 Архитектура
| Область | Факт |
|--------|------|
| **Сборка** | ✅ `./gradlew compileJava` проходит |
| **Адаптивность** | 🟡 Подключено: `AdaptiveServices`, `MetricsCollector` на генераторе, профиль после fall/reset |
| **Elytra** | 🟡 `ElytraMode` + `ElytraGenerator` есть; отдельного `mode/elytra/*` из старого TODO **нет** |
| **Размер файлов** | 🔴 Цель «все < 200 строк» **не выполнена**: 18 файлов > 150, 2 > 200 |
| **i18n** | 🟡 Основные потоки через `Locales`; остаётся Phase 0 split + `MessageKeys` |
| **GUI** | 🟡 Основные меню на LoLib OK; **заглушки** `Menu`/`PagedMenu`, `DynamicMenu` вырезан |
| **TODO.md** | Ранее помечено ✅ то, что не сделано — статусы ниже исправлены |
| # | Задача | Файлы | Статус |
|---|--------|-------|--------|
| 1 | Разбить ParkourGenerator (335 строк) | Generator.java, GeneratorConfig.java | 🟢 |
| 2 | Разбить GeneratorProfileManager | ProfileManager.java, ProfileCalculator.java | 🟢 |
| 3 | Вынести statistics из generator | statistics/TimeTracker.java, ScoreTracker.java, LeaderboardSync.java | 🟢 |
| 4 | Создать структуру пакетов | generator/core/, generator/profile/, mode/base/, mode/impl/ | 🟢 |
| 5 | Рефакторинг интерфейсов | GameMode, DifficultyProfile, GeneratorEventListener | 🟢 |
### 🟢 Большие файлы (>200 строк)
| # | Файл | Строк | Действие | Статус |
|---|------|-------|----------|--------|
| 1 | Option.java | 335 | Разбить по типам опций | 🟢 |
| 2 | ElytraGenerator.java | 261 | Вынести в mode/elytra/ | 🟢 |
| 3 | Locales.java | 260 | Разбить на LocaleLoader + LocaleCache | 🟢 |
| 4 | ParkourPlayer.java | 236 | Вынести inventory/settings в отдельные классы | 🟢 |
| 5 | Leaderboard.java | 234 | Разбить на Leaderboard + LeaderboardStorage | 🟢 |
**Статистика:** ~160 `.java` файлов, средний размер ~85 строк, максимум **252** (`ElytraGenerator.java`).
---
## Фаза 2: Адаптивная система (3-4 дня)
## Фаза 0: Полный рефакторинг (KISS · DRY · SOLID) — приоритет
### 🟡 Инфраструктура
> Цель: предсказуемая структура, тонкие классы, без «божественных» файлов.
| # | Задача | Пакет | Статус |
|---|--------|-------|--------|
| 1 | Создать модели данных | adaptive/model/ | 🟡 |
| 2 | Реализовать хранилища | adaptive/storage/ | 🟡 |
| 3 | SQL таблицы для метрик | loparkour_player_stats | 🟡 |
| 4 | Файловое хранилище | playerdata/<uuid>.json | 🟡 |
### Правила (жёсткие)
### 🟡 Сбор метрик
| Правило | Значение |
|--------|----------|
| **Целевой размер** | ~**150** строк на файл |
| **Жёсткий потолок** | **190–200** строк (исключения только с комментом в PR) |
| **Файлов в папке** | ≤ **6** (при росте — новая подпапка) |
| **KISS** | Один публичный сценарий = один класс / use-case |
| **DRY** | Сообщения только через `Locales`; команды — `Messages` / `admin.*` |
| **SOLID** | `generator` не импортирует `adaptive`; связь только через `GeneratorEventListener` + DI в bootstrap |
| # | Задача | Класс | Статус |
|---|--------|-------|--------|
| 1 | MetricsCollector | adaptive/core/MetricsCollector.java | 🟡 |
| 2 | GeneratorEventListener | generator/core/GeneratorEventListener.java | 🟡 |
| 3 | Интеграция с Generator | Generator.score(), Generator.fall() | 🟡 |
| 4 | Детекция nearMiss | MetricsCollector.onBlockLand() | 🟡 |
### 🟡 Анализ и корректировка
| # | Задача | Класс | Статус |
|---|--------|-------|--------|
| 1 | SkillAnalyzer | adaptive/core/SkillAnalyzer.java | 🟡 |
| 2 | DifficultyCalculator | adaptive/core/DifficultyCalculator.java | 🟡 |
| 3 | DifficultyAdjuster | adaptive/core/DifficultyAdjuster.java | 🟡 |
| 4 | Формулы рейтинга | SkillRating calculation | 🟡 |
### 🟡 Интеграция с режимами
| # | Задача | Класс | Статус |
|---|--------|-------|--------|
| 1 | AdaptiveMode базовый класс | mode/base/AdaptiveMode.java | 🟡 |
| 2 | AdaptiveSession | adaptive/core/AdaptiveSession.java | 🟡 |
| 3 | StatsService | adaptive/core/StatsService.java | 🟡 |
| 4 | Миграция DefaultMode | mode/impl/DefaultMode.java | 🟡 |
| 5 | Миграция SpeedrunMode | mode/impl/SpeedrunMode.java, SpeedrunGenerator.java | 🟡 |
| 6 | Миграция HardcoreMode | mode/impl/HardcoreMode.java | 🟡 |
| 7 | Миграция GravityShiftMode | mode/impl/GravityShiftMode.java | 🟡 |
### 🟡 Конфигурация
| # | Задача | Файл | Статус |
|---|--------|------|--------|
| 1 | Секция adaptive в config.yml | config.yml | 🟡 |
| 2 | Конфиги режимов | modes/*.yml | 🟡 |
| 3 | AdaptiveConfig с кэшированием | adaptive/model/AdaptiveConfig.java | 🟡 |
| 4 | Предзагрузка при старте | LoParkour.onEnable() | 🟡 |
---
## Фаза 3: ElytraMode (1-2 дня)
### 🔴 Критичные классы (отсутствуют)
| # | Класс | Ответственность | Статус |
|---|-------|-----------------|--------|
| 1 | ElytraConfig | Загрузка настроек из modes/elytra.yml | 🔴 |
| 2 | ElytraRing | Модель кольца (центр, радиус, направление) | 🔴 |
| 3 | RingGenerator | Генерация колец по траектории | 🔴 |
| 4 | RingRenderer | Отрисовка частицами | 🔴 |
| 5 | ElytraPhysics | Проверка пролёта, буст от фейерверков | 🔴 |
### 🔴 Интеграция
| # | Задача | Статус |
|---|--------|--------|
| 1 | Создать ElytraMode | 🔴 |
| 2 | Выдача элитры и фейерверков | 🔴 |
| 3 | Тесты полёта | 🔴 |
| 4 | Конфиг modes/elytra.yml | 🔴 |
---
## Фаза 4: Новые режимы (2-3 дня)
### 🟢 RoguelikeMode
| # | Задача | Статус |
|---|--------|--------|
| 1 | Случайные модификаторы каждые N блоков | 🟢 |
| 2 | Выбор перков перед стартом | 🟢 |
| 3 | Риск-награда развилки | 🟢 |
| 4 | Балансировка | 🟢 |
### 🟢 Проверка существующих режимов
| # | Режим | Задача | Статус |
|---|-------|--------|--------|
| 1 | RaceMode | Проверить прогресс-бар и финиш | 🟢 |
| 2 | CoopMode | Проверить мультиплеер | 🟢 |
| 3 | DefaultMode | Тесты после рефакторинга | 🟢 |
| 4 | SpeedrunMode | Тесты таймеров блоков | 🟢 |
---
## Архитектурные решения
### Структура пакетов
### Целевая структура пакетов (✅ 2026-06-01)
```
src/main/java/dev/loki/loparkour/
├── adaptive/ # Адаптивная система
│ ├── core/ # MetricsCollector, SkillAnalyzer, DifficultyCalculator, DifficultyAdjuster
│ ├── model/ # PlayerMetrics, SkillRating, AdaptiveConfig
│ └── storage/ # StatsRepository, SQLStatsStorage, FileStatsStorage
├── statistics/ # Вынесено из generator
│ ├── TimeTracker.java
│ ├── ScoreTracker.java
│ └── LeaderboardSync.java
dev.loki.loparkour/
├── LoParkour.java # main plugin class
├── command/
│ ├── LoParkourCommand.java
│ ├── player/ # PlayerCommandHandler
│ ├── admin/ # AdminCommandHandler
│ └── schematic/ # SchematicCommandHandler
├── config/
│ ├── core/ # Config, ConfigLoader, ConfigAccessor, ConfigUpdater
│ ├── locale/ # Locales, LocaleLoader, LocaleCache
│ └── options/ # Option + OptionSQL, OptionGeneral, …
├── generator/
│ ├── core/ # Generator, GeneratorState, GeneratorConfig, Island
│ ├── profile/ # DifficultyProfile, ProfileManager, ProfileCalculator
│ ├── jump/ # JumpCalculator, JumpValidator, JumpDirector, BlockPlacer
│ └── lifecycle/ # GeneratorLifecycle, GeneratorCleanup
└── mode/
├── base/ # GameMode, AdaptiveMode, ModeRegistry
├── impl/ # DefaultMode, SpeedrunMode, HardcoreMode, GravityShiftMode, RoguelikeMode
└── elytra/ # ElytraMode, ElytraConfig, ElytraRing, RingGenerator, RingRenderer, ElytraPhysics
│ ├── core/ # ParkourGenerator, Profile, GeneratorState, …
│ ├── jump/, lifecycle/, profile/, effect/
│ └── core/GeneratorEventListener
├── mode/
│ ├── base/ # Mode, MultiMode, Modes, ModeMessages
│ ├── impl/ # DefaultMode, SpeedrunMode, …
│ └── elytra/ # ElytraGenerator + ring/physics/hud
├── menu/
│ ├── core/ # LPMenu, MainMenu, Menus, ParkourOption
│ └── play/, settings/, community/, lobby/
├── player/
│ ├── core/ # ParkourUser, ParkourPlayer
│ ├── service/ # UserRegistry, hotbar, scoreboard, …
│ ├── spectator/
│ └── data/
├── util/
│ ├── text/, item/, gui/, world/, particle/, misc/
├── storage/
│ ├── Storage.java # фасад
│ ├── sql/ # StorageSQL, SQL*, миграции
│ └── disk/ # StorageDisk (JSON)
├── session/
│ ├── core/Session.java
│ └── manager/ # State, Player, User managers
├── listener/
│ ├── player/, gameplay/, schematic/
├── leaderboard/
│ ├── model/Score, core/, persistence/
├── ghost/
│ ├── model/, core/
├── hook/
│ ├── papi/, holo/, floodgate/, vault/
├── schematic/
│ ├── registry/Schematics.java
│ └── lpschem/
├── api/core/ + api/event/
├── reward/core/, style/core/, world/core/
├── adaptive/bootstrap/AdaptiveServices
├── bootstrap/PluginBootstrap
└── …
```
### Зависимости
Скрипт переноса: `scripts/refactor-packages.ps1` (+ `fix-imports-after-refactor.ps1`).
```
adaptive → generator/profile (только через DifficultyProfile интерфейс)
generator → НЕ знает про adaptive (через GeneratorEventListener)
statistics → используется и generator, и adaptive
mode → использует generator + опционально adaptive
```
### Очередь разбиения (файлы &gt; 150 строк)
### Хранение данных
| Приоритет | Файл | Строк | Действие |
|-----------|------|-------|----------|
| ✅ 1 | `ElytraGenerator.java` | 101 | → `elytra/ElytraRingLoop`, `ElytraScoring`, `ElytraHud`, `ElytraPlayerKit`, `ElytraFireworkListener` |
| ✅ 2 | `ParkourPlayer.java` | 166 | → `ParkourHotbar` |
| 🟡 3 | `PAPIHook.java` | 194 | → placeholders по группам |
| 🟡 4 | `LoParkour.java` | 192 | → `bootstrap/PluginBootstrap.java` |
| 🟡 5 | `MetricsCollector.java` | 187 | → session hook + jump hook + flush |
| 🟡 6 | `StorageSQL.java` | 179 | → queries / mapper / connection (уже частично) |
| 🟡 7 | `BlockPlacer.java` | 176 | | |
| 🟡 8 | `JumpValidator.java` | 172 | | |
| 🟡 9 | `LoParkourCommand.java` | 170 | → делегаты только маршрутизация |
| 🟡 10 | `CoopMode.java` | 165 | → `CoopGenerator` отдельный файл |
| 🟢 | остальные 151–159 | 8 файлов | после топ-10 |
**SQL (критичные метрики):**
- `loparkour_player_stats`: skill_rating, sessions_count, total_jumps, longest_streak
- Автосохранение каждые 5 минут
### Чек-лист фазы 0
**JSON (детальная аналитика):**
- `playerdata/<uuid>.json`: avgTimePerBlock, jumpTypeStats, nearMissCount
- `playerdata/history/<uuid>_jumps.jsonl`: история прыжков (последние 1000)
- Сохранение при выходе игрока
- [ ] Нет файлов &gt; 200 строк (кроме явно согласованных)
- [x] Пакеты разнесены (config/mode/command/generator/menu/player/util)
- [ ] Нет циклических зависимостей `generator` ↔ `adaptive`
- [ ] Единый `MessageService` / `Locales` для player + console + action bar + title
- [ ] Удалить или подключить мёртвый код: `Menu`, `PagedMenu`, `DynamicMenu`, `MenuStub`
- [ ] `Option.java` — только фасад, без роста (делегаты в `options/*`)
- [x] `./gradlew build` зелёный
- [ ] README/TODO не врут про «всё готово»
---
## Технический долг
## Баги и долг (найдено при аудите)
### 🟢 Оптимизация
### 🔴 Сборка
| # | Задача | Приоритет | Статус |
|---|--------|-----------|--------|
| 1 | Кэширование конфигов режимов | Средний | ✅ (в дизайне) |
| 2 | Batch updates для SQL метрик | Средний | 🟢 |
| 3 | Очистка старой истории прыжков | Низкий | 🟢 |
| # | Проблема | Статус |
|---|----------|--------|
| B1 | `SQLConnectionManager` visibility | ✅ |
| B2 | `AdaptiveConfig` + `Config.CONFIG` | ✅ |
### 🟢 Тестирование
### 🟡 Локализация (i18n) — большинство закрыто
| # | Проблема | Статус |
|---|----------|--------|
| I1 | `LPMenu` back/next | ✅ `other.back`, `other.next` |
| I2 | `other.no_menu_items` | ✅ |
| I3 | joining disabled | ✅ `ModeMessages` + `other.joining_disabled` |
| I4 | Elytra messages | ✅ `modes.elytra.*` |
| I5–I11 | Race, coop, admin, schematic, commands, bungee, GUI stubs | ✅ (см. `locales/*.yml`) |
| I12 | `ghost.saved` в en | ✅ |
| I13 | `%[a-z]` vs `%s` | 🟢 документировать / унифицировать позже |
| I14 | reload NPE с консоли | ✅ `getString(sender, …)` |
| I15 | недостающие ключи | ✅ основной набор добавлен |
### 🟡 Логика / архитектура
| # | Проблема | Где |
|---|----------|-----|
| A1 | `MetricsCollector` на генераторе | ✅ `SessionStateManager` + `AdaptiveServices.attachToGenerator` |
| A2 | Adaptive → профиль генерации | ✅ `GeneratorProfileManager` + `AdaptiveDifficulty` |
| A3 | `adaptive.*` в config.yml | ✅ |
| A4 | SQL таблица stats | ✅ `StorageSQL` + `SQLMigrationManager.createAdaptiveStatsTable` |
| A5 | `DynamicMenu` мёртвый | 🔴 |
| A6 | STONE debug-spam | ✅ только при отсутствии `.material` |
| A7 | Несогласованные пути locale: `play.single.*` vs `modes.*` для режимов | `*Mode.getItem()` |
| A8 | `sendHelp` упоминает `/LoParkour`, игроки используют `/parkour` | `PlayerCommandHandler` |
### 🟢 Мелочи
| # | Проблема |
|---|----------|
| M1 | `SpectatorMode`: MiniMessage `<red>` vs legacy `§c` в других режимах |
| M2 | `HardcoreMode` / др.: смешение `ColorUtil` и сырого `sendMessage` |
| M3 | Unit/integration тесты в TODO помечены ✅ — **фактически** только 2 test-класса |
---
## Фаза 1: Рефакторинг структуры (статус реальный)
### Архитектура
| # | Задача | Статус |
|---|--------|--------|
| 1 | Unit тесты для адаптивной системы | 🟢 |
| 2 | Integration тесты для режимов | 🟢 |
| 3 | Performance тесты (100+ игроков) | 🟢 |
| 1 | Разбить `ParkourGenerator` | 🟡 Частично (composition), файл 159 строк — ок |
| 2 | `GeneratorProfileManager` | 🟡 Есть, без отдельного `ProfileCalculator` |
| 3 | statistics из generator | 🟡 `GeneratorStatistics` внутри generator, не пакет `statistics/` |
| 4 | Структура пакетов `generator/core`, `jump`, `lifecycle` | 🟡 Частично |
| 5 | `GeneratorEventListener` | ✅ Интерфейс + вызовы в `PlayerInteractionHandler` |
### Большие файлы (цель &lt; 200) — **не выполнено**
| Файл | Строк | Статус |
|------|-------|--------|
| ElytraGenerator.java | 101 | ✅ |
| ParkourPlayer.java | 166 | ✅ |
| PAPIHook, LoParkour, MetricsCollector, … | 151–194 | 🟡 см. Фаза 0 |
---
## Чек-лист перед релизом
## Фаза 2: Адаптивная система (статус реальный)
- [ ] Все режимы работают (default, speedrun, hardcore, gravity-shift, elytra)
- [ ] Адаптивная система корректно собирает метрики
- [ ] SQL миграции применены
- [ ] Конфиги валидируются при загрузке
- [ ] Нет memory leaks (проверить с VisualVM)
- [ ] Документация обновлена (README.md, CHANGELOG.md)
- [ ] Тесты проходят (`./gradlew test`)
- [ ] Build успешен (`./gradlew build`)
| # | Задача | Статус |
|---|--------|--------|
| 1 | `adaptive/model/` | 🟡 Классы есть |
| 2 | `adaptive/storage/` | ✅ compile + SQL/file repo |
| 3 | SQL таблицы | ✅ CREATE в `StorageSQL` / `SQLMigrationManager` |
| 4 | File JSON storage | ✅ |
| 5 | `MetricsCollector` | ✅ |
| 6 | Calculator / Adjuster в профиле | ✅ через `AdaptiveDifficulty` |
| 7 | `AdaptiveMode`, `StatsService` | 🔴 отдельный режим не нужен пока |
| 8 | `config.yml` → `adaptive.*` | ✅ |
---
## Оценка времени
## Фаза 3: ElytraMode (статус реальный)
| Фаза | Задач | Дней | Начало | Конец |
|------|-------|------|--------|-------|
| Фаза 1: Рефакторинг | 10 | 2-3 | День 1 | День 3 |
| Фаза 2: Адаптивность | 20 | 3-4 | День 4 | День 7 |
| Фаза 3: ElytraMode | 9 | 1-2 | День 8 | День 9 |
| Фаза 4: Новые режимы | 8 | 2-3 | День 10 | День 12 |
| **Итого** | **47** | **8-12** | - | - |
| # | Задача | Статус |
|---|--------|--------|
| 1 | `ElytraMode` | 🟡 Есть |
| 2 | `ElytraGenerator` + helpers | ✅ Разбит на `mode/elytra/*` |
| 3 | `ElytraConfig`, `RingRenderer`, … | ✅ В пакете `mode/elytra/` |
| 4 | `modes/elytra.yml` | 🔴 Нет |
| 5 | i18n для elytra | ✅ |
---
## Метрики успеха
## Фаза 4: Режимы (статус реальный)
### Технические
- ✅ Все файлы < 200 строк
- ✅ Максимум 6 файлов в папке
- ✅ SOLID принципы соблюдены
- ✅ Нет циклических зависимостей
### Функциональные
- ✅ Адаптивная сложность работает для 4+ режимов
- ✅ Метрики сохраняются между сессиями
- ✅ ElytraMode полностью функционален
- ✅ Новый режим (Roguelike) добавлен
### Производительность
- ✅ TPS не падает при 50+ игроках
- ✅ SQL запросы < 50ms
- ✅ Конфиги кэшируются (нет I/O при входе игрока)
| Режим | Меню | i18n join disabled | Примечание |
|-------|------|-------------------|------------|
| default, speedrun, hardcore, gravity-shift | ✅ SingleMenu | 🔴 хардкод EN | |
| elytra, race | ✅ | 🔴 | race — ещё UI хардкод |
| coop | ❌ (null item — ок) | 🔴 | title хардкод |
| spectator | ✅ | 🟡 частично Locales | |
| roguelike | — | — | **Нет в коде** (в старом TODO было ✅ — ошибка) |
---
## SQL миграция
## Фаза 5: i18n — единая система сообщений
| # | Задача | Статус |
|---|--------|--------|
| 1 | `other.back` / `other.next` | ✅ |
| 2 | Ключи из таблицы I* | ✅ |
| 3 | `MessageKeys` enum или constants class (DRY путей) | 🟢 |
| 4 | `Locales.getString(CommandSender)` — использовать везде для консоли | 🟡 Частично |
| 5 | Убрать debug-spam STONE | ✅ |
| 6 | Синхронизировать `en.yml` ↔ `ru.yml` | 🟡 периодический diff |
| 7 | Проверка CI: скрипт diff ключей locale vs `Locales.getString`/`sendTranslated` | 🟢 |
---
## Технический долг (актуальный)
| # | Задача | Приоритет |
|---|--------|-----------|
| 1 | Compile adaptive | ✅ |
| 2 | Bootstrap MetricsCollector | ✅ |
| 3 | SQL adaptive table | ✅ |
| 4 | Удалить или реализовать `Menu`/`PagedMenu`/`DynamicMenu` | 🟡 |
| 5 | Реальные тесты adaptive + modes | 🟢 |
---
## Чек-лист перед релизом (честный)
- [x] `./gradlew build` без ошибок
- [x] Нет хардкода RU в `LPMenu` / основных режимах и админке
- [ ] Полный diff ключей locale vs Java (CI)
- [ ] Адаптивность: ручной тест на сервере (`adaptive.enabled: true`)
- [ ] ElytraMode протестирован вручную
- [ ] Нет файлов &gt; 200 строк (или исключения задокументированы)
- [ ] README / CHANGELOG соответствуют коду
---
## SQL миграция (без изменений)
```sql
-- Создание таблицы для адаптивных метрик
CREATE TABLE IF NOT EXISTS loparkour_player_stats (
player_uuid VARCHAR(36) PRIMARY KEY,
skill_rating DOUBLE DEFAULT 1.0,
@ -260,11 +289,36 @@ CREATE TABLE IF NOT EXISTS loparkour_player_stats (
INDEX idx_skill_rating (skill_rating),
INDEX idx_last_updated (last_updated)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Миграция существующих данных (если есть старая таблица)
INSERT INTO loparkour_player_stats (player_uuid, sessions_count, last_updated)
SELECT player_uuid, COUNT(*) as sessions, MAX(timestamp) as last_updated
FROM loparkour_scores
GROUP BY player_uuid
ON DUPLICATE KEY UPDATE sessions_count = VALUES(sessions_count);
```
---
## Оценка времени (пересмотр)
| Фаза | Дней | Комментарий |
|------|------|-------------|
| **0: KISS/DRY/SOLID + лимит строк** | 5–8 | Блокирует нормальную поддержку |
| **5: i18n** | 2–3 | Можно параллельно с 0 |
| **2: Adaptive** | 3–4 | После зелёной сборки + bootstrap |
| **3: Elytra split** | 1–2 | После 0 |
| **4: Режимы / тесты** | 2–3 | |
| **Итого** | **13–20** | Старые «8–12 дней» занижены |
---
## Метрики успеха (пересмотр)
### Технические
- [ ] ≥ 90% файлов ≤ 150 строк
- [ ] 0 файлов &gt; 200 строк
- [ ] SOLID: `adaptive` → listener/port, не прямой import generator internals
- [ ] `./gradlew test` — осмысленное покрытие, не «галочка в TODO»
### Функциональные
- [ ] 2+ языка без хардкода в Java (кроме логов debug)
- [ ] Adaptive влияет на профиль после N сессий
- [ ] ElytraMode без монолита 250+ строк
### Производительность
- [ ] TPS стабилен 50+ игроков (профилирование)
- [ ] SQL batch / flush adaptive &lt; 50ms