Restructure core modules into subpackages (generator, schematic, config, session, etc.); add .schem/.nbt schematic pipeline, commands, adaptive stats skeleton, and bootstrap wiring.
324 lines
15 KiB
Markdown
324 lines
15 KiB
Markdown
# LoParkour — TODO (обновлено 2026-06-01)
|
||
|
||
> Roadmap + **честный аудит** репозитория. Старые галочки «✅» в фазах 1–4 не соответствуют коду — см. раздел **Аудит**.
|
||
|
||
## Легенда
|
||
|
||
- 🔴 Критично — блокирует сборку / релиз / основной UX
|
||
- 🟡 Важно — заявленная функциональность или i18n
|
||
- 🟢 Планово — улучшение, рефакторинг
|
||
- ✅ Готово (проверено в коде)
|
||
|
||
---
|
||
|
||
## Аудит 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** | Ранее помечено ✅ то, что не сделано — статусы ниже исправлены |
|
||
|
||
**Статистика:** ~160 `.java` файлов, средний размер ~85 строк, максимум **252** (`ElytraGenerator.java`).
|
||
|
||
---
|
||
|
||
## Фаза 0: Полный рефакторинг (KISS · DRY · SOLID) — приоритет
|
||
|
||
> Цель: предсказуемая структура, тонкие классы, без «божественных» файлов.
|
||
|
||
### Правила (жёсткие)
|
||
|
||
| Правило | Значение |
|
||
|--------|----------|
|
||
| **Целевой размер** | ~**150** строк на файл |
|
||
| **Жёсткий потолок** | **190–200** строк (исключения только с комментом в PR) |
|
||
| **Файлов в папке** | ≤ **6** (при росте — новая подпапка) |
|
||
| **KISS** | Один публичный сценарий = один класс / use-case |
|
||
| **DRY** | Сообщения только через `Locales`; команды — `Messages` / `admin.*` |
|
||
| **SOLID** | `generator` не импортирует `adaptive`; связь только через `GeneratorEventListener` + DI в bootstrap |
|
||
|
||
### Целевая структура пакетов (✅ 2026-06-01)
|
||
|
||
```
|
||
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/ # 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`).
|
||
|
||
### Очередь разбиения (файлы > 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 |
|
||
|
||
### Чек-лист фазы 0
|
||
|
||
- [ ] Нет файлов > 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 не врут про «всё готово»
|
||
|
||
---
|
||
|
||
## Баги и долг (найдено при аудите)
|
||
|
||
### 🔴 Сборка
|
||
|
||
| # | Проблема | Статус |
|
||
|---|----------|--------|
|
||
| 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 | Разбить `ParkourGenerator` | 🟡 Частично (composition), файл 159 строк — ок |
|
||
| 2 | `GeneratorProfileManager` | 🟡 Есть, без отдельного `ProfileCalculator` |
|
||
| 3 | statistics из generator | 🟡 `GeneratorStatistics` внутри generator, не пакет `statistics/` |
|
||
| 4 | Структура пакетов `generator/core`, `jump`, `lifecycle` | 🟡 Частично |
|
||
| 5 | `GeneratorEventListener` | ✅ Интерфейс + вызовы в `PlayerInteractionHandler` |
|
||
|
||
### Большие файлы (цель < 200) — **не выполнено**
|
||
|
||
| Файл | Строк | Статус |
|
||
|------|-------|--------|
|
||
| ElytraGenerator.java | 101 | ✅ |
|
||
| ParkourPlayer.java | 166 | ✅ |
|
||
| PAPIHook, LoParkour, MetricsCollector, … | 151–194 | 🟡 см. Фаза 0 |
|
||
|
||
---
|
||
|
||
## Фаза 2: Адаптивная система (статус реальный)
|
||
|
||
| # | Задача | Статус |
|
||
|---|--------|--------|
|
||
| 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 | `ElytraMode` | 🟡 Есть |
|
||
| 2 | `ElytraGenerator` + helpers | ✅ Разбит на `mode/elytra/*` |
|
||
| 3 | `ElytraConfig`, `RingRenderer`, … | ✅ В пакете `mode/elytra/` |
|
||
| 4 | `modes/elytra.yml` | 🔴 Нет |
|
||
| 5 | i18n для elytra | ✅ |
|
||
|
||
---
|
||
|
||
## Фаза 4: Режимы (статус реальный)
|
||
|
||
| Режим | Меню | i18n join disabled | Примечание |
|
||
|-------|------|-------------------|------------|
|
||
| default, speedrun, hardcore, gravity-shift | ✅ SingleMenu | 🔴 хардкод EN | |
|
||
| elytra, race | ✅ | 🔴 | race — ещё UI хардкод |
|
||
| coop | ❌ (null item — ок) | 🔴 | title хардкод |
|
||
| spectator | ✅ | 🟡 частично Locales | |
|
||
| roguelike | — | — | **Нет в коде** (в старом TODO было ✅ — ошибка) |
|
||
|
||
---
|
||
|
||
## Фаза 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 протестирован вручную
|
||
- [ ] Нет файлов > 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,
|
||
sessions_count INT DEFAULT 0,
|
||
total_jumps INT DEFAULT 0,
|
||
total_falls INT DEFAULT 0,
|
||
longest_streak INT DEFAULT 0,
|
||
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_skill_rating (skill_rating),
|
||
INDEX idx_last_updated (last_updated)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
```
|
||
|
||
---
|
||
|
||
## Оценка времени (пересмотр)
|
||
|
||
| Фаза | Дней | Комментарий |
|
||
|------|------|-------------|
|
||
| **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 файлов > 200 строк
|
||
- [ ] SOLID: `adaptive` → listener/port, не прямой import generator internals
|
||
- [ ] `./gradlew test` — осмысленное покрытие, не «галочка в TODO»
|
||
|
||
### Функциональные
|
||
- [ ] 2+ языка без хардкода в Java (кроме логов debug)
|
||
- [ ] Adaptive влияет на профиль после N сессий
|
||
- [ ] ElytraMode без монолита 250+ строк
|
||
|
||
### Производительность
|
||
- [ ] TPS стабилен 50+ игроков (профилирование)
|
||
- [ ] SQL batch / flush adaptive < 50ms
|