- Remove HardcoreMode (lose all rewards on fall) - Disable Elytra mode in default config - Add todo.md with 20 identified bugs - Fix 3 remaining bugs: checkPlayerFall continue, removeTrailBlocks sync, scoredBlocks clear on fall
109 lines
8.9 KiB
Markdown
109 lines
8.9 KiB
Markdown
# LoParkour Bug Tracker
|
||
|
||
## CRITICAL
|
||
|
||
### 1. ProfileValue.asInt/asDouble — NumberFormatException при пустой строке
|
||
- **Файл:** `Profile.java:57-58`
|
||
- **Описание:** `Integer.parseInt(value)` и `Double.parseDouble(value)` падают при пустой строке `""`, которая возвращается по умолчанию при отсутствии ключа (`new ProfileValue("")`)
|
||
- **Фикс:** Добавить дефолтное значение либо кидать понятное исключение, а не NFE
|
||
|
||
### 2. Bukkit.createBlockData — IllegalArgumentException при невалидном materialName
|
||
- **Файл:** `GeneratorProfileManager.java:133`
|
||
- **Описание:** Если materialName из конфига невалидный (опечатка в generation.yml), `Bukkit.createBlockData()` кинет исключение
|
||
- **Фикс:** Валидировать materialName перед вызовом, либо ловить конкретно на строке 133
|
||
|
||
### 3. JumpDirector диагональный дрейф — heading никогда не выпрямляется
|
||
- **Файл:** `JumpDirector.java:87-99`
|
||
- **Описание:** `getRecommendedHeading()` всегда создает `Vector(1,0,1)` для X и Z границ. После нормализации heading становится диагональным и НИКОГДА не возвращается к осевому направлению
|
||
- **Фикс:** Использовать `Vector(1,0,0)` для X и `Vector(0,0,1)` для Z. Сбрасывать heading на исходный при отсутствии границы
|
||
|
||
### 4. subList().clear() на synchronizedList(LinkedList) — повреждение node связей
|
||
- **Файл:** `GeneratorCleanup.java:50-56`
|
||
- **Описание:** `history.subList(0, removeCount).clear()` на `Collections.synchronizedList(new LinkedList<>())` — compound операция не атомарна. ConcurrentModificationException или повреждение связей
|
||
- **Фикс:** Оборачивать в `synchronized(history)` или использовать CopyOnWriteArrayList
|
||
|
||
### 5. Мультиплеер — runaway генерация блоков
|
||
- **Файл:** `PlayerInteractionHandler.java:116-118` + `LifecycleTickManager.java:89-97`
|
||
- **Описание:** Каждый игрок триггерит `generate(1)` при скоре. N игроков = N блоков/тик. maintainBlockLead добавляет еще. Положительная обратная связь
|
||
- **Фикс:** Не больше 1 блока за тик независимо от числа игроков, кэп на maintainBlockLead
|
||
|
||
### 6. Система схематик полностью сломана
|
||
- **Файл:** `PlayerInteractionHandler.java:111-113` + `BlockPlacer.java:207-212`
|
||
- **Описание:** `isSchematicEndBlock` возвращает true для ЛЮБОГО schematic блока. При входе в схематик сразу удаляется + блоки засчитываются как очки
|
||
- **Фикс:** `isSchematicEndBlock` должен проверять только ПОСЛЕДНИЙ блок схематика. Исключить schematic блоки из скоринга
|
||
|
||
### 7. Прыжки за границами валидации
|
||
- **Файл:** `JumpCalculator.java:101-114`
|
||
- **Описание:** JumpOffsetGenerator генерирует offset до 5 блоков, total horizontal = sqrt(4²+5²)=6.4 при лимите 4.1. Retry-цикл не гарантирует валидность за 10 попыток
|
||
- **Фикс:** Ограничить randomOffset пределами валидатора: `maxRandomOffset = sqrt(maxHorizontal² - distance²)`
|
||
|
||
## HIGH
|
||
|
||
### 8. angleInY вращает offset относительно глобального Option.HEADING
|
||
- **Файл:** `JumpCalculator.java:105-113`
|
||
- **Описание:** rotateAroundY использует разницу между текущим heading и Option.HEADING, а не между текущим и предыдущим heading. Блоки уходят вбок
|
||
- **Фикс:** Вращать offset относительно предыдущего heading
|
||
|
||
### 9. Дублирование canJump(Location) и canJump(Vector) — рассинхронизация
|
||
- **Файл:** `JumpValidator.java:73-161`
|
||
- **Описание:** Два метода canJump. Location-версия учитывает slab-корректировку, Vector — нет
|
||
- **Фикс:** Убрать дублирование или добавить adjustment в Vector-версию
|
||
|
||
### 10. synchronizedList — итерации без синхронизации
|
||
- **Файл:** `GeneratorState.java:35`
|
||
- **Описание:** Итерации по `history` (cleanupDistantBlocks, historyContains, indexOfBlock) не обернуты в `synchronized`. Пока все на main thread, но это хрупко
|
||
- **Фикс:** Добавить `synchronized(history)` на все итерации, либо убрать synchronizedList
|
||
|
||
### 11. heading.clone() без null-проверки
|
||
- **Файл:** `JumpCalculator.java:101-103`
|
||
- **Описание:** `generator.state.heading.clone()` упадет с NPE, если heading не инициализирован до первого вызова calculateJumpOffset
|
||
- **Фикс:** Добавить null-guard: если heading == null, инициализировать из Option.HEADING
|
||
|
||
## MEDIUM
|
||
|
||
### 12. BlockSelector.selectBlockData рекурсия при пустом списке стилей
|
||
- **Файл:** `BlockSelector.java:32-33`
|
||
- **Описание:** Если style == null, код сбрасывает стиль на первый. `orElseThrow()` упадет, если список пуст
|
||
- **Фикс:** Проверка `if (styles.isEmpty()) return Material.STONE.createBlockData()`
|
||
|
||
### 13. checkPlayerFall — break вместо continue
|
||
- **Файл:** `LifecycleTickManager.java:74-78`
|
||
- **Описание:** При падении двух игроков в одном тике, второй пропускает обработку до следующего тика
|
||
- **Фикс:** Заменить break на continue
|
||
|
||
### 14. removeTrailBlocks сдвигает индексы истории
|
||
- **Файл:** `GeneratorCleanup.java:79-93`
|
||
- **Описание:** `history.subList(startIndex, endIndex).clear()` сдвигает индексы при параллельном вызове historyContains
|
||
- **Фикс:** Защититься копированием или проверять синхронизацию
|
||
|
||
### 15. Дублирование isSlabMaterial
|
||
- **Файл:** `JumpValidator.java:67-71`
|
||
- **Описание:** JumpValidator и BlockSelector имеют дублирующуюся логику определения slab. Рассинхронизация при изменениях
|
||
- **Фикс:** Вынести в общий утилитарный метод
|
||
|
||
### 16. DifficultyScore thresholds для льда (1.0/2.0) могут быть недостижимы
|
||
- **Файл:** `GeneratorProfileManager.java:57-69`
|
||
- **Описание:** packed_ice добавляется при difficulty >= 1.0, blue_ice при >= 2.0. Многие конфиги не достигают этих значений. Лед не появляется
|
||
- **Фикс:** Нормализовать difficulty до предсказуемой шкалы или изменить thresholds
|
||
|
||
### 17. schematicBlocks — ArrayList без thread safety
|
||
- **Файл:** `GeneratorState.java:41`
|
||
- **Описание:** schematicBlocks это plain ArrayList, без синхронизации, в отличие от history
|
||
- **Фикс:** Добавить синхронизацию или документировать main-thread-only
|
||
|
||
## LOW
|
||
|
||
### 18. scoredBlocks HashSet не очищается при fall
|
||
- **Файл:** `ParkourPlayer.java:125-127`
|
||
- **Описание:** HashSet растет бесконечно. При возрождении на тех же координатах не засчитает блоки
|
||
- **Фикс:** scoredBlocks.clear() при fall/reset
|
||
|
||
### 19. getFormattedTime() — Date(elapsed) хак
|
||
- **Файл:** `GeneratorStatistics.java:90-91`
|
||
- **Описание:** `new Date(elapsed)` — Date ожидает timestamp от 1970, а передается длительность. Сломается при >24ч
|
||
- **Фикс:** Использовать Duration API для форматирования
|
||
|
||
### 20. isUnsafeParkourMaterial блокирует PUMPKIN и _BED без причины
|
||
- **Файл:** `BlockSelector.java:48-60`
|
||
- **Описание:** PUMPKIN не имеет onPlace-сканирования соседей. _BED безопасны при setBlockData с флагом false
|
||
- **Фикс:** Убрать лишние исключения
|