fix(elytra,chat): make elytra sub-modes report their mode and wire the chat-scope toggle

- ElytraGenerator no longer hardcodes Modes.elytra(): the mode is passed
  through the constructor (canonical (session, pointType, mode) plus two
  convenience overloads); CloseGenerator/MinSpeed/Obstacle/SpeedDemon/
  TimeTrial now declare their own mode, so getMode() is reachable
- Session.ChatType gets next(); ParkourUser gets setChatType(); SettingsMenu
  gains a chat-scope cycle button (slot 15) so LOBBY_ONLY/PLAYERS_ONLY are
  no longer dead branches
- ChatTypeTest covers the cycle order and wrap-around
- TODO.md: PROGRESS-8 records both fixes

checkstyle 0 error / 135 warning, PMD 0, tests 96/0 fail (1 skipped)
This commit is contained in:
loki5512344 2026-10-06 19:57:00 +02:00
parent 02e2947f47
commit 8ff02cff18
Signed by: boba
GPG key ID: 253067914055423B
11 changed files with 122 additions and 17 deletions

38
TODO.md
View file

@ -86,14 +86,22 @@
- **47. `UserRegistry.getUsers()` строит полный Set на каждый BlockBreak/Place/Drop/Damage** (UserRegistry.java:94-98) → O(n) аллокаций на горячем пути. → Кэш `Map<UUID, ParkourUser>` + привязка через PersistentDataContainer или entity.
- **48. `sendTranslated(...).formatted()` бросает IllegalFormatException при `%` в нике/сообщении** (ParkourUser.java:102, Locales.java:76-82). → Обернуть в try или использовать replace-based формат.
- **49. `getJoinCount` — int между main и async (bStats)** (UserRegistry.java:100-104). → AtomicInteger.
- **168. `ParkourUser.chatType` — состояние без записи.** Поле инициализировано `PUBLIC`, читается в `ParkourRestrictionListener.onChat` (switch `chatType()`), но сеттера нет нигде → ветки PRIVATE/GUILD недостижимы, режим группового чата не работает. → Либо подключить переключатель (команда/меню), либо убрать switch и мёртвый enum.
- **168. ✅ ИСПРАВЛЕНО (2026-10-06, см. ПРОГРЕСС-8): `ParkourUser.chatType` — состояние без записи.** Поле инициализировано
`PUBLIC`, читалось в `ParkourRestrictionListener.onChat` (switch `chatType()`), но сеттера не было нигде → ветки
`LOBBY_ONLY`/`PLAYERS_ONLY` были недостижимы. Оказалось, фича спроектирована, но не подключена: в `en.yml`/`ru.yml`
уже лежал готовый блок `settings.chat` (material/name/lore/values + formats), а `ParkourOption.CHAT("settings.chat", …)`
уже был в реестре опций. → Кнопка-цикл в `SettingsMenu` (слот 15) + `ParkourUser.setChatType(...)` + `ChatType.next()`.
### Modes
- **50. `GravityShiftMode` не снимает эффекты при выходе игрока** (только в reset для присутствующих, GravityShiftMode.java:149-163). → Снимать в unregister.
- **51. `RaceMode.finishRace` — `reset(false)` оставляет игрока в воздухе** (RaceMode.java:142-147; GeneratorCleanup.reset чистит курс без регенерации/телепорта). → Телепорт в лобби + регенерация старта.
- **52. `SpeedrunMode` оставляет удалённые блоки в `history`** → ломает координатные поиски. → Удалять координату из history при снятии блока.
- **167. Elytra-под-режимы не отличаются от базового.** `ElytraGenerator.getMode()` возвращает `Modes.elytra()`, а 5 под-генераторов (`Close/MinSpeed/TimeTrial/SpeedDemon/ObstacleGenerator`) его не переопределяют → guard `getMode() instanceof CloseMode` в `create()` (строка 47 во всех 5 режимах) **никогда не истинен**: повторная команда в том же режиме не делает early-return, а пересоздаёт сессию. Геттеры `Modes.speedDemon()/minSpeed()/timeTrial()/close()/obstacle()` не вызываются нигде (5 статиков только пишутся). → Переопределить `getMode()` в под-генераторах (или сравнивать класс генератора) и использовать/удалить геттеры.
- **167. ✅ ИСПРАВЛЕНО (2026-10-06, см. ПРОГРЕСС-8): Elytra-под-режимы не отличались от базового.**
`ElytraGenerator.getMode()` возвращал `Modes.elytra()`, а 5 под-генераторов (`Close/MinSpeed/TimeTrial/SpeedDemon/ObstacleGenerator`)
его не переопределяли → guard `getMode() instanceof CloseMode` в `create()` **никогда не был истинен**: повторная команда
в том же режиме не делала early-return, а пересоздавала сессию. Геттеры `Modes.speedDemon()/minSpeed()/timeTrial()/close()/obstacle()`
не вызывались нигде. → режим передаётся в конструктор `ElytraGenerator`, `getMode()` отдаёт его.
### Схематики (жизнь в игре)
@ -549,6 +557,32 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар
> Урок на будущее: после любой отменённой пачки правок сверять `git status` + полный `check`, а не только счётчики.
> - **Остаток бэклога (135):** `ReturnCount` 69, `IllegalCatch` 23, `ClassFanOutComplexity` 17, `NPathComplexity` 12,
> `ParameterNumber` 8, `MethodLength` 4, `ClassDataAbstractionCoupling` 2.
>
> ## ✅ ПРОГРЕСС-8 (Задача 2: починены реальные баги 167 и 168, 2026-10-06)
> По решению владельца вместо механической зачистки варнингов взяты два мёртвых куска логики.
> - **167 — elytra-под-режимы научились представляться.** `ElytraGenerator` больше не возвращает хардкод
> `Modes.elytra()`: режим передаётся в конструктор (`private final Mode mode`, канонический конструктор
> `(session, pointType, mode)` + two convenience-перегрузки), `getMode()` отдаёт его. Под-генераторы
> объявляют свою идентичность одной строкой `super(session, Modes.speedDemon())` — вместо пяти копий `getMode()`
> (у `ObstacleGenerator` не хватило бы строк: лимит FileLength = 200, а файл — 199).
> Попутно удалено дублирование: `ObstacleGenerator` заново читал `elytra.radius` и держал свой `DEFAULT_RADIUS = 3` —
> теперь общий `radius()` из родителя (199 → 194 строки).
> - **Что реально поменялось для игрока (следствие, а не побочка):**
> 1) guard в `create()` стал достижим → повторная `/parkour speeddemon` в том же режиме больше не пересоздаёт сессию;
> 2) обратная сторона — `/parkour elytra` внутри под-режима раньше **не работал** (под-генератор выдавал себя за
> `ElytraMode`, `instanceof ElytraMode` был истинен и делал early-return), теперь переключает;
> 3) очки, rewards (`%mode:`-префикс в `rewards-v2.yml`), голограммы и PAPI теперь попадают в **свой** лидерборд
> под-режима, а не в общий `elytra`. ⚠️ Старые записи в `leaderboards/elytra.json`/SQL не теряются, но
> пер-режимные доски начинаются с нуля; реальных прогонов на сервере не было (только `check`).
> - **168 — переключатель области чата подключён к меню.** Фича была спроектирована, но не собрана: блоки
> `settings.chat` (material/name/lore/values + formats) есть и в `en.yml`, и в `ru.yml`, `ParkourOption.CHAT` зарегистрирован,
> `ParkourRestrictionListener.onChat` уже умеет маршрутизировать `LOBBY_ONLY`/`PLAYERS_ONLY`, но кнопка не
> создавалась, сеттера не было, и `chatType` навечно оставался `PUBLIC`.
> Сделано: `Session.ChatType.next()` (цикл по порядку объявления = порядку `settings.chat.values`),
> `ParkourUser.setChatType(...)`, кнопка в `SettingsMenu` (слот 15, gate `ParkourOption.CHAT.mayPerform`) по образцу
> `LobbyMenu.visibilityItem` — клик меняет область и перерисовывает меню. Новатье — `ChatTypeTest` (2 теста).
> - **Счётчики:** checkstyle **0 error / 135 warning** (тот же бэклог, что в ПРОГРЕСС-7 — баги чинились без
> новых варнингов), PMD **0**, тесты **96 / 0 fail (1 skipped)**, `./gradlew check` зелёный.
---