refactor(config): encapsulate option sections, drop the Option mirror
Option was a facade of 45 public static fields kept in sync with the four sections by syncFields(); the sections themselves also exposed public static mutable fields. 88 VisibilityModifier violations in one place and two owners for every value. - Option keeps only init(boolean) (the reload entry ConfigLoader calls); the mirrors, syncFields() and the unused @Deprecated initStyles are gone - sections hold private static state behind fluent accessors (OptionSQL.enabled(), OptionGeneration.cleanupInterval(), OptionGeneral.goBackLocation()); the two Option.SQL = false writes become OptionSQL.disable() - collection/location accessors hand out unmodifiable views and a Location copy, so no caller can mutate loaded config state - ParticleShape promoted from nested enum to config/options/ParticleShape - delete 17 never-read generation probability fields and initGenerationProbabilities(): live weights come from advanced.* via GeneratorProfileManager/GenerationProfileLoader; min-y/max-y guard moved 1:1 into initBounds() - rewrite 63 Option.<FIELD> reads in 19 files to the section accessors - validateConfigs() now checks the paths the code actually consumes (advanced.distance/height/type weights) instead of the dead generation.normal.* - ParkourRestrictionListener.onChat: getOrDefault(CHAT, true) instead of unboxing a Boolean that may be absent checkstyle 0 errors / 329 -> 240 warnings (VisibilityModifier 163 -> 75); config/options itself is clean. Dead config keys found on the way are filed as TODO items 164-166. ./gradlew check green: PMD 0, 94 tests, NullAway unchanged.
This commit is contained in:
parent
4263df50a2
commit
c120309663
27 changed files with 386 additions and 359 deletions
47
TODO.md
47
TODO.md
|
|
@ -112,10 +112,10 @@
|
|||
|
||||
- **60. `SQLDataMapper` UUID.fromString кидает unchecked IllegalArgumentException** (SQLDataMapper.java:65,31) мимо catch(SQLException) → битые строки роняют чтение. → Ловить и логировать/скипать строку.
|
||||
- **61. `SQLQueryExecutor.prepareStatement`** (без callers) никогда не закрывает connection → утечка пула при будущем использовании. → try-with-resources / borrow-tracking.
|
||||
- **62. `SQLMigrationManager` мёртв; `migration_adaptive.sql` ссылается на несуществующую `loparkour_scores` и нигде не исполняется**; та же DDL в 3 копиях; префикс `SQL_PREFIX` применён непоследовательно. → Единый мигратор с версионированием; починить/удалить SQL-файл.
|
||||
- **62. `SQLMigrationManager` мёртв; `migration_adaptive.sql` ссылается на несуществующую `loparkour_scores` и нигде не исполняется**; та же DDL в 3 копиях; префикс `OptionSQL.prefix()` применён непоследовательно. → Единый мигратор с версионированием; починить/удалить SQL-файл.
|
||||
- **63. `writeScores` гоняет всю таблицу туда-сюда** без дельт (StorageSQL.java:105-128). → Инкрементальные upsert'ы только изменённых записей.
|
||||
- **64. `Score.fromString` — CSV по запятой: ник с `,` ломает все поля** (Score.java:19-31). → Экранирование или JSON.
|
||||
- **65. `validateConnection` мутирует `Option.SQL=false` при транзиентном сбое** (SQLConnectionManager.java:81-86) — SQL отключается навсегда. → Отдельный флаг/ретрай.
|
||||
- **65. `validateConnection` мутирует `OptionSQL.disable()` при транзиентном сбое** (SQLConnectionManager.java:87-92) — SQL отключается навсегда. → Отдельный флаг/ретрай.
|
||||
|
||||
### Adaptive
|
||||
|
||||
|
|
@ -126,11 +126,14 @@
|
|||
|
||||
### Config / reload
|
||||
|
||||
- **70. `/lp reload` не атомарен**: дублирует `Registry.STYLES` (никогда не чистится), оставляет старые `Rewards` при `enabled:false`, `BORDER_SIZE` устаревший, миры/генераторы не обновляются (ConfigUpdater, OptionStyles.java:23-43, Rewards.java:41-52, OptionGeneral.java:38-40). → Полный reset registry + пересоздание зависимых подсистем.
|
||||
- **70. `/lp reload` не атомарен**: дублирует `Registry.STYLES` (никогда не чистится), оставляет старые `Rewards` при `enabled:false`, `OptionGeneral.borderSize()` устаревший (перечитывается только при `firstLoad`), миры/генераторы не обновляются (ConfigUpdater, OptionStyles.java:23-43, Rewards.java:41-52, OptionGeneral.java:44-50). → Полный reset registry + пересоздание зависимых подсистем.
|
||||
- **71. `LocaleLoader` при ошибке подменяет кэш на emptyMap** → все сообщения пустые (LocaleCache.java:22-25). → Сохранять старый кэш при сбое.
|
||||
- **72. Нет fallback на en** для пропущенных ключей/неизвестных локалей (Locales.java:69-71). → Fallback-цепочка locale → en.
|
||||
- **73. `Rewards.init()` при `enabled:false` не чистит статические мапы** → старые награды продолжают выдаваться после reload. → Очистка.
|
||||
- **74. `ConfigAccessor` типо-опечатка тихо даёт false/0** (ConfigAccessor.java:45-48). → Строгая валидация ключей при старте.
|
||||
- **164. Секция `generation.*` в generation.yml игнорируется.** Живые веса берутся из `advanced.distance|height|type|special` (`GeneratorProfileManager`, `GenerationProfileLoader`), а `generation.type|normal|special` читал только мёртвый `OptionGeneration.initGenerationProbabilities()` — удалён на шаге инкапсуляции `config/options`. В shipped `generation.yml` обе секции дублируют друг друга (значения разные: 85/10/5 против 80/15/5) → пользователь правит то, что не работает. → Либо удалить `generation.type|normal|special` из generation.yml, либо сделать их источником для профилей; `generation.settings.min-y/max-y` остаются живыми. `ConfigLoader.validateConfigs` уже проверяет живые пути `advanced.*`.
|
||||
- **165. Мёртвый конфиг частиц/звуков.** `particles.sound-type|sound-pitch|sound-volume|particle-type|particle-shape` читаются в `OptionParticles`, все 6 геттеров (`shape()`, `soundType()`, `particleData()`…) не вызываются нигде → настройки не влияют на игру (используются per-player `ParkourPlayer.particles/sound` из меню). → Подключить в рендере частиц/звуков (`util/particle/`, `HookSound`) или убрать секцию из config.yml.
|
||||
- **166. Ещё два читаемых, но не потребляемых ключа.** `options.leads.amount` → `OptionGeneral.possibleLeads()` (валидация 1–128 и лог работают, список не читает никто — lead берётся из `default-values.leads.default`), `memory.block-cleanup-distance` → `OptionGeneration.blockCleanupDistance()` (дистанция чистки захардкожена в `GeneratorCleanup`). → Подключить или удалить.
|
||||
|
||||
### Session
|
||||
|
||||
|
|
@ -169,7 +172,7 @@
|
|||
- **95. `SchematicIdGenerator` хеширует только byte-координаты → коллизии id** (SchematicIdGenerator.java:23-28).
|
||||
- **96. `parseDifficulty` принимает NaN/Infinity** (SchematicCreator.java:64-70) → схема навсегда невыбираема.
|
||||
- **97. `stem()` регистрирует файл `.nbt` под пустым id**; `.NBT` игнорируется, `.schematic` — case-insensitive (SchematicManager.java:147-150,88).
|
||||
- **98. `Locales.getPlayerLocale`/`ParkourUser.locale` deref `OPTIONS_DEFAULTS.get(LANG)` без null-check** (Locales.java:170-184, ParkourUser.java:64).
|
||||
- **98. `Locales.getPlayerLocale`/`ParkourUser.locale` deref `OptionGeneral.optionsDefaults().get(LANG)` без null-check** (Locales.java:170-184, ParkourUser.java:64).
|
||||
- **99. Магические числа**: fall-threshold -30 (LifecycleTickManager.java:72), render 24.0/0.1/%4 (InvisibleBarrierMode.java:67-68,98), 10/30 (GravityShiftMode.java:82,108), 100 тиков (RaceMode.java:147), hotbar delay 5 (ParkourPlayer.java:192), `TICKS_PER_SECOND=20` переопределён (SpeedrunMode.java:82). → В конфиг/константы.
|
||||
- **100. Deprecated API**: `Material.STONE_SLAB` (MaterialUtil.java:20), `registerNewObjective(String,String,String)` (ScoreboardManager.java:75), `getOfflinePlayer(name)` (AdminCommandHandler.java:186-199), legacy ACTION_BAR (ParkourSpectator.java:73, RaceMode.java:103).
|
||||
- **101. `ParkourSpectator.java:96` — бессмысленный double-set spectator target** (no-op).
|
||||
|
|
@ -427,14 +430,38 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар
|
|||
> Изменено 45 файлов (TODO.md, 2 конфига линтеров, 46 исходников).
|
||||
>
|
||||
> **Осталось (порядок):**
|
||||
> 1. Тест-гейт «**в папке ≤ 4 файлов**» (JUnit, без новых зависимостей) + разбивка 6 переполненных пакетов:
|
||||
> `elytra/generator` 7, `elytra/mode` 6, `command/schematic` 6, `player/core` 5, `generator/jump/placement` 5, `generator/jump/calculation` 5 —
|
||||
> 10 файлов переносятся в подпапки (механически: package + импорты).
|
||||
> 2. Пакет за пакетом обнулять бэклог, начиная с `config/options` (91 нарушение в 2 файлах: `Option` — 45 public-полей,
|
||||
> `OptionGeneration` — 25; это и есть п.77/107 про public mutable state) → после обнуления перевести правило в error.
|
||||
> 1. ✅ Тест-гейт «**в папке ≤ 4 файлов**» (JUnit, без новых зависимостей) + разбивка 6 переполненных пакетов —
|
||||
> сделано в `4263df5`, `FOLDER_DEBT = Set.of()`, ни одна папка `src/main/java` не содержит >4 файлов.
|
||||
> 2. ✅ Бэклог `config/options` обнулён (см. ПРОГРЕСС-5 ниже) → **`VisibilityModifier` ещё НЕ в error**: в пакете остались
|
||||
> только 2 `IllegalCatch` (общий бэклог п.3), а правило висит на 75 нарушениях в других пакетах.
|
||||
> 3. `IllegalCatch` 23: 3 сайта намеренные (команда/конфиг с откатом/скетчи — boundary-catch), остальные 20 — выловить конкретные типы;
|
||||
> **нужно решение владельца** по трём.
|
||||
> 4. Дальше по плану (a): PMD-категории целиком, CPD, SpotBugs, ArchUnit-слои, JaCoCo, Spotless, quality-job в CI.
|
||||
> 4. Дальше по плану (a): PMD-категории целиком, CPD, SpotBugs, ArchUnit-слои, JaCoCo, Spotless, quality-job в CI — ✅ сделано в `b771cc1`.
|
||||
>
|
||||
> ## ✅ ПРОГРЕСС-5 (Задача 2, шаг (c): `config/options` инкапсулирован, 2026-10-06)
|
||||
> Был анти-паттерн: `Option` — фасад-зеркало на **45 `public static` полей**, которые `syncFields()` копировал
|
||||
> из 4 секций (`OptionSQL`, `OptionParticles`, `OptionGeneration`, `OptionGeneral`), и в самих секциях поля тоже
|
||||
> были `public static` (мутабельное состояние чужих пакетов = п.77/107 в миниатюре).
|
||||
> - **`Option` сокращён до `init(boolean)`** — единственная точка перезагрузки конфигов (её зовёт `ConfigLoader`);
|
||||
> зеркало, `syncFields()` и мёртвый `@Deprecated initStyles` (0 вызовов) удалены. Класс больше не дублирует секции.
|
||||
> - **Секции: `private static` поля + fluent-аксессоры** (`OptionSQL.enabled()`, `OptionGeneration.cleanupInterval()`,
|
||||
> `OptionGeneral.goBackLocation()`…), без префикса `get` — как у соседних статик-холдеров (`Materials.parse`, `Probs.random`).
|
||||
> Две точки записи `Option.SQL = false` → `OptionSQL.disable()`.
|
||||
> - **Защита от протечки состояния:** `possibleLeads()/optionsEnabled()/optionsDefaults()` отдают unmodifiable-представления
|
||||
> (null сохраняется как раньше), `goBackLocation()` — копию (`Location` мутируется при reload через `setPitch/setYaw`).
|
||||
> - `ParticleShape` вынесен из вложенного enum'а в `config/options/ParticleShape.java` (в `section/` леж бы 5-й файл — гейт «≤4»).
|
||||
> - **Удалены мёртвые данные:** 17 полей вероятностей генерации + `initGenerationProbabilities()` — не читались нигде,
|
||||
> живые веса берутся из `advanced.*` (`GeneratorProfileManager`/`GenerationProfileLoader`). Границы `min-y/max-y`
|
||||
> с проверкой `minY >= maxY` перенесены 1-в-1 в `initBounds()`. Найденное оформлено пунктами **164–166**.
|
||||
> - **63 обращения `Option.<ПОЛЕ>`** (не 127 — счёт предыдущей сессии был завышен из-за подстроки в `ParkourOption.*`)
|
||||
> переписаны в **19 файлах**; `Option`-импорт остался только в `ConfigLoader`.
|
||||
> - Попутно (предложения `CodeReview`): `ConfigLoader.validateConfigs()` проверяет **живые** пути
|
||||
> `advanced.distance.chances|height.chances|height.min|max|type` (раньше проверялась мёртвая секция `generation.normal.height.*`);
|
||||
> в `ParkourRestrictionListener.onChat` unboxing `Boolean` → `getOrDefault(CHAT, true)` как в `ParkourOption.mayPerform`.
|
||||
> - **Счётчики:** checkstyle **0 error / 329 → 240 warning**; `VisibilityModifier` **163 → 75**, `MethodLength` 5 → 4.
|
||||
> В `config/options` — **0 нарушений VisibilityModifier** (было 88), остаются 2 `IllegalCatch`.
|
||||
> `./gradlew check` зелёный: PMD 0, тесты 94/0 fail, JaCoCo/ArchUnit/Spotless ок, NullAway 48 (как в baseline).
|
||||
> Пороги правил не снижались, `suppressions.xml` пустая. Реальных прогонов на сервере после правок не было (только `check`).
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue