refactor(player): encapsulate game state behind PlayerSettings, clear VisibilityModifier and HiddenField
- ParkourUser/ParkourPlayer/GeneratorState public mutable fields -> private + short accessors - per-player settings extracted to player/data/PlayerSettings (ParkourPlayer 188 -> 96 lines, PMD-counted methods 20 -> 9); this is what fixed the TooManyMethods violation that the encapsulation itself introduced — by decomposition, not by lowering the threshold - disk storage reads and writes the same canonical 12-key map as the SQL JSON column; legacy key names normalized on read, @Expose dropped from domain classes - 39 settings call sites rewritten across 7 files - HiddenField backlog cleared (Config, ObstacleGenerator, GhostService, Leaderboard, PagedMenu, Reward, ParticleData, World) - checkstyle ratchet: VisibilityModifier, HiddenField, EmptyLineSeparator and MissingDeprecated flipped to error once their counters hit zero; suppressions.xml empty - javac -Xmaxwarns raised so NullAway/Error Prone counts are honest (baseline 165/289, now 164/265) checkstyle 0 errors / 135 warnings, PMD 0 violations, tests 94/0 fail, ./gradlew check green. TODO.md: items 30 and 107 updated, new items 167-170, ПРОГРЕСС-6/7 recorded.
This commit is contained in:
parent
c120309663
commit
02e2947f47
90 changed files with 1103 additions and 660 deletions
99
TODO.md
99
TODO.md
|
|
@ -55,7 +55,7 @@
|
||||||
- **27. SQL: утечка Hikari-пулов.** `StorageSQL.init(mode)` вызывается на каждый режим (6 лидербордов), каждый раз пересоздаёт `connectionManager` (StorageSQL.java:66-77, LeaderboardStorage.java:35) → до ~10 соединений висят навсегда. → Один пул на плагин, инициализация один раз.
|
- **27. SQL: утечка Hikari-пулов.** `StorageSQL.init(mode)` вызывается на каждый режим (6 лидербордов), каждый раз пересоздаёт `connectionManager` (StorageSQL.java:66-77, LeaderboardStorage.java:35) → до ~10 соединений висят навсегда. → Один пул на плагин, инициализация один раз.
|
||||||
- **28. SQL: блокирующий connect на main thread** (SQLConnectionManager.java:34-56) — при лежащей БД включение висит до 5с×режимы. → `setInitializationFailTimeout(-1)` + асинхронный connect.
|
- **28. SQL: блокирующий connect на main thread** (SQLConnectionManager.java:34-56) — при лежащей БД включение висит до 5с×режимы. → `setInitializationFailTimeout(-1)` + асинхронный connect.
|
||||||
- **29. SQL: адаптив не стартует.** `AdaptiveServices.init` вызывается до создания лидербордов, а `StorageSQL.runWhenConnected` выбрасывает колбэк при не-подключении (StorageSQL.java:45-51). → Очередь колбэков в SQLConnectionManager.onConnectCallbacks; порядок init: лидерборды → storage → adaptive.
|
- **29. SQL: адаптив не стартует.** `AdaptiveServices.init` вызывается до создания лидербордов, а `StorageSQL.runWhenConnected` выбрасывает колбэк при не-подключении (StorageSQL.java:45-51). → Очередь колбэков в SQLConnectionManager.onConnectCallbacks; порядок init: лидерборды → storage → adaptive.
|
||||||
- **30. SQL: настройки игрока не сохраняются.** `SQLDataMapper.serializeSettings` всегда `"{}"`, десериализация no-op (SQLDataMapper.java:105-115) → при входе `schematicDifficulty`/`blockLead` и др. = null → NPE в `ParkourPlayer.updateGeneratorSettings:157-164` и `PAPIHook.java:140`. → Реализовать сериализацию/десериализацию всех настроек (JSON-колонка) + fallback на defaults.
|
- **30. SQL: настройки игрока не сохраняются.** ✅ **СДЕЛАНО**: `SQLDataMapper.serializeSettings` пишет `PlayerSettingsManager.toSettingsMap(player)` (12 ключей) в JSON-колонку, `deserializeSettings` читает её тем же словарём (`normalizeNumericSettings` + `applySettings`) и падает на `applyDefaults` при пустоте/битости → `schematicDifficulty`/`blockLead` больше не null при входе (NPE в `updateGeneratorSettings`/`PAPIHook` не стало). Диск и SQL теперь используют **один и тот же** набор ключей.
|
||||||
- **31. Схематики: креш на main thread от битых файлов.** `LPSchematicLegacy`/`SchemLoader` парсят без валидации; NPE/AIOOBE мимо `catch(IOException)` (SchematicManager.java:109-133, LpschemConverter.java:44-54). Битый файл = отказ включения плагина. → Валидация dimensions/palette/frameCount + `catch(RuntimeException)` + лимиты размера файла/блоков.
|
- **31. Схематики: креш на main thread от битых файлов.** `LPSchematicLegacy`/`SchemLoader` парсят без валидации; NPE/AIOOBE мимо `catch(IOException)` (SchematicManager.java:109-133, LpschemConverter.java:44-54). Битый файл = отказ включения плагина. → Валидация dimensions/palette/frameCount + `catch(RuntimeException)` + лимиты размера файла/блоков.
|
||||||
- **32. JumpCalculator: возврат невалидного прыжка.** После 10 неудач валидация сдаётся и возвращает заведомо невалидный блок (JumpCalculator.java:125-136). → Пере-роллить offset/направление, а не только уменьшать distance/height; при исчерпании — safety-fallback (простой допустимый шаг).
|
- **32. JumpCalculator: возврат невалидного прыжка.** После 10 неудач валидация сдаётся и возвращает заведомо невалидный блок (JumpCalculator.java:125-136). → Пере-роллить offset/направление, а не только уменьшать distance/height; при исчерпании — safety-fallback (простой допустимый шаг).
|
||||||
- **33. JumpCalculator: диагональный heading залипает.** `JumpDirector.getRecommendedHeading` в безопасной зоне возвращает current без изменений (JumpDirector.java:105-107), а `calculateJumpOffset` при `offset.getX()!=0` перезаписывает Z (JumpCalculator.java:110-115) → курс зигзагом и не выпрямляется. → При диагонали аппендикс не перезаписывать случайным; корректный поворот-домножитель.
|
- **33. JumpCalculator: диагональный heading залипает.** `JumpDirector.getRecommendedHeading` в безопасной зоне возвращает current без изменений (JumpDirector.java:105-107), а `calculateJumpOffset` при `offset.getX()!=0` перезаписывает Z (JumpCalculator.java:110-115) → курс зигзагом и не выпрямляется. → При диагонали аппендикс не перезаписывать случайным; корректный поворот-домножитель.
|
||||||
|
|
@ -86,12 +86,14 @@
|
||||||
- **47. `UserRegistry.getUsers()` строит полный Set на каждый BlockBreak/Place/Drop/Damage** (UserRegistry.java:94-98) → O(n) аллокаций на горячем пути. → Кэш `Map<UUID, ParkourUser>` + привязка через PersistentDataContainer или entity.
|
- **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 формат.
|
- **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.
|
- **49. `getJoinCount` — int между main и async (bStats)** (UserRegistry.java:100-104). → AtomicInteger.
|
||||||
|
- **168. `ParkourUser.chatType` — состояние без записи.** Поле инициализировано `PUBLIC`, читается в `ParkourRestrictionListener.onChat` (switch `chatType()`), но сеттера нет нигде → ветки PRIVATE/GUILD недостижимы, режим группового чата не работает. → Либо подключить переключатель (команда/меню), либо убрать switch и мёртвый enum.
|
||||||
|
|
||||||
### Modes
|
### Modes
|
||||||
|
|
||||||
- **50. `GravityShiftMode` не снимает эффекты при выходе игрока** (только в reset для присутствующих, GravityShiftMode.java:149-163). → Снимать в unregister.
|
- **50. `GravityShiftMode` не снимает эффекты при выходе игрока** (только в reset для присутствующих, GravityShiftMode.java:149-163). → Снимать в unregister.
|
||||||
- **51. `RaceMode.finishRace` — `reset(false)` оставляет игрока в воздухе** (RaceMode.java:142-147; GeneratorCleanup.reset чистит курс без регенерации/телепорта). → Телепорт в лобби + регенерация старта.
|
- **51. `RaceMode.finishRace` — `reset(false)` оставляет игрока в воздухе** (RaceMode.java:142-147; GeneratorCleanup.reset чистит курс без регенерации/телепорта). → Телепорт в лобби + регенерация старта.
|
||||||
- **52. `SpeedrunMode` оставляет удалённые блоки в `history`** → ломает координатные поиски. → Удалять координату из history при снятии блока.
|
- **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()` в под-генераторах (или сравнивать класс генератора) и использовать/удалить геттеры.
|
||||||
|
|
||||||
### Схематики (жизнь в игре)
|
### Схематики (жизнь в игре)
|
||||||
|
|
||||||
|
|
@ -181,7 +183,7 @@
|
||||||
- **104. `ProfileValue.asDouble/asInt` бросают NFE на нечисловых строках** (Profile.java:54,64) — только empty-guard.
|
- **104. `ProfileValue.asDouble/asInt` бросают NFE на нечисловых строках** (Profile.java:54,64) — только empty-guard.
|
||||||
- **105. Дубликат логики `create()` во всех режимах** с несогласованными проверками дублей (instanceof DefaultMode vs instanceof SpeedrunGenerator).
|
- **105. Дубликат логики `create()` во всех режимах** с несогласованными проверками дублей (instanceof DefaultMode vs instanceof SpeedrunGenerator).
|
||||||
- **106. Мёртвый код**: `EffectManager` целиком, `SessionUserManager`, `SQLMigrationManager`, `SQLQueryExecutor`, `SkullSetter`, `Locations.max/min`, `ParkourBlockGenerateEvent`, `onPlayerJoin` (CoopMode.java:169), `PLAYER_COLUMNS`/`OptionContainer` (ParkourPlayer.java:38,205), `acceptingPlayers`-механизм, `Muted` в двух местах.
|
- **106. Мёртвый код**: `EffectManager` целиком, `SessionUserManager`, `SQLMigrationManager`, `SQLQueryExecutor`, `SkullSetter`, `Locations.max/min`, `ParkourBlockGenerateEvent`, `onPlayerJoin` (CoopMode.java:169), `PLAYER_COLUMNS`/`OptionContainer` (ParkourPlayer.java:38,205), `acceptingPlayers`-механизм, `Muted` в двух местах.
|
||||||
- **107. Публичные mutable поля-государство**: `GeneratorState` — 15 публичных полей, `ParkourGenerator.session/island/profile/state/generatorOptions` публичные final, `Score` времени в строке. → Инкапсуляция.
|
- **107. Публичные mutable поля-государство**: ✅ **ЧАСТИЧНО** — `GeneratorState` (15 полей) и весь кластер `player/*` (`ParkourUser`, `ParkourPlayer`, `PlayerSettings`) инкапсулированы в ПРОГРЕСС-6, `VisibilityModifier` переведён в `error`. Остались `ParkourGenerator.session/island/profile/state/generatorOptions` (публичные final — правило их разрешает, но состояние чужое мутируется) и `Score` времени в строке. → Инкапсуляция.
|
||||||
|
|
||||||
## 🧪 Тесты
|
## 🧪 Тесты
|
||||||
|
|
||||||
|
|
@ -306,7 +308,19 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар
|
||||||
- **135. [M] Починить `NPathComplexity` (legacy, 29 / 25 файлов).** Решается декомпозицией методов (файлы и так в списке на рефакторинг).
|
- **135. [M] Починить `NPathComplexity` (legacy, 29 / 25 файлов).** Решается декомпозицией методов (файлы и так в списке на рефакторинг).
|
||||||
- **136. [M] Починить `IllegalCatch` (legacy, 22 / 19 файлов).** catch(Exception/Throwable/RuntimeException)→сужение до конкретных исключений либо переименовать в `expected|ignore`.
|
- **136. [M] Починить `IllegalCatch` (legacy, 22 / 19 файлов).** catch(Exception/Throwable/RuntimeException)→сужение до конкретных исключений либо переименовать в `expected|ignore`.
|
||||||
- **137. [M] Починить все нарушения PMD (45):** `CognitiveComplexity(17)` + `CyclomaticComplexity(7)` — декомпозиция; `GodClass(4)` + `TooManyMethods(8)` — декомпозиция; `AvoidReassigningParameters(7)` — переименование параметров; `EmptyCatchBlock(2)` — комментарий/конкретный тип.
|
- **137. [M] Починить все нарушения PMD (45):** `CognitiveComplexity(17)` + `CyclomaticComplexity(7)` — декомпозиция; `GodClass(4)` + `TooManyMethods(8)` — декомпозиция; `AvoidReassigningParameters(7)` — переименование параметров; `EmptyCatchBlock(2)` — комментарий/конкретный тип.
|
||||||
- **138. [S] Перевести PMD из report-only в жёсткий гейт.** После зачистки убрать `isIgnoreFailures=true` в `build.gradle.kts` (pmd-блок).
|
- **138. [S] Перевести PMD из report-only в жёсткий гейт.** После зачистки убрать `isIgnoreFailures=true` в `build.gradle.kts` (pmd-блок). ✅ сделано в `b771cc1`.
|
||||||
|
- **169. [M] Починить `ReturnCount` (69 warning / 52 файла) — ПЕРЕСТРУКТУРИРОВАНИЕМ, не порогом.** Все 69 — это половина
|
||||||
|
правила про void (`maxForVoid` по умолчанию = 1 ⇒ запрещена любая guard-клауза `if (x) { return; }`); для value-методов
|
||||||
|
при `max=5` нарушений 0. Владелец запретил поднимать порог → в каждом из 69 void-методов убираем ранний `return`
|
||||||
|
(условие становится обычным `if`-блоком) либо выносим тело в helper-метод. Следить за `NestedIfDepth` (error, max=2)
|
||||||
|
и `NPathComplexity`. После обнуления — перевести `ReturnCount` в `error`.
|
||||||
|
- **170. [M] Починить `IllegalCatch` (23 warning / 18 файлов).** 19× `catch (Exception)`, 3× `catch (Throwable)`,
|
||||||
|
1× `catch (RuntimeException)`. Там, где конкретика реальна, — сужать до `IOException`/`NumberFormatException`/
|
||||||
|
`SQLException`/`YAMLException` и т. п. На истинных границах (диспетчер команд `LoParkourCommand:36`, `session-state:84`,
|
||||||
|
`LoParkour:128`) `Throwable` **обязан** стать `RuntimeException`: `Throwable` глотает `Error` (OOM, StackOverflow) —
|
||||||
|
это баг, а не стиль. Пограничные `catch (Exception)` в `onEnable/onDisable`, загрузчиках конфигов/схематчей и
|
||||||
|
`SchematicManager` — оставляем широкими осознанно (релоад плагина не должен ронять сервер), но каждый такой site
|
||||||
|
требует комментария «почему именно Exception»; после разбора правило остаётся в warning на незачищенном остатке.
|
||||||
|
|
||||||
После зачистки п.132–137 — удалить соответствующие записи из `config/checkstyle/suppressions.xml` (супрессии остаются только для ещё незачищенных legacy-файлов).
|
После зачистки п.132–137 — удалить соответствующие записи из `config/checkstyle/suppressions.xml` (супрессии остаются только для ещё незачищенных legacy-файлов).
|
||||||
|
|
||||||
|
|
@ -398,8 +412,9 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар
|
||||||
> `ClassFanOutComplexity` 30→12, `MagicNumber` и `FinalLocalVariable` **warning→error** (оба чистые!).
|
> `ClassFanOutComplexity` 30→12, `MagicNumber` и `FinalLocalVariable` **warning→error** (оба чистые!).
|
||||||
> Добавлены: `ReturnCount` (max 5), `FinalClass`, `HiddenField`, `VisibilityModifier`, `NPathComplexity` (секция вернулась в явном виде).
|
> Добавлены: `ReturnCount` (max 5), `FinalClass`, `HiddenField`, `VisibilityModifier`, `NPathComplexity` (секция вернулась в явном виде).
|
||||||
> - `severity="warning"` (build зелёный, но каждое нарушение видно в отчёте) — **бэклог дизайна**, правило:
|
> - `severity="warning"` (build зелёный, но каждое нарушение видно в отчёте) — **бэклог дизайна**, правило:
|
||||||
> **счётчик обнулился → сразу переводить в error**. Сейчас: `VisibilityModifier` 163, `ReturnCount` 69, `HiddenField` 30,
|
> **счётчик обнулился → сразу переводить в error**. Актуальный список — в ПРОГРЕСС-6 ниже
|
||||||
> `IllegalCatch` 23, `ClassFanOutComplexity` 17, `NPathComplexity` 12, `ParameterNumber` 8, `MethodLength` 5, `CDAC` 2.
|
> (на шаге 0.5: `VisibilityModifier` 163, `ReturnCount` 69, `HiddenField` 30, `IllegalCatch` 23,
|
||||||
|
> `ClassFanOutComplexity` 17, `NPathComplexity` 12, `ParameterNumber` 8, `MethodLength` 5, `CDAC` 2).
|
||||||
> - **`suppressions.xml` ОБНУЛЁН** (45 записей удалено, включая FileLength/NPath/WhitespaceAround/IllegalCatch).
|
> - **`suppressions.xml` ОБНУЛЁН** (45 записей удалено, включая FileLength/NPath/WhitespaceAround/IllegalCatch).
|
||||||
>
|
>
|
||||||
> **Красный baseline (честный, до фиксов): 431 нарушение в 129 файлах.** Замеры порогов перед выбором:
|
> **Красный baseline (честный, до фиксов): 431 нарушение в 129 файлах.** Замеры порогов перед выбором:
|
||||||
|
|
@ -460,8 +475,80 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар
|
||||||
> в `ParkourRestrictionListener.onChat` unboxing `Boolean` → `getOrDefault(CHAT, true)` как в `ParkourOption.mayPerform`.
|
> в `ParkourRestrictionListener.onChat` unboxing `Boolean` → `getOrDefault(CHAT, true)` как в `ParkourOption.mayPerform`.
|
||||||
> - **Счётчики:** checkstyle **0 error / 329 → 240 warning**; `VisibilityModifier` **163 → 75**, `MethodLength` 5 → 4.
|
> - **Счётчики:** checkstyle **0 error / 329 → 240 warning**; `VisibilityModifier` **163 → 75**, `MethodLength` 5 → 4.
|
||||||
> В `config/options` — **0 нарушений VisibilityModifier** (было 88), остаются 2 `IllegalCatch`.
|
> В `config/options` — **0 нарушений VisibilityModifier** (было 88), остаются 2 `IllegalCatch`.
|
||||||
> `./gradlew check` зелёный: PMD 0, тесты 94/0 fail, JaCoCo/ArchUnit/Spotless ок, NullAway 48 (как в baseline).
|
> `./gradlew check` зелёный: PMD 0, тесты 94/0 fail, JaCoCo/ArchUnit/Spotless ок, NullAway 48 (как в baseline;
|
||||||
|
> ⚠️ как выяснилось на шаге 6 — это было артефактом обрезки javac на 100 предупреждениях, честные числа там же).
|
||||||
> Пороги правил не снижались, `suppressions.xml` пустая. Реальных прогонов на сервере после правок не было (только `check`).
|
> Пороги правил не снижались, `suppressions.xml` пустая. Реальных прогонов на сервере после правок не было (только `check`).
|
||||||
|
>
|
||||||
|
> ## ✅ ПРОГРЕСС-6 (Задача 2, шаг (c): инкапсуляция игрового состояния — `player/*` + `GeneratorState`, 2026-10-06)
|
||||||
|
> **Checkstyle 329 → 165 warning; `VisibilityModifier` 75 → 0 и переведён в `error`.**
|
||||||
|
> - **Кластер `player` закрыт:** `ParkourUser` (5 полей) и `ParkourPlayer` (12) → `private` + аксессоры без `get` (та же идиома,
|
||||||
|
> что в ПРОГРЕСС-5 для `config/options`). ~46 обращений в 19 файлах переписаны.
|
||||||
|
> - **Выключен баг тенимого `locale`:** `@Expose String locale` был объявлен и в `ParkourUser`, и в `ParkourPlayer` —
|
||||||
|
> запись в поле и в аксессор расходилась. Осталось одно поле в родителе; на gson-2.11.0 проверено, что `@Expose`
|
||||||
|
> наследуемого поля сериализуется, поэтому набор ключей на диске не изменился.
|
||||||
|
> - **П.107 для `GeneratorState`:** 15 публичных полей → `private` + аксессоры/сеттеры; инкременты и сбросы оформлены
|
||||||
|
> методами (`addGeneratedBlocks(int)`, `resetScore()`, `setScore(int)`), ~90 обращений в 25 файлах.
|
||||||
|
> Публичные `ParkourGenerator.session/island/profile/state` — как были (остаток п.107).
|
||||||
|
> - **SRP: настройки игрока вынесены в `player/data/PlayerSettings`** (11 полей + аксессоры). `ParkourPlayer` —
|
||||||
|
> 194 → 93 строки, засчитываемых методов 20 → 9. Это и починило свежесломанное нарушение PMD `TooManyMethods` (гейт 0 нарушений
|
||||||
|
> был испорчен моим же рефакторингом) — декомпозицией, а НЕ ослаблением порога и НЕ переименованием под name-based
|
||||||
|
> освобождение правила (`getX()` в проекте не используется: 0 объявлений в `src/main`).
|
||||||
|
> - **DRY по настройкам:** троекратное повторение санитизации «legacy-locale» → `PlayerSettingsManager.sanitizeLocale()`
|
||||||
|
> (фолбэк берётся из конфига, а не захардкожен `"en"`); байт-в-байт одинаковая сборка 12-ключевого map в
|
||||||
|
> `StorageDisk.readPlayer` и `SQLDataMapper.serializeSettings` → `toSettingsMap()`; удалены мёртвый `getColumnMappings()`,
|
||||||
|
> `PLAYER_COLUMNS` и дубль `OptionContainer` (п.106); реестр-фасады `ParkourPlayer.isPlayer/getPlayer/getPlayers`
|
||||||
|
> переехали в `UserRegistry` (22 файла);
|
||||||
|
> дубли `parseIntSafe`/`parseDoubleSafe` схлопнуты в `numericOrFallback`.
|
||||||
|
> - **Закрыт п.30** (SQL не сохранял настройки) и **формат disk-файла игрока пересобран сознательно:** раньше Gson писал
|
||||||
|
> `ParkourPlayer` по `@Expose`-полям, а читал его через Unsafe-аллокацию «полупустого» объекта, чей map потом переливали
|
||||||
|
> в живого игрока. Теперь `StorageDisk` пишет и читает тот же канонический `Map<String,Object>`, что и JSON-колонка SQL —
|
||||||
|
> один словарь на оба бэкенда, `@Expose` из доменных классов убран совсем. Старые файлы остаются читаемыми: 3 ключа,
|
||||||
|
> которые совпадали с именами полей (`particles`/`useSpecialBlocks`/`showFallMessage`), нормализует
|
||||||
|
> `StorageDisk.normalizeLegacyKeys()`. ⚠️ Живых файлов игроков в `run/plugins/LoParkour/players/` нет — миграция
|
||||||
|
> проверена кодом и тестами, не реальным апгрейдом.
|
||||||
|
> - **Артефакт замера предупреждений:** в `build.gradle.kts` (JavaCompile) добавлен `-Xmaxwarns 100000`, потому что javac по умолчанию
|
||||||
|
> обрывает список предупреждений на 100 — отсюда «NullAway 48» в ПРОГРЕСС-5. Честные замеры через `git stash`:
|
||||||
|
> baseline HEAD **NullAway 165 / Error Prone 289**, сейчас **NullAway 164 / EP 265** — регресса нет.
|
||||||
|
> - **Счётчики:** checkstyle **0 error / 165 warning** (бэклог: `ReturnCount` 69, `HiddenField` 30, `IllegalCatch` 23,
|
||||||
|
> `ClassFanOutComplexity` 17, `NPathComplexity` 12, `ParameterNumber` 8, `MethodLength` 4, `CDAC` 2).
|
||||||
|
> В ноль обнулены и переведены в `error`: `VisibilityModifier`, `EmptyLineSeparator`, `MissingDeprecated`.
|
||||||
|
> PMD **0** (жёсткий гейт), `./gradlew check` зелёный, тесты **94 / 0 fail (1 skipped)**. Пороги правил не снижались,
|
||||||
|
> `suppressions.xml` пустая. Реальных прогонов на сервере не было (только `check`).
|
||||||
|
> - **Найдено по пути и оформлено отдельными пунктами (молча не чинилось):** **167** (elytra-под-режимы недостижимы —
|
||||||
|
> `getMode()` не переопределён) и **168** (`ParkourUser.chatType` не записывается нигде).
|
||||||
|
>
|
||||||
|
> ## ✅ ПРОГРЕСС-7 (Задача 2, шаг (c): `HiddenField` обнулён + директива владельца про линтеры, 2026-10-06)
|
||||||
|
> **Checkstyle 165 → 135 warning; `HiddenField` 30 → 0 и переведён в `error`.**
|
||||||
|
> - **Директива владельца (зафиксирована, действует дальше):** нарушения линтеров **чинить**, а не **глушить** —
|
||||||
|
> пороги правил не поднимать, `suppressions.xml` не наполнять, под name-based освобождения не подстраиваться.
|
||||||
|
> В `error` правило переводится только после того, как счётчик реально обнулён правками.
|
||||||
|
> - **`HiddenField` 30 → 0** — тенящие параметры/локалы переименованы (поля не трогали): `Config` (12 методов-делегата,
|
||||||
|
> параметр `path` → `key`), `ObstacleGenerator` (внешнее поле `radius` → `obstacleRadius` — одним переименованием сняли
|
||||||
|
> 7 теней в enum-реализациях `getPoints(center, radius)`), `GhostService.setReplaying(uuid, active)`,
|
||||||
|
> `Leaderboard.sort(order)`, `PagedMenu.addToDisplay(menuItems)`, `Reward` (локал `string` → `command`),
|
||||||
|
> `ParticleData` (fluent-сеттеры принимают `value`), `World.create()` (локал `world` → `existingWorld`, а
|
||||||
|
> `World.world = world` превратился в обычное присваивание поля).
|
||||||
|
> - **`ReturnCount` 69 — решено оформить отдельным пунктом 169:** все 69 нарушений приходятся на половину правила
|
||||||
|
> про void (`maxForVoid` по умолчанию = 1, то есть запрещена любая guard-клауза `if (x) { return; }`);
|
||||||
|
> для value-методов при `max=5` нарушений **0**. Поднимать `maxForVoid` я предлагал, владелец отказал — значит
|
||||||
|
> 69 void-методов переструктурируются по-настоящему (early-return → `if`-блок или выделение helper'а),
|
||||||
|
> с оглядкой на `NestedIfDepth` (error, max=2) и `NPathComplexity`.
|
||||||
|
> - **`IllegalCatch` 23** — сузить типы там, где конкретика реальна (IO/NumberFormat/SQL/ClassNotFound),
|
||||||
|
> а на истинных границах заменить `catch (Throwable)` → `catch (RuntimeException)` (это не «приглушение», а починка:
|
||||||
|
> `Throwable` глотает `Error`-ы — OOM, StackOverflow). Диспетчер команд, `onEnable/onDisable`, загрузчики конфигов
|
||||||
|
> и схематчей, session-state — пограничные точки, их фиксируем отдельно (п.170).
|
||||||
|
> - **Авария и восстановление:** после частичного отката правок в IDE (отклонённые diffs) рабочий код был сломан:
|
||||||
|
> `player/data/PlayerSettings.java` удалился целиком, `ParkourPlayer` вернулся к 11 `@Expose`-полям при уже
|
||||||
|
> переписанных вызовах (`settings()` ссылался на несуществующее поле, `updateGeneratorSettings` пропал),
|
||||||
|
> в `GeneratorCleanup` потерялся импорт `@Nullable` (2 ошибки компиляции), `checkstyle.xml` откатился к
|
||||||
|
> HEAD-версии и **потерял все четыре флипа** (`VisibilityModifier`, `EmptyLineSeparator`, `MissingDeprecated`,
|
||||||
|
> `HiddenField`), `PagedMenu`/`GhostService` вернулись к тенящим параметрам, а с `ParkourUser` слез лишний `@Expose`
|
||||||
|
> (после перехода диска на map он мёртвый). Всё восстановлено и перепроверено:
|
||||||
|
> `./gradlew check` — **BUILD SUCCESSFUL**, checkstyle **0 error / 135 warning** (test-отчёт пуст), PMD **0**,
|
||||||
|
> тесты **94 / 0 fail (1 skipped)**, `suppressions.xml` пустая. Реальных прогонов на сервере по-прежнему не было.
|
||||||
|
> Урок на будущее: после любой отменённой пачки правок сверять `git status` + полный `check`, а не только счётчики.
|
||||||
|
> - **Остаток бэклога (135):** `ReturnCount` 69, `IllegalCatch` 23, `ClassFanOutComplexity` 17, `NPathComplexity` 12,
|
||||||
|
> `ParameterNumber` 8, `MethodLength` 4, `ClassDataAbstractionCoupling` 2.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ tasks.withType<Test> {
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
options.release = javaRelease
|
options.release = javaRelease
|
||||||
|
options.compilerArgs.addAll(listOf("-Xmaxwarns", "100000"))
|
||||||
options.errorprone {
|
options.errorprone {
|
||||||
disableWarningsInGeneratedCode = true
|
disableWarningsInGeneratedCode = true
|
||||||
// NullAway: only code annotated @NullMarked-style packages is checked once adopted;
|
// NullAway: only code annotated @NullMarked-style packages is checked once adopted;
|
||||||
|
|
|
||||||
|
|
@ -113,12 +113,12 @@
|
||||||
<module name="HiddenField">
|
<module name="HiddenField">
|
||||||
<property name="ignoreConstructorParameter" value="true"/>
|
<property name="ignoreConstructorParameter" value="true"/>
|
||||||
<property name="ignoreSetter" value="true"/>
|
<property name="ignoreSetter" value="true"/>
|
||||||
<property name="severity" value="warning"/>
|
<property name="severity" value="error"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
<module name="VisibilityModifier">
|
<module name="VisibilityModifier">
|
||||||
<property name="allowPublicFinalFields" value="true"/>
|
<property name="allowPublicFinalFields" value="true"/>
|
||||||
<property name="severity" value="warning"/>
|
<property name="severity" value="error"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
<module name="EmptyBlock">
|
<module name="EmptyBlock">
|
||||||
|
|
@ -182,15 +182,12 @@
|
||||||
<!-- ============ Style and maintainability ============ -->
|
<!-- ============ Style and maintainability ============ -->
|
||||||
<module name="EmptyLineSeparator">
|
<module name="EmptyLineSeparator">
|
||||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||||
<property name="severity" value="warning"/>
|
|
||||||
</module>
|
</module>
|
||||||
<module name="WhitespaceAround"/>
|
<module name="WhitespaceAround"/>
|
||||||
<module name="WhitespaceAfter"/>
|
<module name="WhitespaceAfter"/>
|
||||||
<module name="NoWhitespaceBefore"/>
|
<module name="NoWhitespaceBefore"/>
|
||||||
<module name="MissingOverride"/>
|
<module name="MissingOverride"/>
|
||||||
<module name="MissingDeprecated">
|
<module name="MissingDeprecated"/>
|
||||||
<property name="severity" value="warning"/>
|
|
||||||
</module>
|
|
||||||
<module name="FinalLocalVariable">
|
<module name="FinalLocalVariable">
|
||||||
<property name="validateEnhancedForLoopVariable" value="true"/>
|
<property name="validateEnhancedForLoopVariable" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,8 @@ public final class LoParkour extends JavaPlugin {
|
||||||
ParkourUser.leave(user);
|
ParkourUser.leave(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Modes.DEFAULT != null && Modes.DEFAULT.getLeaderboard() != null) {
|
if (Modes.defaultMode() != null && Modes.defaultMode().getLeaderboard() != null) {
|
||||||
Modes.DEFAULT.getLeaderboard().write(false);
|
Modes.defaultMode().getLeaderboard().write(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rewards.clear();
|
Rewards.clear();
|
||||||
|
|
|
||||||
|
|
@ -172,8 +172,8 @@ public class SQLStatsStorage implements StatsRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class PendingUpdate {
|
private static final class PendingUpdate {
|
||||||
int jumps = 0;
|
private int jumps = 0;
|
||||||
int falls = 0;
|
private int falls = 0;
|
||||||
int longestStreak = 0;
|
private int longestStreak = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package dev.loki.loparkour.api.core;
|
||||||
import dev.loki.loparkour.api.event.generation.ParkourBlockGenerateEvent;
|
import dev.loki.loparkour.api.event.generation.ParkourBlockGenerateEvent;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -28,7 +29,7 @@ public final class ParkourAPI {
|
||||||
* @return The {@link ParkourPlayer}. Null if not found.
|
* @return The {@link ParkourPlayer}. Null if not found.
|
||||||
*/
|
*/
|
||||||
public static @Nullable ParkourPlayer getPlayer(Player player) {
|
public static @Nullable ParkourPlayer getPlayer(Player player) {
|
||||||
return ParkourPlayer.getPlayer(player);
|
return UserRegistry.getPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ public final class PluginBootstrap {
|
||||||
private static void setupMetrics(@NotNull LoParkour plugin) {
|
private static void setupMetrics(@NotNull LoParkour plugin) {
|
||||||
final Metrics metrics = new Metrics(plugin, BSTATS_PLUGIN_ID);
|
final Metrics metrics = new Metrics(plugin, BSTATS_PLUGIN_ID);
|
||||||
metrics.addCustomChart(new SimplePie("using_sql", () -> Boolean.toString(OptionSQL.enabled())));
|
metrics.addCustomChart(new SimplePie("using_sql", () -> Boolean.toString(OptionSQL.enabled())));
|
||||||
metrics.addCustomChart(new SimplePie("using_rewards", () -> Boolean.toString(Rewards.REWARDS_ENABLED)));
|
metrics.addCustomChart(new SimplePie("using_rewards", () -> Boolean.toString(Rewards.enabled())));
|
||||||
metrics.addCustomChart(new SimplePie("locale_count", () -> Integer.toString(Locales.getLocaleCount())));
|
metrics.addCustomChart(new SimplePie("locale_count", () -> Integer.toString(Locales.getLocaleCount())));
|
||||||
metrics.addCustomChart(new SingleLineChart("player_joins", () ->
|
metrics.addCustomChart(new SingleLineChart("player_joins", () ->
|
||||||
dev.loki.loparkour.player.service.UserRegistry.getJoinCount()));
|
dev.loki.loparkour.player.service.UserRegistry.getJoinCount()));
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ public final class ForceJoinExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void joinEveryone(CommandSender sender) {
|
private void joinEveryone(CommandSender sender) {
|
||||||
Bukkit.getOnlinePlayers().forEach(p -> Modes.DEFAULT.create(p));
|
Bukkit.getOnlinePlayers().forEach(p -> Modes.defaultMode().create(p));
|
||||||
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_join_everyone"));
|
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_join_everyone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ public final class ForceJoinExecutor {
|
||||||
if (closest == null) {
|
if (closest == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Modes.DEFAULT.create(closest);
|
Modes.defaultMode().create(closest);
|
||||||
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_join_player").formatted(closest.getName()));
|
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_join_player").formatted(closest.getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +55,6 @@ public final class ForceJoinExecutor {
|
||||||
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_online"));
|
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_online"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Modes.DEFAULT.create(other);
|
Modes.defaultMode().create(other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ package dev.loki.loparkour.command.admin.ops;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.command.player.PlayerCommandHandler;
|
import dev.loki.loparkour.command.player.PlayerCommandHandler;
|
||||||
import dev.loki.loparkour.config.locale.Locales;
|
import dev.loki.loparkour.config.locale.Locales;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import dev.loki.loparkour.command.admin.support.AdminResponses;
|
import dev.loki.loparkour.command.admin.support.AdminResponses;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public final class ForceLeaveExecutor {
|
public final class ForceLeaveExecutor {
|
||||||
|
|
@ -32,7 +32,7 @@ public final class ForceLeaveExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void leaveEveryone(CommandSender sender) {
|
private void leaveEveryone(CommandSender sender) {
|
||||||
ParkourPlayer.getPlayers().forEach(ParkourUser::leave);
|
UserRegistry.getPlayers().forEach(ParkourUser::leave);
|
||||||
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_leave_everyone"));
|
AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.force_leave_everyone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package dev.loki.loparkour.command.core;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.api.core.Registry;
|
import dev.loki.loparkour.api.core.Registry;
|
||||||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
|
||||||
import dev.loki.loparkour.schematic.core.SchematicManager;
|
import dev.loki.loparkour.schematic.core.SchematicManager;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -86,7 +86,7 @@ final class CommandTabCompleter {
|
||||||
final List<String> completions = new ArrayList<>();
|
final List<String> completions = new ArrayList<>();
|
||||||
if (sender.hasPermission(ParkourOption.ADMIN.permission)) {
|
if (sender.hasPermission(ParkourOption.ADMIN.permission)) {
|
||||||
completions.add("everyone");
|
completions.add("everyone");
|
||||||
ParkourPlayer.getPlayers().forEach(pp -> completions.add(pp.getName()));
|
UserRegistry.getPlayers().forEach(pp -> completions.add(pp.getName()));
|
||||||
}
|
}
|
||||||
return filter(input, completions);
|
return filter(input, completions);
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +95,7 @@ final class CommandTabCompleter {
|
||||||
final List<String> completions = new ArrayList<>();
|
final List<String> completions = new ArrayList<>();
|
||||||
if (ParkourOption.JOIN.mayPerform(sender)) {
|
if (ParkourOption.JOIN.mayPerform(sender)) {
|
||||||
Registry.getModes().forEach(m -> completions.add(m.getName()));
|
Registry.getModes().forEach(m -> completions.add(m.getName()));
|
||||||
ParkourPlayer.getPlayers().forEach(pp -> completions.add(pp.getName()));
|
UserRegistry.getPlayers().forEach(pp -> completions.add(pp.getName()));
|
||||||
}
|
}
|
||||||
return filter(input, completions);
|
return filter(input, completions);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import dev.loki.loparkour.mode.base.MultiMode;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
@ -37,7 +38,7 @@ public class JoinCommandExecutor {
|
||||||
CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.unknown_player"));
|
CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.unknown_player"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(other);
|
final ParkourPlayer pp = UserRegistry.getPlayer(other);
|
||||||
if (pp == null) {
|
if (pp == null) {
|
||||||
CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.not_playing"));
|
CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.not_playing"));
|
||||||
return;
|
return;
|
||||||
|
|
@ -53,16 +54,16 @@ public class JoinCommandExecutor {
|
||||||
if (session.isAcceptingPlayers()) {
|
if (session.isAcceptingPlayers()) {
|
||||||
joinAcceptingSession(p, session);
|
joinAcceptingSession(p, session);
|
||||||
} else {
|
} else {
|
||||||
Modes.SPECTATOR.create(p, session);
|
Modes.spectator().create(p, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void joinAcceptingSession(Player p, Session session) {
|
private void joinAcceptingSession(Player p, Session session) {
|
||||||
final Mode sessionMode = session.generator.getMode();
|
final Mode sessionMode = session.generator().getMode();
|
||||||
if (sessionMode instanceof MultiMode mm) {
|
if (sessionMode instanceof MultiMode mm) {
|
||||||
mm.join(p, session);
|
mm.join(p, session);
|
||||||
} else {
|
} else {
|
||||||
Modes.SPECTATOR.create(p, session);
|
Modes.spectator().create(p, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ public class PlayerCommandHandler {
|
||||||
if (ParkourUser.getUser(player) != null) {
|
if (ParkourUser.getUser(player) != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Modes.DEFAULT.create(player);
|
Modes.defaultMode().create(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReload(@NotNull CommandSender sender, @Nullable Player player) {
|
private void handleReload(@NotNull CommandSender sender, @Nullable Player player) {
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,10 @@ public enum Config {
|
||||||
REWARDS("rewards-v2.yml", List.of("score-rewards", "interval-rewards", "one-time-rewards")),
|
REWARDS("rewards-v2.yml", List.of("score-rewards", "interval-rewards", "one-time-rewards")),
|
||||||
SCHEMATICS("schematics/schematics.yml", List.of("difficulty"));
|
SCHEMATICS("schematics/schematics.yml", List.of("difficulty"));
|
||||||
|
|
||||||
public File path;
|
private File path;
|
||||||
public final String fileName;
|
public final String fileName;
|
||||||
public final List<String> ignoredSections;
|
public final List<String> ignoredSections;
|
||||||
public FileConfiguration fileConfiguration;
|
private FileConfiguration fileConfiguration;
|
||||||
|
|
||||||
private ConfigAccessor accessor;
|
private ConfigAccessor accessor;
|
||||||
|
|
||||||
|
|
@ -31,6 +31,43 @@ public enum Config {
|
||||||
this.path = null; // Resolved lazily
|
this.path = null; // Resolved lazily
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of this config on disk.
|
||||||
|
*
|
||||||
|
* @return The file this configuration is read from.
|
||||||
|
*/
|
||||||
|
public File path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsed contents of this config.
|
||||||
|
*
|
||||||
|
* @return The parsed configuration currently held in memory.
|
||||||
|
*/
|
||||||
|
public FileConfiguration fileConfiguration() {
|
||||||
|
return fileConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remembers where this configuration lives on disk. Only called while the config files are first resolved.
|
||||||
|
*
|
||||||
|
* @param file The resolved config file.
|
||||||
|
*/
|
||||||
|
public void bindPath(@NotNull File file) {
|
||||||
|
this.path = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the in-memory configuration and refreshes the accessor that delegates reads to it.
|
||||||
|
*
|
||||||
|
* @param configuration The freshly loaded configuration.
|
||||||
|
*/
|
||||||
|
public void apply(@NotNull FileConfiguration configuration) {
|
||||||
|
this.fileConfiguration = configuration;
|
||||||
|
this.accessor = new ConfigAccessor(configuration, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload all configuration files.
|
* Reload all configuration files.
|
||||||
*/
|
*/
|
||||||
|
|
@ -56,8 +93,7 @@ public enum Config {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fileConfiguration = ConfigLoader.loadConfig(path);
|
apply(ConfigLoader.loadConfig(path));
|
||||||
this.accessor = new ConfigAccessor(fileConfiguration, fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,55 +107,55 @@ public enum Config {
|
||||||
|
|
||||||
// Delegate all accessor methods to ConfigAccessor
|
// Delegate all accessor methods to ConfigAccessor
|
||||||
|
|
||||||
public boolean isPath(@NotNull String path) {
|
public boolean isPath(@NotNull String key) {
|
||||||
return accessor != null && accessor.isPath(path);
|
return accessor != null && accessor.isPath(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object get(@NotNull String path) {
|
public Object get(@NotNull String key) {
|
||||||
return accessor.get(path);
|
return accessor.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getBoolean(@NotNull String path) {
|
public boolean getBoolean(@NotNull String key) {
|
||||||
return accessor.getBoolean(path);
|
return accessor.getBoolean(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getBoolean(@NotNull String path, boolean defaultValue) {
|
public boolean getBoolean(@NotNull String key, boolean defaultValue) {
|
||||||
return accessor != null ? accessor.getBoolean(path, defaultValue) : defaultValue;
|
return accessor != null ? accessor.getBoolean(key, defaultValue) : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInt(@NotNull String path) {
|
public int getInt(@NotNull String key) {
|
||||||
return accessor.getInt(path);
|
return accessor.getInt(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInt(@NotNull String path, int defaultValue) {
|
public int getInt(@NotNull String key, int defaultValue) {
|
||||||
return accessor != null ? accessor.getInt(path, defaultValue) : defaultValue;
|
return accessor != null ? accessor.getInt(key, defaultValue) : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDouble(@NotNull String path) {
|
public double getDouble(@NotNull String key) {
|
||||||
return accessor.getDouble(path);
|
return accessor.getDouble(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDouble(@NotNull String path, double defaultValue) {
|
public double getDouble(@NotNull String key, double defaultValue) {
|
||||||
return accessor != null ? accessor.getDouble(path, defaultValue) : defaultValue;
|
return accessor != null ? accessor.getDouble(key, defaultValue) : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getString(@NotNull String path) {
|
public String getString(@NotNull String key) {
|
||||||
return accessor.getString(path);
|
return accessor.getString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<String> getStringList(@NotNull String path) {
|
public List<String> getStringList(@NotNull String key) {
|
||||||
return accessor.getStringList(path);
|
return accessor.getStringList(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<Integer> getIntList(@NotNull String path) {
|
public List<Integer> getIntList(@NotNull String key) {
|
||||||
return accessor.getIntList(path);
|
return accessor.getIntList(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<String> getChildren(@NotNull String path, boolean... deep) {
|
public List<String> getChildren(@NotNull String key, boolean... deep) {
|
||||||
return accessor.getChildren(path, deep);
|
return accessor.getChildren(key, deep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@ public final class ConfigLoader {
|
||||||
public static void reloadAllConfigs(boolean initialLoad) {
|
public static void reloadAllConfigs(boolean initialLoad) {
|
||||||
for (final Config config : Config.values()) {
|
for (final Config config : Config.values()) {
|
||||||
if (initialLoad) {
|
if (initialLoad) {
|
||||||
config.path = initializeConfigFile(config.fileName);
|
config.bindPath(initializeConfigFile(config.fileName));
|
||||||
updateConfig(config.fileName, config.path, config.ignoredSections);
|
updateConfig(config.fileName, config.path(), config.ignoredSections);
|
||||||
}
|
}
|
||||||
config.fileConfiguration = loadConfig(config.path);
|
config.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
validateConfigs();
|
validateConfigs();
|
||||||
|
|
|
||||||
|
|
@ -170,13 +170,13 @@ public final class Locales {
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String getPlayerLocale(@NotNull Player player) {
|
private static String getPlayerLocale(@NotNull Player player) {
|
||||||
final ParkourUser user = ParkourUser.getUser(player);
|
final ParkourUser user = ParkourUser.getUser(player);
|
||||||
String loc = user == null ? OptionGeneral.optionsDefaults().get(ParkourOption.LANG) : user.locale;
|
String loc = user == null ? OptionGeneral.optionsDefaults().get(ParkourOption.LANG) : user.locale();
|
||||||
|
|
||||||
// Fix legacy Boolean "true" values from old config parsing
|
// Fix legacy Boolean "true" values from old config parsing
|
||||||
if ("true".equals(loc) || "false".equals(loc)) {
|
if ("true".equals(loc) || "false".equals(loc)) {
|
||||||
loc = OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
loc = OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
user.locale = loc; // Update user's locale to fixed value
|
user.setLocale(loc); // Update user's locale to fixed value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ public final class Option {
|
||||||
OptionGeneral.init(firstLoad);
|
OptionGeneral.init(firstLoad);
|
||||||
OptionGeneral.initOptions();
|
OptionGeneral.initOptions();
|
||||||
|
|
||||||
OptionStyles.initStyles("styles.list", Config.CONFIG.fileConfiguration, RandomStyle::new)
|
OptionStyles.initStyles("styles.list", Config.CONFIG.fileConfiguration(), RandomStyle::new)
|
||||||
.forEach(Registry::register);
|
.forEach(Registry::register);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public final class OptionGeneral {
|
||||||
if (option == ParkourOption.LANG) {
|
if (option == ParkourOption.LANG) {
|
||||||
LoParkour.getPlugin().getLogger().warning("LANG default path not found: " + defaultPath);
|
LoParkour.getPlugin().getLogger().warning("LANG default path not found: " + defaultPath);
|
||||||
LoParkour.getPlugin().getLogger().warning(
|
LoParkour.getPlugin().getLogger().warning(
|
||||||
"Available keys: " + Config.CONFIG.fileConfiguration.getKeys(true));
|
"Available keys: " + Config.CONFIG.fileConfiguration().getKeys(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ final class DuelsCountdown {
|
||||||
void giveStartItem(@NotNull ParkourPlayer owner) {
|
void giveStartItem(@NotNull ParkourPlayer owner) {
|
||||||
Bukkit.getScheduler().runTaskLater(LoParkour.getPlugin(), () -> {
|
Bukkit.getScheduler().runTaskLater(LoParkour.getPlugin(), () -> {
|
||||||
if (owner.player.isOnline()) {
|
if (owner.player.isOnline()) {
|
||||||
owner.player.getInventory().addItem(Locales.getItem(owner.locale, "play.multi.duels.start").build());
|
owner.player.getInventory().addItem(Locales.getItem(owner.locale(), "play.multi.duels.start").build());
|
||||||
}
|
}
|
||||||
}, BANNER_DELAY_TICKS);
|
}, BANNER_DELAY_TICKS);
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ final class DuelsCountdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
void tick(@NotNull int[] counter) {
|
void tick(@NotNull int[] counter) {
|
||||||
if (generator.state.stopped) {
|
if (generator.state.stopped()) {
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -79,19 +79,19 @@ final class DuelsCountdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
void startRace() {
|
void startRace() {
|
||||||
generator.allowJoining = false;
|
generator.setAllowJoining(false);
|
||||||
for (final Map.Entry<ParkourPlayer, SingleDuelsGenerator> entry : generator.playerGenerators.entrySet()) {
|
for (final Map.Entry<ParkourPlayer, SingleDuelsGenerator> entry : generator.playerGenerators.entrySet()) {
|
||||||
final Location ps = generator.playerSpawns.get(entry.getKey());
|
final Location ps = generator.playerSpawns.get(entry.getKey());
|
||||||
if (ps == null) {
|
if (ps == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final Vector heading = entry.getValue().state.heading;
|
final Vector heading = entry.getValue().state.heading();
|
||||||
final Location bs = ps.clone().add(FIRST_BLOCK_FORWARD * heading.getX() - SPAWN_CENTER_OFFSET, 0,
|
final Location bs = ps.clone().add(FIRST_BLOCK_FORWARD * heading.getX() - SPAWN_CENTER_OFFSET, 0,
|
||||||
FIRST_BLOCK_FORWARD * heading.getZ() - SPAWN_CENTER_OFFSET);
|
FIRST_BLOCK_FORWARD * heading.getZ() - SPAWN_CENTER_OFFSET);
|
||||||
entry.getValue().generateFirst(ps, bs);
|
entry.getValue().generateFirst(ps, bs);
|
||||||
}
|
}
|
||||||
for (final ParkourPlayer pp : new ArrayList<>(generator.playerGenerators.keySet())) {
|
for (final ParkourPlayer pp : new ArrayList<>(generator.playerGenerators.keySet())) {
|
||||||
pp.player.sendTitle(ColorUtil.color(Locales.getString(pp.locale, "play.multi.duels.go")), "", 0, TITLE_STAY, 0);
|
pp.player.sendTitle(ColorUtil.color(Locales.getString(pp.locale(), "play.multi.duels.go")), "", 0, TITLE_STAY, 0);
|
||||||
}
|
}
|
||||||
cancel();
|
cancel();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
private static final int VICTORY_STAY = 100;
|
private static final int VICTORY_STAY = 100;
|
||||||
private static final int VICTORY_FADE_OUT = 10;
|
private static final int VICTORY_FADE_OUT = 10;
|
||||||
|
|
||||||
public boolean allowJoining = true;
|
private boolean allowJoining = true;
|
||||||
public final ParkourPlayer owner;
|
public final ParkourPlayer owner;
|
||||||
public final Map<ParkourPlayer, SingleDuelsGenerator> playerGenerators = new LinkedHashMap<>();
|
public final Map<ParkourPlayer, SingleDuelsGenerator> playerGenerators = new LinkedHashMap<>();
|
||||||
public final int goal;
|
public final int goal;
|
||||||
|
|
@ -53,6 +53,22 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
countdown.giveStartItem(owner);
|
countdown.giveStartItem(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return True while new players may still join this duel.
|
||||||
|
*/
|
||||||
|
public boolean allowJoining() {
|
||||||
|
return allowJoining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens or closes the duel for new players.
|
||||||
|
*
|
||||||
|
* @param allowJoining Whether joining is allowed.
|
||||||
|
*/
|
||||||
|
public void setAllowJoining(boolean allowJoining) {
|
||||||
|
this.allowJoining = allowJoining;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateFirst(Location spawn, Location block) {
|
public void generateFirst(Location spawn, Location block) {
|
||||||
playerSpawns.put(owner, spawn);
|
playerSpawns.put(owner, spawn);
|
||||||
|
|
@ -65,7 +81,7 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
if (!state.stopped) {
|
if (!state.stopped()) {
|
||||||
playerGenerators.values().forEach(SingleDuelsGenerator::tick);
|
playerGenerators.values().forEach(SingleDuelsGenerator::tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,10 +91,10 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final SingleDuelsGenerator gen = new SingleDuelsGenerator(session);
|
final SingleDuelsGenerator gen = new SingleDuelsGenerator(session);
|
||||||
gen.owningGenerator = this;
|
gen.setOwningGenerator(this);
|
||||||
gen.setOwnedPlayer(pp);
|
gen.setOwnedPlayer(pp);
|
||||||
gen.setPlayerIndex(playerGenerators.size());
|
gen.setPlayerIndex(playerGenerators.size());
|
||||||
gen.state.heading = OptionGeneral.heading().getDirection();
|
gen.state.setHeading(OptionGeneral.heading().getDirection());
|
||||||
final Location spawnBase = session.getSpawnLocation();
|
final Location spawnBase = session.getSpawnLocation();
|
||||||
if (spawnBase == null || spawnBase.getWorld() == null) {
|
if (spawnBase == null || spawnBase.getWorld() == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -115,23 +131,23 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void win(@NotNull ParkourPlayer winner) {
|
public void win(@NotNull ParkourPlayer winner) {
|
||||||
if (state.stopped) {
|
if (state.stopped()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.stopped = true;
|
state.setStopped(true);
|
||||||
countdown.cancel();
|
countdown.cancel();
|
||||||
final String winningName = winner.getName();
|
final String winningName = winner.getName();
|
||||||
final String winningTime = playerGenerators.get(winner).getFormattedTime();
|
final String winningTime = playerGenerators.get(winner).getFormattedTime();
|
||||||
final List<Map.Entry<ParkourPlayer, SingleDuelsGenerator>> leaderboard =
|
final List<Map.Entry<ParkourPlayer, SingleDuelsGenerator>> leaderboard =
|
||||||
new ArrayList<>(playerGenerators.entrySet());
|
new ArrayList<>(playerGenerators.entrySet());
|
||||||
leaderboard.sort((one, two) -> two.getValue().state.score - one.getValue().state.score);
|
leaderboard.sort((one, two) -> two.getValue().state.score() - one.getValue().state.score());
|
||||||
for (final ParkourPlayer pp : new ArrayList<>(playerGenerators.keySet())) {
|
for (final ParkourPlayer pp : new ArrayList<>(playerGenerators.keySet())) {
|
||||||
pp.send(Locales.getString(pp.locale, "play.multi.duels.overview").formatted(winningName, winningTime));
|
pp.send(Locales.getString(pp.locale(), "play.multi.duels.overview").formatted(winningName, winningTime));
|
||||||
for (int i = 0; i < leaderboard.size(); i++) {
|
for (int i = 0; i < leaderboard.size(); i++) {
|
||||||
pp.send(Locales.getString(pp.locale, "play.multi.duels.place")
|
pp.send(Locales.getString(pp.locale(), "play.multi.duels.place")
|
||||||
.formatted(i + 1, leaderboard.get(i).getKey().getName(), leaderboard.get(i).getValue().state.score));
|
.formatted(i + 1, leaderboard.get(i).getKey().getName(), leaderboard.get(i).getValue().state.score()));
|
||||||
}
|
}
|
||||||
pp.player.sendTitle(ColorUtil.color(Locales.getString(pp.locale,
|
pp.player.sendTitle(ColorUtil.color(Locales.getString(pp.locale(),
|
||||||
pp == winner ? "play.multi.duels.victory" : "play.multi.duels.loss")), "", VICTORY_FADE_IN, VICTORY_STAY, VICTORY_FADE_OUT);
|
pp == winner ? "play.multi.duels.victory" : "play.multi.duels.loss")), "", VICTORY_FADE_IN, VICTORY_STAY, VICTORY_FADE_OUT);
|
||||||
}
|
}
|
||||||
Bukkit.getScheduler().runTaskLater(LoParkour.getPlugin(), () -> returnToLobby(), WIN_RETURN_DELAY_TICKS);
|
Bukkit.getScheduler().runTaskLater(LoParkour.getPlugin(), () -> returnToLobby(), WIN_RETURN_DELAY_TICKS);
|
||||||
|
|
@ -155,13 +171,13 @@ public class DuelsGenerator extends ParkourGenerator {
|
||||||
private void returnToLobby() {
|
private void returnToLobby() {
|
||||||
for (final ParkourPlayer pp : new ArrayList<>(playerGenerators.keySet())) {
|
for (final ParkourPlayer pp : new ArrayList<>(playerGenerators.keySet())) {
|
||||||
if (pp.player.isOnline() && playerGenerators.containsKey(pp)) {
|
if (pp.player.isOnline() && playerGenerators.containsKey(pp)) {
|
||||||
Modes.DEFAULT.create(pp.player);
|
Modes.defaultMode().create(pp.player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.DUELS;
|
return Modes.duels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class DuelsMode implements MultiMode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create(Player player) {
|
public void create(Player player) {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof DuelsGenerator) {
|
if (pp != null && pp.session.generator() instanceof DuelsGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +52,7 @@ public class DuelsMode implements MultiMode {
|
||||||
|
|
||||||
final int maxPlayers = getMaxPlayers();
|
final int maxPlayers = getMaxPlayers();
|
||||||
Session.create(DuelsGenerator::new,
|
Session.create(DuelsGenerator::new,
|
||||||
session -> session.getPlayers().size() < maxPlayers && ((DuelsGenerator) session.generator).allowJoining,
|
session -> session.getPlayers().size() < maxPlayers && ((DuelsGenerator) session.generator()).allowJoining(),
|
||||||
null,
|
null,
|
||||||
player);
|
player);
|
||||||
}
|
}
|
||||||
|
|
@ -66,16 +67,16 @@ public class DuelsMode implements MultiMode {
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourUser.register(player, session);
|
final ParkourPlayer pp = ParkourUser.register(player, session);
|
||||||
session.addPlayers(pp);
|
session.addPlayers(pp);
|
||||||
((DuelsGenerator) session.generator).addPlayer(pp);
|
((DuelsGenerator) session.generator()).addPlayer(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leave(Player player, Session session) {
|
public void leave(Player player, Session session) {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp == null || session.getPlayers().isEmpty()) {
|
if (pp == null || session.getPlayers().isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
((DuelsGenerator) session.generator).removePlayer(pp);
|
((DuelsGenerator) session.generator()).removePlayer(pp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import java.util.List;
|
||||||
* One parkour course per duels player, isolated from everyone else.
|
* One parkour course per duels player, isolated from everyone else.
|
||||||
*
|
*
|
||||||
* <p>Scoring only looks at the owned player: {@link #getPlayers()} returns just
|
* <p>Scoring only looks at the owned player: {@link #getPlayers()} returns just
|
||||||
* the owned player, so {@code state.history}/{@code state.score} belong to that
|
* the owned player, so {@code state.history}/{@code state.score()} belong to that
|
||||||
* single course. The owning {@link DuelsGenerator} ticks every instance.</p>
|
* single course. The owning {@link DuelsGenerator} ticks every instance.</p>
|
||||||
*/
|
*/
|
||||||
public class SingleDuelsGenerator extends ParkourGenerator {
|
public class SingleDuelsGenerator extends ParkourGenerator {
|
||||||
|
|
@ -26,7 +26,7 @@ public class SingleDuelsGenerator extends ParkourGenerator {
|
||||||
Material.LIGHT_GRAY_CONCRETE, Material.BLACK_CONCRETE, Material.GREEN_CONCRETE, Material.BROWN_CONCRETE,
|
Material.LIGHT_GRAY_CONCRETE, Material.BLACK_CONCRETE, Material.GREEN_CONCRETE, Material.BROWN_CONCRETE,
|
||||||
Material.CYAN_CONCRETE, Material.PURPLE_CONCRETE, Material.GRAY_CONCRETE, Material.PINK_CONCRETE);
|
Material.CYAN_CONCRETE, Material.PURPLE_CONCRETE, Material.GRAY_CONCRETE, Material.PINK_CONCRETE);
|
||||||
|
|
||||||
public DuelsGenerator owningGenerator;
|
private DuelsGenerator owningGenerator;
|
||||||
private ParkourPlayer ownedPlayer;
|
private ParkourPlayer ownedPlayer;
|
||||||
private BlockData blockData;
|
private BlockData blockData;
|
||||||
private boolean won;
|
private boolean won;
|
||||||
|
|
@ -52,6 +52,10 @@ public class SingleDuelsGenerator extends ParkourGenerator {
|
||||||
state.specialChances.clear();
|
state.specialChances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOwningGenerator(@NotNull DuelsGenerator generator) {
|
||||||
|
this.owningGenerator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
public void setOwnedPlayer(@NotNull ParkourPlayer pp) {
|
public void setOwnedPlayer(@NotNull ParkourPlayer pp) {
|
||||||
this.ownedPlayer = pp;
|
this.ownedPlayer = pp;
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +99,7 @@ public class SingleDuelsGenerator extends ParkourGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkWin() {
|
private void checkWin() {
|
||||||
if (!won && owningGenerator != null && state.score >= owningGenerator.goal) {
|
if (!won && owningGenerator != null && state.score() >= owningGenerator.goal) {
|
||||||
won = true;
|
won = true;
|
||||||
owningGenerator.win(ownedPlayer);
|
owningGenerator.win(ownedPlayer);
|
||||||
}
|
}
|
||||||
|
|
@ -103,6 +107,6 @@ public class SingleDuelsGenerator extends ParkourGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.DUELS;
|
return Modes.duels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class CloseMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof CloseMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof CloseMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class ElytraGenerator extends ParkourGenerator {
|
||||||
private static final double GENERATE_DISTANCE = 6.0;
|
private static final double GENERATE_DISTANCE = 6.0;
|
||||||
public final Map<Integer, ElytraSection> sections = new LinkedHashMap<>();
|
public final Map<Integer, ElytraSection> sections = new LinkedHashMap<>();
|
||||||
public final ClientBlockChanger blockChanger = new ClientBlockChanger();
|
public final ClientBlockChanger blockChanger = new ClientBlockChanger();
|
||||||
protected double movementScore = 0.0;
|
private double movementScore = 0.0;
|
||||||
private Long startTime = null;
|
private Long startTime = null;
|
||||||
private Vector playerSpawn;
|
private Vector playerSpawn;
|
||||||
private Vector blockSpawn;
|
private Vector blockSpawn;
|
||||||
|
|
@ -133,7 +133,7 @@ public class ElytraGenerator extends ParkourGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.ELYTRA;
|
return Modes.elytra();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getElytraScore() {
|
public int getElytraScore() {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class ElytraMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof ElytraMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof ElytraMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class MinSpeedMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof MinSpeedMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof MinSpeedMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ public class ObstacleGenerator extends ElytraGenerator {
|
||||||
private static final int OBSTACLE_KNOT = 3;
|
private static final int OBSTACLE_KNOT = 3;
|
||||||
|
|
||||||
private final Map<Integer, List<Vector>> obstacles = new HashMap<>();
|
private final Map<Integer, List<Vector>> obstacles = new HashMap<>();
|
||||||
private final int radius;
|
private final int obstacleRadius;
|
||||||
|
|
||||||
public ObstacleGenerator(Session session) {
|
public ObstacleGenerator(Session session) {
|
||||||
super(session);
|
super(session);
|
||||||
radius = Config.GENERATION.getInt("elytra.radius", DEFAULT_RADIUS);
|
obstacleRadius = Config.GENERATION.getInt("elytra.radius", DEFAULT_RADIUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -48,7 +48,7 @@ public class ObstacleGenerator extends ElytraGenerator {
|
||||||
|
|
||||||
private void generateObstacle(int idx, ElytraSection section, int knotIdx) {
|
private void generateObstacle(int idx, ElytraSection section, int knotIdx) {
|
||||||
final Obstacle obstacle = Obstacle.values()[ThreadLocalRandom.current().nextInt(Obstacle.values().length)];
|
final Obstacle obstacle = Obstacle.values()[ThreadLocalRandom.current().nextInt(Obstacle.values().length)];
|
||||||
final List<Vector> points = obstacle.getPoints(section.getKnot(knotIdx), radius);
|
final List<Vector> points = obstacle.getPoints(section.getKnot(knotIdx), obstacleRadius);
|
||||||
if (points.isEmpty()) {
|
if (points.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class ObstacleMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof ObstacleMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof ObstacleMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class SpeedDemonMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof SpeedDemonMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof SpeedDemonMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -42,8 +43,8 @@ public class TimeTrialMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof TimeTrialMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof TimeTrialMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import dev.loki.loparkour.leaderboard.model.Score;
|
||||||
import dev.loki.loparkour.mode.base.Mode;
|
import dev.loki.loparkour.mode.base.Mode;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages generator statistics and time formatting.
|
* Manages generator statistics and time formatting.
|
||||||
|
|
@ -62,15 +64,15 @@ public class GeneratorStatistics {
|
||||||
for (final ParkourPlayer player : generator.getPlayers()) {
|
for (final ParkourPlayer player : generator.getPlayers()) {
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
if (generator.state.score > record) {
|
if (generator.state.score() > record) {
|
||||||
// New record
|
// New record
|
||||||
message = Locales.getString(player.player, "parkour.fall.new-record")
|
message = Locales.getString(player.player, "parkour.fall.new-record")
|
||||||
.replace("%score%", String.valueOf(generator.state.score))
|
.replace("%score%", String.valueOf(generator.state.score()))
|
||||||
.replace("%time%", getFormattedTime());
|
.replace("%time%", getFormattedTime());
|
||||||
} else {
|
} else {
|
||||||
// Regular fall
|
// Regular fall
|
||||||
message = Locales.getString(player.player, "parkour.fall.regular")
|
message = Locales.getString(player.player, "parkour.fall.regular")
|
||||||
.replace("%score%", String.valueOf(generator.state.score))
|
.replace("%score%", String.valueOf(generator.state.score()))
|
||||||
.replace("%record%", String.valueOf(record))
|
.replace("%record%", String.valueOf(record))
|
||||||
.replace("%time%", getFormattedTime());
|
.replace("%time%", getFormattedTime());
|
||||||
}
|
}
|
||||||
|
|
@ -81,11 +83,12 @@ public class GeneratorStatistics {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String getTime(@NotNull String format) {
|
private String getTime(@NotNull String format) {
|
||||||
if (generator.state.start == null) {
|
final @Nullable Instant start = generator.state.start();
|
||||||
|
if (start == null) {
|
||||||
return "00:00.000";
|
return "00:00.000";
|
||||||
}
|
}
|
||||||
|
|
||||||
final long elapsed = java.time.Duration.between(generator.state.start, java.time.Instant.now()).toMillis();
|
final long elapsed = Duration.between(start, Instant.now()).toMillis();
|
||||||
final Duration d = Duration.ofMillis(elapsed);
|
final Duration d = Duration.ofMillis(elapsed);
|
||||||
|
|
||||||
if ("HH:mm:ss.SSS".equals(format)) {
|
if ("HH:mm:ss.SSS".equals(format)) {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public class ParkourGenerator {
|
||||||
if (selection == null || selection.length < 2) {
|
if (selection == null || selection.length < 2) {
|
||||||
throw new IllegalArgumentException("Invalid session zone: selection must contain at least 2 locations");
|
throw new IllegalArgumentException("Invalid session zone: selection must contain at least 2 locations");
|
||||||
}
|
}
|
||||||
this.state.zone = selection;
|
this.state.setZone(selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize components
|
// Initialize components
|
||||||
|
|
@ -132,7 +132,7 @@ public class ParkourGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.DEFAULT;
|
return Modes.defaultMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import org.bukkit.Location;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -14,36 +15,30 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds all state data for parkour generation.
|
* Holds all state data for parkour generation.
|
||||||
* No logic - just data and getters/setters.
|
* No logic - just data and accessors.
|
||||||
*/
|
*/
|
||||||
public class GeneratorState {
|
public class GeneratorState {
|
||||||
|
|
||||||
// Score state
|
// Score state
|
||||||
public int score = 0;
|
private int score;
|
||||||
public int totalScore = 0;
|
private boolean stopped;
|
||||||
public int schematicCooldown;
|
|
||||||
public boolean stopped = false;
|
|
||||||
|
|
||||||
// Location state
|
// Location state — seeded before the first jump is calculated: the zone by ParkourGenerator,
|
||||||
public Location blockSpawn;
|
// the heading by Island, the spawn by BlockPlacer, so readers treat all three as non-null.
|
||||||
public Location playerSpawn;
|
private Location playerSpawn;
|
||||||
public Location lastStandingPlayerLocation;
|
private Vector heading;
|
||||||
public Vector heading;
|
private Location[] zone; // [min, max] bounding box for generation area
|
||||||
public Location[] zone; // [min, max] bounding box for generation area
|
|
||||||
|
|
||||||
// Time state
|
// Time state — null until the first block is scored.
|
||||||
public Instant start;
|
private @Nullable Instant start;
|
||||||
|
|
||||||
// Block history — all access is main-thread-only (Bukkit scheduler runTimer), so no sync wrapper needed.
|
// Block history — all access is main-thread-only (Bukkit scheduler runTimer), so no sync wrapper needed.
|
||||||
// Iteration is safe without external synchronization as all mutations happen on the same thread.
|
// Iteration is safe without external synchronization as all mutations happen on the same thread.
|
||||||
public final List<Block> history = new ArrayList<>();
|
public final List<Block> history = new ArrayList<>();
|
||||||
public int lastPositionIndexPlayer = -1;
|
private int generatedBlocks;
|
||||||
public int generatedBlocks = 0;
|
|
||||||
|
|
||||||
// Schematic state
|
// Schematic state — null while no schematic is pasted.
|
||||||
public boolean deleteSchematic = false;
|
private @Nullable List<Block> schematicBlocks;
|
||||||
public boolean waitForSchematicCompletion = false;
|
|
||||||
public List<Block> schematicBlocks = new ArrayList<>();
|
|
||||||
|
|
||||||
// Chance maps
|
// Chance maps
|
||||||
public final Map<Integer, Double> distanceChances = new HashMap<>();
|
public final Map<Integer, Double> distanceChances = new HashMap<>();
|
||||||
|
|
@ -51,21 +46,78 @@ public class GeneratorState {
|
||||||
public final Map<BlockData, Double> specialChances = new HashMap<>();
|
public final Map<BlockData, Double> specialChances = new HashMap<>();
|
||||||
public final Map<ParkourGenerator.BlockGenerationType, Double> defaultChances = new HashMap<>();
|
public final Map<ParkourGenerator.BlockGenerationType, Double> defaultChances = new HashMap<>();
|
||||||
|
|
||||||
public GeneratorState() { }
|
public int score() {
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScore(int score) {
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zeroes the score and restarts the timer. */
|
||||||
public void resetScore() {
|
public void resetScore() {
|
||||||
score = 0;
|
score = 0;
|
||||||
start = null;
|
start = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearHistory() {
|
public boolean stopped() {
|
||||||
history.clear();
|
return stopped;
|
||||||
lastPositionIndexPlayer = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetSchematicState() {
|
public void setStopped(boolean stopped) {
|
||||||
waitForSchematicCompletion = false;
|
this.stopped = stopped;
|
||||||
deleteSchematic = true;
|
}
|
||||||
schematicBlocks.clear();
|
|
||||||
|
public Location playerSpawn() {
|
||||||
|
return playerSpawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayerSpawn(Location playerSpawn) {
|
||||||
|
this.playerSpawn = playerSpawn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector heading() {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeading(Vector heading) {
|
||||||
|
this.heading = heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Location[] zone() {
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZone(Location[] zone) {
|
||||||
|
this.zone = zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Instant start() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStart(Instant start) {
|
||||||
|
this.start = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int generatedBlocks() {
|
||||||
|
return generatedBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneratedBlocks(int generatedBlocks) {
|
||||||
|
this.generatedBlocks = generatedBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Counts blocks placed by one generation step (a schematic may place several at once). */
|
||||||
|
public void addGeneratedBlocks(int count) {
|
||||||
|
generatedBlocks += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable List<Block> schematicBlocks() {
|
||||||
|
return schematicBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchematicBlocks(@Nullable List<Block> schematicBlocks) {
|
||||||
|
this.schematicBlocks = schematicBlocks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -30,7 +29,7 @@ public final class Island {
|
||||||
|
|
||||||
public final Session session;
|
public final Session session;
|
||||||
|
|
||||||
public List<Block> blocks;
|
private List<Block> blocks;
|
||||||
|
|
||||||
public Island(@NotNull Session session) {
|
public Island(@NotNull Session session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
|
@ -50,10 +49,9 @@ public final class Island {
|
||||||
final int hx = face.getModX();
|
final int hx = face.getModX();
|
||||||
final int hz = face.getModZ();
|
final int hz = face.getModZ();
|
||||||
|
|
||||||
if (session.generator.state.heading == null) {
|
if (session.generator().state.heading() == null) {
|
||||||
session.generator.state.heading = face.getDirection();
|
session.generator().state.setHeading(face.getDirection());
|
||||||
}
|
}
|
||||||
final Vector h = session.generator.state.heading;
|
|
||||||
|
|
||||||
String firstBlockStr = Config.GENERATION.getString("advanced.island.parkour.first-block-material");
|
String firstBlockStr = Config.GENERATION.getString("advanced.island.parkour.first-block-material");
|
||||||
if (firstBlockStr.isBlank()) {
|
if (firstBlockStr.isBlank()) {
|
||||||
|
|
@ -85,12 +83,12 @@ public final class Island {
|
||||||
|
|
||||||
final Location parkourStart = world.getBlockAt(qx, qy, qz).getLocation();
|
final Location parkourStart = world.getBlockAt(qx, qy, qz).getLocation();
|
||||||
|
|
||||||
session.generator.generateFirst(ps, parkourStart);
|
session.generator().generateFirst(ps, parkourStart);
|
||||||
session.generator.startTick();
|
session.generator().startTick();
|
||||||
session.getPlayers().forEach(pp -> pp.setup(ps));
|
session.getPlayers().forEach(pp -> pp.setup(ps));
|
||||||
|
|
||||||
if (LoParkour.getGhostService() != null) {
|
if (LoParkour.getGhostService() != null) {
|
||||||
LoParkour.getGhostService().startRun(session.generator);
|
LoParkour.getGhostService().startRun(session.generator());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blocks = placed;
|
this.blocks = placed;
|
||||||
|
|
|
||||||
|
|
@ -51,16 +51,16 @@ public class JumpCalculator {
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public Block calculateNextBlock(@NotNull Block current, int distance, int height) {
|
public Block calculateNextBlock(@NotNull Block current, int distance, int height) {
|
||||||
if (generator.state.zone == null || generator.state.zone.length < 2) {
|
if (generator.state.zone() == null || generator.state.zone().length < 2) {
|
||||||
throw new IllegalStateException("Generator zone not initialized");
|
throw new IllegalStateException("Generator zone not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
final JumpDirector director = new JumpDirector(
|
final JumpDirector director = new JumpDirector(
|
||||||
BoundingBox.of(generator.state.zone[0], generator.state.zone[1]),
|
BoundingBox.of(generator.state.zone()[0], generator.state.zone()[1]),
|
||||||
current.getLocation().toVector()
|
current.getLocation().toVector()
|
||||||
);
|
);
|
||||||
|
|
||||||
generator.state.heading = director.getRecommendedHeading(generator.state.heading);
|
generator.state.setHeading(director.getRecommendedHeading(generator.state.heading()));
|
||||||
int resolvedHeight = director.getRecommendedHeight(height);
|
int resolvedHeight = director.getRecommendedHeight(height);
|
||||||
|
|
||||||
final JumpConstraints constraints = calculateJumpConstraints(current);
|
final JumpConstraints constraints = calculateJumpConstraints(current);
|
||||||
|
|
@ -76,7 +76,7 @@ public class JumpCalculator {
|
||||||
|
|
||||||
final double randomOffsetSd = Arrays.asList(generator.generatorOptions)
|
final double randomOffsetSd = Arrays.asList(generator.generatorOptions)
|
||||||
.contains(GeneratorOption.REDUCE_RANDOM_BLOCK_SELECTION_ANGLE) ? RANDOM_OFFSET_SD : DEFAULT_RANDOM_OFFSET_SD;
|
.contains(GeneratorOption.REDUCE_RANDOM_BLOCK_SELECTION_ANGLE) ? RANDOM_OFFSET_SD : DEFAULT_RANDOM_OFFSET_SD;
|
||||||
final Location candidate = validateAndRetryJump(current.getLocation(), generator.state.heading,
|
final Location candidate = validateAndRetryJump(current.getLocation(), generator.state.heading(),
|
||||||
resolvedDistance, resolvedHeight, ThreadLocalRandom.current(), randomOffsetSd);
|
resolvedDistance, resolvedHeight, ThreadLocalRandom.current(), randomOffsetSd);
|
||||||
return candidate.getBlock();
|
return candidate.getBlock();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,8 @@ public class BlockPlacer {
|
||||||
|
|
||||||
worldPlacer.place(block, blockData);
|
worldPlacer.place(block, blockData);
|
||||||
generator.state.history.add(block);
|
generator.state.history.add(block);
|
||||||
generator.state.generatedBlocks++;
|
generator.state.addGeneratedBlocks(1);
|
||||||
generator.state.playerSpawn = spawn;
|
generator.state.setPlayerSpawn(spawn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,7 +102,7 @@ public class BlockPlacer {
|
||||||
final Block selectedBlock = blocks.get(0);
|
final Block selectedBlock = blocks.get(0);
|
||||||
worldPlacer.place(selectedBlock, blockData);
|
worldPlacer.place(selectedBlock, blockData);
|
||||||
generator.state.history.add(selectedBlock);
|
generator.state.history.add(selectedBlock);
|
||||||
generator.state.generatedBlocks++;
|
generator.state.addGeneratedBlocks(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void placeSpecialBlock() {
|
private void placeSpecialBlock() {
|
||||||
|
|
@ -121,6 +121,6 @@ public class BlockPlacer {
|
||||||
final Block selectedBlock = blocks.get(0);
|
final Block selectedBlock = blocks.get(0);
|
||||||
worldPlacer.place(selectedBlock, specialBlockData);
|
worldPlacer.place(selectedBlock, specialBlockData);
|
||||||
generator.state.history.add(selectedBlock);
|
generator.state.history.add(selectedBlock);
|
||||||
generator.state.generatedBlocks++;
|
generator.state.addGeneratedBlocks(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ import org.bukkit.Sound;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies block data to the world and plays placement effects for all players.
|
* Applies block data to the world and plays placement effects for all players.
|
||||||
|
|
@ -45,9 +48,11 @@ class BlockWorldPlacer {
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteSchematic() {
|
void deleteSchematic() {
|
||||||
if (generator.state.schematicBlocks != null) {
|
final @Nullable List<Block> blocks = generator.state.schematicBlocks();
|
||||||
generator.state.schematicBlocks.forEach(block -> block.setType(Material.AIR));
|
if (blocks == null) {
|
||||||
generator.state.schematicBlocks.clear();
|
return;
|
||||||
}
|
}
|
||||||
|
blocks.forEach(block -> block.setType(Material.AIR));
|
||||||
|
blocks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class SchematicBlockPlacer {
|
||||||
}
|
}
|
||||||
|
|
||||||
final double maxDifficulty = generator.getPlayers().stream()
|
final double maxDifficulty = generator.getPlayers().stream()
|
||||||
.mapToDouble(p -> p.schematicDifficulty)
|
.mapToDouble(p -> p.settings().schematicDifficulty())
|
||||||
.min()
|
.min()
|
||||||
.orElse(1.0);
|
.orElse(1.0);
|
||||||
|
|
||||||
|
|
@ -54,8 +54,8 @@ public class SchematicBlockPlacer {
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.state.history.addAll(schematicBlocks);
|
generator.state.history.addAll(schematicBlocks);
|
||||||
generator.state.generatedBlocks += schematicBlocks.size();
|
generator.state.addGeneratedBlocks(schematicBlocks.size());
|
||||||
generator.state.schematicBlocks = schematicBlocks;
|
generator.state.setSchematicBlocks(schematicBlocks);
|
||||||
|
|
||||||
for (final ParkourPlayer player : generator.getPlayers()) {
|
for (final ParkourPlayer player : generator.getPlayers()) {
|
||||||
new ParkourSchematicGenerateEvent(schematic, generator, player).call();
|
new ParkourSchematicGenerateEvent(schematic, generator, player).call();
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import dev.loki.loparkour.reward.core.Rewards;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -55,14 +54,14 @@ public class LifecycleEventManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRewards() {
|
private void checkRewards() {
|
||||||
if (!Rewards.REWARDS_ENABLED) {
|
if (!Rewards.enabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Mode mode = generator.getMode();
|
final Mode mode = generator.getMode();
|
||||||
|
|
||||||
for (final ParkourPlayer player : generator.getPlayers()) {
|
for (final ParkourPlayer player : generator.getPlayers()) {
|
||||||
final int score = generator.state.score;
|
final int score = generator.state.score();
|
||||||
applyScoreRewards(player, mode, score);
|
applyScoreRewards(player, mode, score);
|
||||||
applyIntervalRewards(player, mode, score);
|
applyIntervalRewards(player, mode, score);
|
||||||
applyOneTimeRewards(player, mode, score);
|
applyOneTimeRewards(player, mode, score);
|
||||||
|
|
@ -70,16 +69,16 @@ public class LifecycleEventManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyScoreRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
private void applyScoreRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
||||||
if (!Rewards.SCORE_REWARDS.containsKey(score)) {
|
if (!Rewards.scoreRewards().containsKey(score)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (final Reward reward : Rewards.SCORE_REWARDS.get(score)) {
|
for (final Reward reward : Rewards.scoreRewards().get(score)) {
|
||||||
reward.execute(player, mode);
|
reward.execute(player, mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyIntervalRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
private void applyIntervalRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
||||||
for (final Map.Entry<Integer, List<Reward>> entry : Rewards.INTERVAL_REWARDS.entrySet()) {
|
for (final Map.Entry<Integer, List<Reward>> entry : Rewards.intervalRewards().entrySet()) {
|
||||||
if (score > 0 && score % entry.getKey() == 0) {
|
if (score > 0 && score % entry.getKey() == 0) {
|
||||||
for (final Reward reward : entry.getValue()) {
|
for (final Reward reward : entry.getValue()) {
|
||||||
reward.execute(player, mode);
|
reward.execute(player, mode);
|
||||||
|
|
@ -89,35 +88,32 @@ public class LifecycleEventManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyOneTimeRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
private void applyOneTimeRewards(@NotNull ParkourPlayer player, @NotNull Mode mode, int score) {
|
||||||
if (!Rewards.ONE_TIME_REWARDS.containsKey(score)) {
|
if (!Rewards.oneTimeRewards().containsKey(score)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (player.collectedRewards == null) {
|
|
||||||
player.collectedRewards = new ArrayList<>();
|
|
||||||
}
|
|
||||||
final String rewardKey = String.valueOf(score);
|
final String rewardKey = String.valueOf(score);
|
||||||
if (player.collectedRewards.contains(rewardKey)) {
|
if (player.settings().collectedRewards().contains(rewardKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (final Reward reward : Rewards.ONE_TIME_REWARDS.get(score)) {
|
for (final Reward reward : Rewards.oneTimeRewards().get(score)) {
|
||||||
reward.execute(player, mode);
|
reward.execute(player, mode);
|
||||||
}
|
}
|
||||||
player.collectedRewards.add(rewardKey);
|
player.settings().collectedRewards().add(rewardKey);
|
||||||
player.save(false);
|
player.save(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processFall() {
|
private void processFall() {
|
||||||
// Record statistics
|
// Record statistics
|
||||||
final int score = generator.state.score;
|
final int score = generator.state.score();
|
||||||
final String time = generator.getFormattedTime();
|
final String time = generator.getFormattedTime();
|
||||||
final String difficulty = String.valueOf(generator.getDifficultyScore());
|
final String difficulty = String.valueOf(generator.getDifficultyScore());
|
||||||
|
|
||||||
generator.registerScore(time, difficulty, generator.state.score);
|
generator.registerScore(time, difficulty, generator.state.score());
|
||||||
|
|
||||||
// Teleport players back to spawn before reset
|
// Teleport players back to spawn before reset
|
||||||
for (final ParkourPlayer player : generator.getPlayers()) {
|
for (final ParkourPlayer player : generator.getPlayers()) {
|
||||||
if (generator.state.playerSpawn != null) {
|
if (generator.state.playerSpawn() != null) {
|
||||||
player.teleport(generator.state.playerSpawn);
|
player.teleport(generator.state.playerSpawn());
|
||||||
}
|
}
|
||||||
// Clear scored blocks so same coordinates can be re-scored after respawn
|
// Clear scored blocks so same coordinates can be re-scored after respawn
|
||||||
player.clearScoredBlocks();
|
player.clearScoredBlocks();
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,12 @@ public class LifecycleTickManager {
|
||||||
* Check if any player fell below the threshold.
|
* Check if any player fell below the threshold.
|
||||||
*/
|
*/
|
||||||
private void checkPlayerFall() {
|
private void checkPlayerFall() {
|
||||||
if (generator.state.playerSpawn == null) {
|
if (generator.state.playerSpawn() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Block latest = generator.getLatest();
|
final Block latest = generator.getLatest();
|
||||||
final double referenceY = latest != null ? latest.getY() : generator.state.playerSpawn.getY();
|
final double referenceY = latest != null ? latest.getY() : generator.state.playerSpawn().getY();
|
||||||
final double fallThreshold = referenceY - FALL_THRESHOLD_OFFSET;
|
final double fallThreshold = referenceY - FALL_THRESHOLD_OFFSET;
|
||||||
|
|
||||||
for (final var player : generator.getPlayers()) {
|
for (final var player : generator.getPlayers()) {
|
||||||
|
|
@ -92,8 +92,8 @@ public class LifecycleTickManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maintainBlockLead() {
|
private void maintainBlockLead() {
|
||||||
final int currentBlocks = generator.state.generatedBlocks;
|
final int currentBlocks = generator.state.generatedBlocks();
|
||||||
final int targetBlocks = generator.state.score + generationLead();
|
final int targetBlocks = generator.state.score() + generationLead();
|
||||||
|
|
||||||
if (currentBlocks < targetBlocks) {
|
if (currentBlocks < targetBlocks) {
|
||||||
final int needed = targetBlocks - currentBlocks;
|
final int needed = targetBlocks - currentBlocks;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class LifecycleVisualUpdater {
|
||||||
|
|
||||||
private void updatePlayerVisuals(@NotNull ParkourPlayer player) {
|
private void updatePlayerVisuals(@NotNull ParkourPlayer player) {
|
||||||
// Update scoreboard if enabled
|
// Update scoreboard if enabled
|
||||||
if (player.showScoreboard != null && player.showScoreboard) {
|
if (player.settings().showScoreboard() != null && player.settings().showScoreboard()) {
|
||||||
player.updateScoreboard(generator);
|
player.updateScoreboard(generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
@ -98,7 +99,7 @@ public class GeneratorCleanup {
|
||||||
* The end block is kept in history so the landing player can score it.
|
* The end block is kept in history so the landing player can score it.
|
||||||
*/
|
*/
|
||||||
public void removeSchematicBlocks() {
|
public void removeSchematicBlocks() {
|
||||||
final List<Block> schematicBlocks = generator.state.schematicBlocks;
|
final @Nullable List<Block> schematicBlocks = generator.state.schematicBlocks();
|
||||||
if (schematicBlocks == null || schematicBlocks.isEmpty()) {
|
if (schematicBlocks == null || schematicBlocks.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +116,7 @@ public class GeneratorCleanup {
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.state.history.removeIf(remove::contains);
|
generator.state.history.removeIf(remove::contains);
|
||||||
generator.state.schematicBlocks = null;
|
generator.state.setSchematicBlocks(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -127,9 +128,8 @@ public class GeneratorCleanup {
|
||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
generator.state.history.clear();
|
generator.state.history.clear();
|
||||||
generator.state.generatedBlocks = 0;
|
generator.state.setGeneratedBlocks(0);
|
||||||
generator.state.score = 0;
|
generator.state.resetScore();
|
||||||
generator.state.start = null;
|
|
||||||
|
|
||||||
if (regenerate) {
|
if (regenerate) {
|
||||||
regenerateFirstBlock();
|
regenerateFirstBlock();
|
||||||
|
|
@ -145,15 +145,16 @@ public class GeneratorCleanup {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearSchematicBlocks() {
|
private void clearSchematicBlocks() {
|
||||||
if (generator.state.schematicBlocks == null) {
|
final @Nullable List<Block> blocks = generator.state.schematicBlocks();
|
||||||
|
if (blocks == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clearBlocks(generator.state.schematicBlocks);
|
clearBlocks(blocks);
|
||||||
generator.state.schematicBlocks.clear();
|
blocks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void regenerateFirstBlock() {
|
private void regenerateFirstBlock() {
|
||||||
final Location spawn = generator.state.playerSpawn;
|
final Location spawn = generator.state.playerSpawn();
|
||||||
if (spawn == null) {
|
if (spawn == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,11 @@ public class PlayerInteractionHandler {
|
||||||
*/
|
*/
|
||||||
public void handleScore() {
|
public void handleScore() {
|
||||||
// Start the timer on the very first scored block
|
// Start the timer on the very first scored block
|
||||||
if (generator.state.start == null) {
|
if (generator.state.start() == null) {
|
||||||
generator.state.start = Instant.now();
|
generator.state.setStart(Instant.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
generator.state.score++;
|
generator.state.setScore(generator.state.score() + 1);
|
||||||
eventManager.handleScore();
|
eventManager.handleScore();
|
||||||
|
|
||||||
// Fire event listeners for adaptive system
|
// Fire event listeners for adaptive system
|
||||||
|
|
@ -127,8 +127,8 @@ public class PlayerInteractionHandler {
|
||||||
|
|
||||||
/** Check if block is any schematic block (not just the last one). */
|
/** Check if block is any schematic block (not just the last one). */
|
||||||
private boolean isOnSchematicBlock(@NotNull Block block) {
|
private boolean isOnSchematicBlock(@NotNull Block block) {
|
||||||
return generator.state.schematicBlocks != null
|
final @Nullable List<Block> blocks = generator.state.schematicBlocks();
|
||||||
&& historyContains(generator.state.schematicBlocks, block);
|
return blocks != null && historyContains(blocks, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSchematicEndBlock() {
|
private void handleSchematicEndBlock() {
|
||||||
|
|
@ -139,7 +139,7 @@ public class PlayerInteractionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSchematicEndBlock(@NotNull Block block) {
|
private boolean isSchematicEndBlock(@NotNull Block block) {
|
||||||
final List<Block> schematicBlocks = generator.state.schematicBlocks;
|
final @Nullable List<Block> schematicBlocks = generator.state.schematicBlocks();
|
||||||
if (schematicBlocks == null || schematicBlocks.isEmpty()) {
|
if (schematicBlocks == null || schematicBlocks.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class GhostService {
|
||||||
private boolean recordingEnabled(ParkourPlayer pp) {
|
private boolean recordingEnabled(ParkourPlayer pp) {
|
||||||
return serverEnabled()
|
return serverEnabled()
|
||||||
&& !replaying.contains(pp.getUUID())
|
&& !replaying.contains(pp.getUUID())
|
||||||
&& Boolean.TRUE.equals(pp.ghostRecording);
|
&& Boolean.TRUE.equals(pp.settings().ghostRecording());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startRun(ParkourGenerator generator) {
|
public void startRun(ParkourGenerator generator) {
|
||||||
|
|
@ -79,11 +79,11 @@ public class GhostService {
|
||||||
return replaying.contains(uuid);
|
return replaying.contains(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReplaying(UUID uuid, boolean replaying) {
|
public void setReplaying(UUID uuid, boolean active) {
|
||||||
if (replaying) {
|
if (active) {
|
||||||
this.replaying.add(uuid);
|
replaying.add(uuid);
|
||||||
} else {
|
} else {
|
||||||
this.replaying.remove(uuid);
|
replaying.remove(uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ public class GlobalPlaceholderResolver {
|
||||||
return LoParkour.getPlugin().getDescription().getVersion();
|
return LoParkour.getPlugin().getDescription().getVersion();
|
||||||
}
|
}
|
||||||
case "leader", "record_player" -> {
|
case "leader", "record_player" -> {
|
||||||
final Score score = Modes.DEFAULT.getLeaderboard().getScoreAtRank(1);
|
final Score score = Modes.defaultMode().getLeaderboard().getScoreAtRank(1);
|
||||||
return score != null ? score.name() : "?";
|
return score != null ? score.name() : "?";
|
||||||
}
|
}
|
||||||
case "leader_score", "record_score", "record" -> {
|
case "leader_score", "record_score", "record" -> {
|
||||||
final Score score = Modes.DEFAULT.getLeaderboard().getScoreAtRank(1);
|
final Score score = Modes.defaultMode().getLeaderboard().getScoreAtRank(1);
|
||||||
return score != null ? Integer.toString(score.score()) : "?";
|
return score != null ? Integer.toString(score.score()) : "?";
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ public class PlayerPlaceholderResolver {
|
||||||
private String resolveLeaderboard(Player player, String params) {
|
private String resolveLeaderboard(Player player, String params) {
|
||||||
switch (params) {
|
switch (params) {
|
||||||
case "rank" -> {
|
case "rank" -> {
|
||||||
return Integer.toString(Modes.DEFAULT.getLeaderboard().getRank(player.getUniqueId()));
|
return Integer.toString(Modes.defaultMode().getLeaderboard().getRank(player.getUniqueId()));
|
||||||
}
|
}
|
||||||
case "highscore", "high_score" -> {
|
case "highscore", "high_score" -> {
|
||||||
return Integer.toString(Modes.DEFAULT.getLeaderboard().get(player.getUniqueId()).score());
|
return Integer.toString(Modes.defaultMode().getLeaderboard().get(player.getUniqueId()).score());
|
||||||
}
|
}
|
||||||
case "high_score_time" -> {
|
case "high_score_time" -> {
|
||||||
return Modes.DEFAULT.getLeaderboard().get(player.getUniqueId()).time();
|
return Modes.defaultMode().getLeaderboard().get(player.getUniqueId()).time();
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -38,7 +38,7 @@ public class PlayerPlaceholderResolver {
|
||||||
private String resolveSession(Player player, String params) {
|
private String resolveSession(Player player, String params) {
|
||||||
final ParkourUser user = ParkourUser.getUser(player);
|
final ParkourUser user = ParkourUser.getUser(player);
|
||||||
final ParkourPlayer pp = extractParkourPlayer(user);
|
final ParkourPlayer pp = extractParkourPlayer(user);
|
||||||
if (pp == null || pp.session.generator == null) {
|
if (pp == null || pp.session.generator() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return resolveGenerator(pp, params);
|
return resolveGenerator(pp, params);
|
||||||
|
|
@ -49,7 +49,7 @@ public class PlayerPlaceholderResolver {
|
||||||
return (ParkourPlayer) user;
|
return (ParkourPlayer) user;
|
||||||
}
|
}
|
||||||
if (user instanceof ParkourSpectator) {
|
if (user instanceof ParkourSpectator) {
|
||||||
return ((ParkourSpectator) user).closest;
|
return ((ParkourSpectator) user).closest();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -57,28 +57,28 @@ public class PlayerPlaceholderResolver {
|
||||||
private String resolveGenerator(ParkourPlayer pp, String params) {
|
private String resolveGenerator(ParkourPlayer pp, String params) {
|
||||||
switch (params) {
|
switch (params) {
|
||||||
case "score", "current_score" -> {
|
case "score", "current_score" -> {
|
||||||
return Integer.toString(pp.session.generator.state.score);
|
return Integer.toString(pp.session.generator().state.score());
|
||||||
}
|
}
|
||||||
case "time", "current_time" -> {
|
case "time", "current_time" -> {
|
||||||
return pp.session.generator.getFormattedTime();
|
return pp.session.generator().getFormattedTime();
|
||||||
}
|
}
|
||||||
case "blocklead", "lead" -> {
|
case "blocklead", "lead" -> {
|
||||||
return Integer.toString(pp.blockLead);
|
return Integer.toString(pp.settings().blockLead());
|
||||||
}
|
}
|
||||||
case "style" -> {
|
case "style" -> {
|
||||||
return pp.style;
|
return pp.settings().style();
|
||||||
}
|
}
|
||||||
case "time_pref", "time_preference" -> {
|
case "time_pref", "time_preference" -> {
|
||||||
return Integer.toString(pp.selectedTime);
|
return Integer.toString(pp.settings().selectedTime());
|
||||||
}
|
}
|
||||||
case "scoreboard" -> {
|
case "scoreboard" -> {
|
||||||
return pp.showScoreboard.toString();
|
return pp.settings().showScoreboard().toString();
|
||||||
}
|
}
|
||||||
case "difficulty" -> {
|
case "difficulty" -> {
|
||||||
return Double.toString(pp.schematicDifficulty);
|
return Double.toString(pp.settings().schematicDifficulty());
|
||||||
}
|
}
|
||||||
case "difficulty_string" -> {
|
case "difficulty_string" -> {
|
||||||
return formatters.parseDifficulty(pp.schematicDifficulty);
|
return formatters.parseDifficulty(pp.settings().schematicDifficulty());
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
return resolveScoreUntil(pp, params);
|
return resolveScoreUntil(pp, params);
|
||||||
|
|
@ -94,7 +94,7 @@ public class PlayerPlaceholderResolver {
|
||||||
try {
|
try {
|
||||||
final int interval = Integer.parseInt(replaced);
|
final int interval = Integer.parseInt(replaced);
|
||||||
if (interval > 0) {
|
if (interval > 0) {
|
||||||
return Integer.toString(interval - (pp.session.generator.state.score % interval));
|
return Integer.toString(interval - (pp.session.generator().state.score() % interval));
|
||||||
}
|
}
|
||||||
return "0";
|
return "0";
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ class ScoreRankResolver {
|
||||||
final int rank = Integer.parseInt(matcher.group(2));
|
final int rank = Integer.parseInt(matcher.group(2));
|
||||||
final String name = matcher.group(1);
|
final String name = matcher.group(1);
|
||||||
final Mode mode = Registry.getMode(name);
|
final Mode mode = Registry.getMode(name);
|
||||||
final Leaderboard leaderboard = mode != null ? mode.getLeaderboard() : Modes.DEFAULT.getLeaderboard();
|
final Leaderboard leaderboard = mode != null ? mode.getLeaderboard() : Modes.defaultMode().getLeaderboard();
|
||||||
return resolveScore(leaderboard, rank, valueFunction);
|
return resolveScore(leaderboard, rank, valueFunction);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return "?";
|
return "?";
|
||||||
|
|
@ -37,7 +37,7 @@ class ScoreRankResolver {
|
||||||
private String resolveGenericRank(String rankData, Function<Score, ?> valueFunction) {
|
private String resolveGenericRank(String rankData, Function<Score, ?> valueFunction) {
|
||||||
try {
|
try {
|
||||||
final int rank = Integer.parseInt(rankData);
|
final int rank = Integer.parseInt(rankData);
|
||||||
return resolveScore(Modes.DEFAULT.getLeaderboard(), rank, valueFunction);
|
return resolveScore(Modes.defaultMode().getLeaderboard(), rank, valueFunction);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,12 +136,12 @@ public class Leaderboard {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns sorted copy of the score map.
|
* Returns sorted copy of the score map.
|
||||||
* @param sort The sorting method
|
* @param order The sorting method
|
||||||
* @return A sorted map of scores
|
* @return A sorted map of scores
|
||||||
*/
|
*/
|
||||||
public Map<UUID, Score> sort(LeaderboardSorter.Sort sort) {
|
public Map<UUID, Score> sort(LeaderboardSorter.Sort order) {
|
||||||
synchronized (scores) {
|
synchronized (scores) {
|
||||||
return sorter.sort(scores, sort);
|
return sorter.sort(scores, order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,8 +149,8 @@ public class Leaderboard {
|
||||||
* @deprecated Use {@link #sort(LeaderboardSorter.Sort)} instead
|
* @deprecated Use {@link #sort(LeaderboardSorter.Sort)} instead
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Map<UUID, Score> sort(Sort sort) {
|
public Map<UUID, Score> sort(Sort order) {
|
||||||
return sort(sort.toSorterSort());
|
return sort(order.toSorterSort());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Backward compatibility ────────────────────────────────────────────────
|
// ── Backward compatibility ────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
|
|
@ -26,10 +27,7 @@ import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
|
||||||
/**
|
/** Hotbar interactions, chat routing, focus mode and general restrictions for parkour players. */
|
||||||
* Handles hotbar item interactions, chat routing, focus mode, and
|
|
||||||
* general restrictions (drop/place/break/damage) for parkour players.
|
|
||||||
*/
|
|
||||||
public class ParkourRestrictionListener implements Listener {
|
public class ParkourRestrictionListener implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
@ -50,7 +48,7 @@ public class ParkourRestrictionListener implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
switch (user.chatType) {
|
switch (user.chatType()) {
|
||||||
case LOBBY_ONLY -> session.getUsers().forEach(
|
case LOBBY_ONLY -> session.getUsers().forEach(
|
||||||
u -> u.sendTranslated("settings.chat.formats.lobby", player.getName(), event.getMessage()));
|
u -> u.sendTranslated("settings.chat.formats.lobby", player.getName(), event.getMessage()));
|
||||||
case PLAYERS_ONLY -> session.getPlayers().forEach(
|
case PLAYERS_ONLY -> session.getPlayers().forEach(
|
||||||
|
|
@ -81,7 +79,7 @@ public class ParkourRestrictionListener implements Listener {
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
public void onInteract(PlayerInteractEvent event) {
|
public void onInteract(PlayerInteractEvent event) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp == null) {
|
if (pp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class PlayerConnectionListener implements Listener {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
|
|
||||||
if (Config.CONFIG.getBoolean("bungeecord.enabled")) {
|
if (Config.CONFIG.getBoolean("bungeecord.enabled")) {
|
||||||
Modes.DEFAULT.create(player);
|
Modes.defaultMode().create(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ public abstract class LPMenu {
|
||||||
|
|
||||||
protected String locale(@NotNull Player player) {
|
protected String locale(@NotNull Player player) {
|
||||||
final var user = dev.loki.loparkour.player.core.ParkourUser.getUser(player);
|
final var user = dev.loki.loparkour.player.core.ParkourUser.getUser(player);
|
||||||
final String loc = user != null ? user.locale : OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
final String loc = user != null ? user.locale() : OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
||||||
return (loc != null && !loc.isBlank()) ? loc : "en";
|
return (loc != null && !loc.isBlank()) ? loc : "en";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,26 @@ public final class Menus {
|
||||||
}
|
}
|
||||||
|
|
||||||
// main
|
// main
|
||||||
public static MainMenu MAIN = new MainMenu();
|
public static final MainMenu MAIN = new MainMenu();
|
||||||
|
|
||||||
// play
|
// play
|
||||||
public static PlayMenu PLAY = new PlayMenu();
|
public static final PlayMenu PLAY = new PlayMenu();
|
||||||
public static SingleMenu SINGLE = new SingleMenu();
|
public static final SingleMenu SINGLE = new SingleMenu();
|
||||||
public static SpectatorMenu SPECTATOR = new SpectatorMenu();
|
public static final SpectatorMenu SPECTATOR = new SpectatorMenu();
|
||||||
|
|
||||||
// community
|
// community
|
||||||
public static CommunityMenu COMMUNITY = new CommunityMenu();
|
public static final CommunityMenu COMMUNITY = new CommunityMenu();
|
||||||
public static LeaderboardsMenu LEADERBOARDS = new LeaderboardsMenu();
|
public static final LeaderboardsMenu LEADERBOARDS = new LeaderboardsMenu();
|
||||||
public static SingleLeaderboardMenu SINGLE_LEADERBOARD = new SingleLeaderboardMenu();
|
public static final SingleLeaderboardMenu SINGLE_LEADERBOARD = new SingleLeaderboardMenu();
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
public static SettingsMenu SETTINGS = new SettingsMenu();
|
public static final SettingsMenu SETTINGS = new SettingsMenu();
|
||||||
public static LangMenu LANG = new LangMenu();
|
public static final LangMenu LANG = new LangMenu();
|
||||||
public static ParkourSettingsMenu PARKOUR_SETTINGS = new ParkourSettingsMenu();
|
public static final ParkourSettingsMenu PARKOUR_SETTINGS = new ParkourSettingsMenu();
|
||||||
|
|
||||||
// lobby
|
// lobby
|
||||||
public static DuelsMenu DUELS = new DuelsMenu();
|
public static final DuelsMenu DUELS = new DuelsMenu();
|
||||||
public static LobbyMenu LOBBY = new LobbyMenu();
|
public static final LobbyMenu LOBBY = new LobbyMenu();
|
||||||
public static PlayerManagementMenu PLAYER_MANAGEMENT = new PlayerManagementMenu();
|
public static final PlayerManagementMenu PLAYER_MANAGEMENT = new PlayerManagementMenu();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ public class PagedMenu {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PagedMenu addToDisplay(List<Item> items) {
|
public PagedMenu addToDisplay(List<Item> menuItems) {
|
||||||
this.items = items;
|
items = menuItems;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public class DuelsMenu extends LPMenu {
|
||||||
final Session session = sessions.get(i);
|
final Session session = sessions.get(i);
|
||||||
final int slot = FIRST_SLOT + (i - startIndex);
|
final int slot = FIRST_SLOT + (i - startIndex);
|
||||||
gui = gui.setItem(slot, sessionItem(player, session), e -> {
|
gui = gui.setItem(slot, sessionItem(player, session), e -> {
|
||||||
Modes.DUELS.join(player, session);
|
Modes.duels().join(player, session);
|
||||||
player.closeInventory();
|
player.closeInventory();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +78,7 @@ public class DuelsMenu extends LPMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Session> collectOpenSessions() {
|
private List<Session> collectOpenSessions() {
|
||||||
final int maxPlayers = Modes.DUELS.getMaxPlayers();
|
final int maxPlayers = Modes.duels().getMaxPlayers();
|
||||||
final List<Session> sessions = new ArrayList<>();
|
final List<Session> sessions = new ArrayList<>();
|
||||||
for (final Session session : Divider.SECTIONS.keySet()) {
|
for (final Session session : Divider.SECTIONS.keySet()) {
|
||||||
if (isOpenForJoining(session, maxPlayers)) {
|
if (isOpenForJoining(session, maxPlayers)) {
|
||||||
|
|
@ -89,13 +89,13 @@ public class DuelsMenu extends LPMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isOpenForJoining(Session session, int maxPlayers) {
|
private boolean isOpenForJoining(Session session, int maxPlayers) {
|
||||||
return session.generator instanceof DuelsGenerator generator
|
return session.generator() instanceof DuelsGenerator generator
|
||||||
&& generator.allowJoining
|
&& generator.allowJoining()
|
||||||
&& session.getPlayers().size() < maxPlayers;
|
&& session.getPlayers().size() < maxPlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemStack sessionItem(@NotNull Player player, @NotNull Session session) {
|
private ItemStack sessionItem(@NotNull Player player, @NotNull Session session) {
|
||||||
final DuelsGenerator generator = (DuelsGenerator) session.generator;
|
final DuelsGenerator generator = (DuelsGenerator) session.generator();
|
||||||
final ItemStack item = new ItemStack(Material.PLAYER_HEAD);
|
final ItemStack item = new ItemStack(Material.PLAYER_HEAD);
|
||||||
applySkullOwner(player, item, generator.owner);
|
applySkullOwner(player, item, generator.owner);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public class LobbyMenu extends LPMenu {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String locale = user.locale;
|
final String locale = user.locale();
|
||||||
final String title = Locales.getString(locale, "lobby.name");
|
final String title = Locales.getString(locale, "lobby.name");
|
||||||
|
|
||||||
final boolean isHost = !user.session.getPlayers().isEmpty()
|
final boolean isHost = !user.session.getPlayers().isEmpty()
|
||||||
|
|
@ -60,7 +60,7 @@ public class LobbyMenu extends LPMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemStack visibilityItem(ParkourUser user) {
|
private ItemStack visibilityItem(ParkourUser user) {
|
||||||
final String locale = user.locale;
|
final String locale = user.locale();
|
||||||
final List<String> values = Locales.getStringList(locale, "lobby.visibility.values");
|
final List<String> values = Locales.getStringList(locale, "lobby.visibility.values");
|
||||||
final String label = switch (user.session.getVisibility()) {
|
final String label = switch (user.session.getVisibility()) {
|
||||||
case PUBLIC -> values.size() > 2 ? values.get(2) : "Public";
|
case PUBLIC -> values.size() > 2 ? values.get(2) : "Public";
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
import dev.loki.loparkour.util.text.ColorUtil;
|
||||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.inventory.ClickType;
|
import org.bukkit.event.inventory.ClickType;
|
||||||
|
|
@ -29,13 +30,13 @@ public class PlayerManagementMenu extends LPMenu {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open(@NotNull Player player) {
|
public void open(@NotNull Player player) {
|
||||||
final ParkourPlayer viewer = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer viewer = UserRegistry.getPlayer(player);
|
||||||
if (viewer == null) {
|
if (viewer == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var session = viewer.session;
|
final var session = viewer.session;
|
||||||
final String locale = viewer.locale;
|
final String locale = viewer.locale();
|
||||||
final String title = Locales.getString(locale, "lobby.player_management.name");
|
final String title = Locales.getString(locale, "lobby.player_management.name");
|
||||||
|
|
||||||
final List<ParkourUser> others = new ArrayList<>();
|
final List<ParkourUser> others = new ArrayList<>();
|
||||||
|
|
@ -102,7 +103,7 @@ public class PlayerManagementMenu extends LPMenu {
|
||||||
@NotNull Session session, @NotNull ParkourUser other,
|
@NotNull Session session, @NotNull ParkourUser other,
|
||||||
@NotNull ClickType click) {
|
@NotNull ClickType click) {
|
||||||
if (click == ClickType.LEFT) {
|
if (click == ClickType.LEFT) {
|
||||||
Modes.DEFAULT.create(other.player);
|
Modes.defaultMode().create(other.player);
|
||||||
other.sendTranslated("lobby.player_management.kicked");
|
other.sendTranslated("lobby.player_management.kicked");
|
||||||
viewer.sendTranslated("lobby.player_management.advice");
|
viewer.sendTranslated("lobby.player_management.advice");
|
||||||
} else if (click == ClickType.RIGHT) {
|
} else if (click == ClickType.RIGHT) {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public class SpectatorMenu extends LPMenu {
|
||||||
for (int i = startIndex; i < endIndex; i++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
final Session session = sessions.get(i);
|
final Session session = sessions.get(i);
|
||||||
final int slot = FIRST_SLOT + (i - startIndex); // Slots 10-16
|
final int slot = FIRST_SLOT + (i - startIndex); // Slots 10-16
|
||||||
gui = gui.setItem(slot, items.get(i), e -> Modes.SPECTATOR.create(player, session));
|
gui = gui.setItem(slot, items.get(i), e -> Modes.spectator().create(player, session));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPage > 0) {
|
if (currentPage > 0) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import dev.loki.loparkour.menu.core.Menus;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
import dev.loki.loparkour.util.text.ColorUtil;
|
||||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -30,12 +31,12 @@ public class LangMenu extends LPMenu {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open(@NotNull Player player) {
|
public void open(@NotNull Player player) {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp == null) {
|
if (pp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String locale = pp.locale;
|
final String locale = pp.locale();
|
||||||
final String title = Locales.getString(locale, "settings.lang.name");
|
final String title = Locales.getString(locale, "settings.lang.name");
|
||||||
|
|
||||||
final List<String> langs = new ArrayList<>(Locales.getLocaleKeys());
|
final List<String> langs = new ArrayList<>(Locales.getLocaleKeys());
|
||||||
|
|
@ -52,7 +53,7 @@ public class LangMenu extends LPMenu {
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
}
|
}
|
||||||
gui = gui.setItem(FIRST_SLOT + i, item, e -> {
|
gui = gui.setItem(FIRST_SLOT + i, item, e -> {
|
||||||
pp.locale = lang;
|
pp.setLocale(lang);
|
||||||
pp.updateHotbar(); // Update hotbar with new language
|
pp.updateHotbar(); // Update hotbar with new language
|
||||||
Menus.SETTINGS.open(player);
|
Menus.SETTINGS.open(player);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.style.core.Style;
|
import dev.loki.loparkour.style.core.Style;
|
||||||
import dev.loki.loparkour.util.text.ColorUtil;
|
import dev.loki.loparkour.util.text.ColorUtil;
|
||||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
@ -47,27 +48,27 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void open(@NotNull Player player) {
|
public void open(@NotNull Player player) {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp == null) {
|
if (pp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String locale = pp.locale;
|
final String locale = pp.locale();
|
||||||
final String title = Locales.getString(locale, "settings.name");
|
final String title = Locales.getString(locale, "settings.name");
|
||||||
|
|
||||||
baseGui(title, ROWS_MAIN)
|
baseGui(title, ROWS_MAIN)
|
||||||
.setItem(SLOT_PARTICLES, toggleItem(locale, "particles", pp.particles),
|
.setItem(SLOT_PARTICLES, toggleItem(locale, "particles", pp.settings().particles()),
|
||||||
e -> toggleSetting(pp, () -> pp.particles = !pp.particles))
|
e -> toggleSetting(pp, () -> pp.settings().setParticles(!pp.settings().particles())))
|
||||||
.setItem(SLOT_SOUND, toggleItem(locale, "sound", pp.sound),
|
.setItem(SLOT_SOUND, toggleItem(locale, "sound", pp.settings().sound()),
|
||||||
e -> toggleSetting(pp, () -> pp.sound = !pp.sound))
|
e -> toggleSetting(pp, () -> pp.settings().setSound(!pp.settings().sound())))
|
||||||
.setItem(SLOT_SPECIAL_BLOCKS, toggleItem(locale, "special_blocks", pp.useSpecialBlocks),
|
.setItem(SLOT_SPECIAL_BLOCKS, toggleItem(locale, "special_blocks", pp.settings().useSpecialBlocks()),
|
||||||
e -> toggleSpecialBlocks(pp))
|
e -> toggleSpecialBlocks(pp))
|
||||||
.setItem(SLOT_FALL_MESSAGE, toggleItem(locale, "fall_message", pp.showFallMessage),
|
.setItem(SLOT_FALL_MESSAGE, toggleItem(locale, "fall_message", pp.settings().showFallMessage()),
|
||||||
e -> toggleSetting(pp, () -> pp.showFallMessage = !pp.showFallMessage))
|
e -> toggleSetting(pp, () -> pp.settings().setShowFallMessage(!pp.settings().showFallMessage())))
|
||||||
.setItem(SLOT_SCOREBOARD, toggleItem(locale, "scoreboard", pp.showScoreboard),
|
.setItem(SLOT_SCOREBOARD, toggleItem(locale, "scoreboard", pp.settings().showScoreboard()),
|
||||||
e -> toggleSetting(pp, () -> pp.showScoreboard = !pp.showScoreboard))
|
e -> toggleSetting(pp, () -> pp.settings().setShowScoreboard(!pp.settings().showScoreboard())))
|
||||||
.setItem(SLOT_GHOST_RECORDING, toggleItem(locale, "ghost_recording",
|
.setItem(SLOT_GHOST_RECORDING, toggleItem(locale, "ghost_recording",
|
||||||
Boolean.TRUE.equals(pp.ghostRecording)), e -> toggleGhostRecording(pp))
|
Boolean.TRUE.equals(pp.settings().ghostRecording())), e -> toggleGhostRecording(pp))
|
||||||
.setItem(SLOT_STYLE, styleItem(pp), e -> openStyleMenu(pp))
|
.setItem(SLOT_STYLE, styleItem(pp), e -> openStyleMenu(pp))
|
||||||
.setItem(SLOT_SCHEM_DIFF, schemDiffItem(pp), e -> cycleSchematicDifficulty(pp))
|
.setItem(SLOT_SCHEM_DIFF, schemDiffItem(pp), e -> cycleSchematicDifficulty(pp))
|
||||||
.setItem(SLOT_CLOSE, closeButton(player), e -> Menus.SETTINGS.open(player))
|
.setItem(SLOT_CLOSE, closeButton(player), e -> Menus.SETTINGS.open(player))
|
||||||
|
|
@ -76,30 +77,30 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
|
|
||||||
private void toggleSetting(ParkourPlayer pp, Runnable update) {
|
private void toggleSetting(ParkourPlayer pp, Runnable update) {
|
||||||
update.run();
|
update.run();
|
||||||
pp.updateGeneratorSettings(pp.session.generator);
|
pp.updateGeneratorSettings(pp.session.generator());
|
||||||
open(pp);
|
open(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleSpecialBlocks(ParkourPlayer pp) {
|
private void toggleSpecialBlocks(ParkourPlayer pp) {
|
||||||
if (pp.session.generator.state.score == 0) {
|
if (pp.session.generator().state.score() == 0) {
|
||||||
pp.useSpecialBlocks = !pp.useSpecialBlocks;
|
pp.settings().setUseSpecialBlocks(!pp.settings().useSpecialBlocks());
|
||||||
pp.updateGeneratorSettings(pp.session.generator);
|
pp.updateGeneratorSettings(pp.session.generator());
|
||||||
}
|
}
|
||||||
open(pp);
|
open(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cycleSchematicDifficulty(ParkourPlayer pp) {
|
private void cycleSchematicDifficulty(ParkourPlayer pp) {
|
||||||
if (pp.session.generator.state.score == 0) {
|
if (pp.session.generator().state.score() == 0) {
|
||||||
final List<Double> diffs = List.of(0.0, DIFF_QUARTER, DIFF_HALF, DIFF_THREE_QUARTER, 1.0);
|
final List<Double> diffs = List.of(0.0, DIFF_QUARTER, DIFF_HALF, DIFF_THREE_QUARTER, 1.0);
|
||||||
final int idx = diffs.indexOf(pp.schematicDifficulty);
|
final int idx = diffs.indexOf(pp.settings().schematicDifficulty());
|
||||||
pp.schematicDifficulty = diffs.get((idx + 1) % diffs.size());
|
pp.settings().setSchematicDifficulty(diffs.get((idx + 1) % diffs.size()));
|
||||||
pp.updateGeneratorSettings(pp.session.generator);
|
pp.updateGeneratorSettings(pp.session.generator());
|
||||||
}
|
}
|
||||||
open(pp);
|
open(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toggleGhostRecording(ParkourPlayer pp) {
|
private void toggleGhostRecording(ParkourPlayer pp) {
|
||||||
pp.ghostRecording = !Boolean.TRUE.equals(pp.ghostRecording);
|
pp.settings().setGhostRecording(!Boolean.TRUE.equals(pp.settings().ghostRecording()));
|
||||||
pp.save(false);
|
pp.save(false);
|
||||||
open(pp);
|
open(pp);
|
||||||
}
|
}
|
||||||
|
|
@ -122,16 +123,16 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
|
|
||||||
private ItemStack schemDiffItem(ParkourPlayer pp) {
|
private ItemStack schemDiffItem(ParkourPlayer pp) {
|
||||||
final List<Double> diffs = List.of(0.0, DIFF_QUARTER, DIFF_HALF, DIFF_THREE_QUARTER, 1.0);
|
final List<Double> diffs = List.of(0.0, DIFF_QUARTER, DIFF_HALF, DIFF_THREE_QUARTER, 1.0);
|
||||||
final List<String> values = Locales.getStringList(pp.locale, "settings.parkour_settings.items.schematics.values");
|
final List<String> values = Locales.getStringList(pp.locale(), "settings.parkour_settings.items.schematics.values");
|
||||||
final int idx = Math.max(0, diffs.indexOf(pp.schematicDifficulty));
|
final int idx = Math.max(0, diffs.indexOf(pp.settings().schematicDifficulty()));
|
||||||
final String label = idx < values.size() ? values.get(idx) : String.valueOf(pp.schematicDifficulty);
|
final String label = idx < values.size() ? values.get(idx) : String.valueOf(pp.settings().schematicDifficulty());
|
||||||
final Material[] mats = {Material.RED_STAINED_GLASS_PANE, Material.LIME_STAINED_GLASS_PANE,
|
final Material[] mats = {Material.RED_STAINED_GLASS_PANE, Material.LIME_STAINED_GLASS_PANE,
|
||||||
Material.YELLOW_STAINED_GLASS_PANE, Material.ORANGE_STAINED_GLASS_PANE, Material.SKELETON_SKULL};
|
Material.YELLOW_STAINED_GLASS_PANE, Material.ORANGE_STAINED_GLASS_PANE, Material.SKELETON_SKULL};
|
||||||
final ItemStack item = new ItemStack(idx < mats.length ? mats[idx] : Material.PAPER);
|
final ItemStack item = new ItemStack(idx < mats.length ? mats[idx] : Material.PAPER);
|
||||||
final ItemMeta meta = item.getItemMeta();
|
final ItemMeta meta = item.getItemMeta();
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
final String name = Locales.getString(pp.locale, "settings.parkour_settings.items.schematics.name");
|
final String name = Locales.getString(pp.locale(), "settings.parkour_settings.items.schematics.name");
|
||||||
final String lore = Locales.getString(pp.locale, "settings.parkour_settings.items.schematics.lore");
|
final String lore = Locales.getString(pp.locale(), "settings.parkour_settings.items.schematics.lore");
|
||||||
meta.setDisplayName(ColorUtil.color(name.replace("%s", label)));
|
meta.setDisplayName(ColorUtil.color(name.replace("%s", label)));
|
||||||
applyLore(meta, lore, label);
|
applyLore(meta, lore, label);
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
|
|
@ -140,13 +141,13 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemStack styleItem(ParkourPlayer pp) {
|
private ItemStack styleItem(ParkourPlayer pp) {
|
||||||
final String name = Locales.getString(pp.locale, "settings.parkour_settings.items.styles.item.name");
|
final String name = Locales.getString(pp.locale(), "settings.parkour_settings.items.styles.item.name");
|
||||||
final String lore = Locales.getString(pp.locale, "settings.parkour_settings.items.styles.item.lore");
|
final String lore = Locales.getString(pp.locale(), "settings.parkour_settings.items.styles.item.lore");
|
||||||
final ItemStack item = new ItemStack(Material.CRAFTING_TABLE);
|
final ItemStack item = new ItemStack(Material.CRAFTING_TABLE);
|
||||||
final ItemMeta meta = item.getItemMeta();
|
final ItemMeta meta = item.getItemMeta();
|
||||||
if (meta != null) {
|
if (meta != null) {
|
||||||
meta.setDisplayName(ColorUtil.color(name.replace("%s", pp.style)));
|
meta.setDisplayName(ColorUtil.color(name.replace("%s", pp.settings().style())));
|
||||||
applyLore(meta, lore, pp.style);
|
applyLore(meta, lore, pp.settings().style());
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -165,7 +166,7 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
|
|
||||||
private void openStyleMenu(ParkourPlayer pp) {
|
private void openStyleMenu(ParkourPlayer pp) {
|
||||||
final Player player = pp.player;
|
final Player player = pp.player;
|
||||||
final String locale = pp.locale;
|
final String locale = pp.locale();
|
||||||
final String title = Locales.getString(locale, "settings.parkour_settings.styles.name");
|
final String title = Locales.getString(locale, "settings.parkour_settings.styles.name");
|
||||||
|
|
||||||
InventoryGui gui = baseGui(title, ROWS_STYLE);
|
InventoryGui gui = baseGui(title, ROWS_STYLE);
|
||||||
|
|
@ -187,10 +188,9 @@ public class ParkourSettingsMenu extends LPMenu {
|
||||||
item.setItemMeta(meta);
|
item.setItemMeta(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Style s = style;
|
|
||||||
gui = gui.setItem(slot++, item, e -> {
|
gui = gui.setItem(slot++, item, e -> {
|
||||||
pp.style = s.getName();
|
pp.settings().setStyle(style.getName());
|
||||||
pp.updateGeneratorSettings(pp.session.generator);
|
pp.updateGeneratorSettings(pp.session.generator());
|
||||||
open(pp);
|
open(pp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
import dev.loki.loparkour.menu.gui.inventory.InventoryGui;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
@ -24,11 +25,11 @@ public class SettingsMenu extends LPMenu {
|
||||||
|
|
||||||
InventoryGui gui = baseGui(title, ROWS);
|
InventoryGui gui = baseGui(title, ROWS);
|
||||||
|
|
||||||
if (ParkourOption.PARKOUR_SETTINGS.mayPerform(player) && ParkourPlayer.isPlayer(player)) {
|
if (ParkourOption.PARKOUR_SETTINGS.mayPerform(player) && UserRegistry.isPlayer(player)) {
|
||||||
gui = gui.setItem(SLOT_PARKOUR_SETTINGS, localeItem(player, "settings.parkour_settings.item"), e -> {
|
gui = gui.setItem(SLOT_PARKOUR_SETTINGS, localeItem(player, "settings.parkour_settings.item"), e -> {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null) {
|
if (pp != null) {
|
||||||
pp.session.generator.menu(pp);
|
pp.session.generator().menu(pp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +37,7 @@ public class SettingsMenu extends LPMenu {
|
||||||
if (ParkourOption.LANG.mayPerform(player) && ParkourUser.isUser(player)) {
|
if (ParkourOption.LANG.mayPerform(player) && ParkourUser.isUser(player)) {
|
||||||
final String langName = Locales.getString(locale, "name");
|
final String langName = Locales.getString(locale, "name");
|
||||||
gui = gui.setItem(SLOT_LANG, localeItem(locale, "settings.lang.item", langName), e -> {
|
gui = gui.setItem(SLOT_LANG, localeItem(locale, "settings.lang.item", langName), e -> {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null) {
|
if (pp != null) {
|
||||||
Menus.LANG.open(pp);
|
Menus.LANG.open(pp);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,35 +21,91 @@ public final class Modes {
|
||||||
private Modes() {
|
private Modes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DefaultMode DEFAULT;
|
private static DefaultMode defaultMode;
|
||||||
public static SpectatorMode SPECTATOR;
|
private static SpectatorMode spectator;
|
||||||
public static SpeedrunMode SPEEDRUN;
|
private static SpeedrunMode speedrun;
|
||||||
public static InvisibleBarrierMode INVISIBLE_BARRIER;
|
private static InvisibleBarrierMode invisibleBarrier;
|
||||||
public static RaceMode RACE;
|
private static RaceMode race;
|
||||||
public static CoopMode COOP;
|
private static CoopMode coop;
|
||||||
public static GravityShiftMode GRAVITY_SHIFT;
|
private static GravityShiftMode gravityShift;
|
||||||
public static ElytraMode ELYTRA;
|
private static ElytraMode elytra;
|
||||||
public static SpeedDemonMode SPEED_DEMON;
|
private static SpeedDemonMode speedDemon;
|
||||||
public static MinSpeedMode MIN_SPEED;
|
private static MinSpeedMode minSpeed;
|
||||||
public static TimeTrialMode TIME_TRIAL;
|
private static TimeTrialMode timeTrial;
|
||||||
public static CloseMode CLOSE;
|
private static CloseMode close;
|
||||||
public static ObstacleMode OBSTACLE;
|
private static ObstacleMode obstacle;
|
||||||
public static DuelsMode DUELS;
|
private static DuelsMode duels;
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
DEFAULT = (DefaultMode) Registry.getMode("default");
|
defaultMode = (DefaultMode) Registry.getMode("default");
|
||||||
SPECTATOR = (SpectatorMode) Registry.getMode("spectator");
|
spectator = (SpectatorMode) Registry.getMode("spectator");
|
||||||
SPEEDRUN = (SpeedrunMode) Registry.getMode("speedrun");
|
speedrun = (SpeedrunMode) Registry.getMode("speedrun");
|
||||||
INVISIBLE_BARRIER = (InvisibleBarrierMode) Registry.getMode("invisible-barrier");
|
invisibleBarrier = (InvisibleBarrierMode) Registry.getMode("invisible-barrier");
|
||||||
RACE = (RaceMode) Registry.getMode("race");
|
race = (RaceMode) Registry.getMode("race");
|
||||||
COOP = (CoopMode) Registry.getMode("coop");
|
coop = (CoopMode) Registry.getMode("coop");
|
||||||
GRAVITY_SHIFT = (GravityShiftMode) Registry.getMode("gravity-shift");
|
gravityShift = (GravityShiftMode) Registry.getMode("gravity-shift");
|
||||||
ELYTRA = (ElytraMode) Registry.getMode("elytra");
|
elytra = (ElytraMode) Registry.getMode("elytra");
|
||||||
SPEED_DEMON = (SpeedDemonMode) Registry.getMode("speeddemon");
|
speedDemon = (SpeedDemonMode) Registry.getMode("speeddemon");
|
||||||
MIN_SPEED = (MinSpeedMode) Registry.getMode("minspeed");
|
minSpeed = (MinSpeedMode) Registry.getMode("minspeed");
|
||||||
TIME_TRIAL = (TimeTrialMode) Registry.getMode("timetrial");
|
timeTrial = (TimeTrialMode) Registry.getMode("timetrial");
|
||||||
CLOSE = (CloseMode) Registry.getMode("close");
|
close = (CloseMode) Registry.getMode("close");
|
||||||
OBSTACLE = (ObstacleMode) Registry.getMode("obstacle");
|
obstacle = (ObstacleMode) Registry.getMode("obstacle");
|
||||||
DUELS = (DuelsMode) Registry.getMode("duels");
|
duels = (DuelsMode) Registry.getMode("duels");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DefaultMode defaultMode() {
|
||||||
|
return defaultMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpectatorMode spectator() {
|
||||||
|
return spectator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpeedrunMode speedrun() {
|
||||||
|
return speedrun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InvisibleBarrierMode invisibleBarrier() {
|
||||||
|
return invisibleBarrier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RaceMode race() {
|
||||||
|
return race;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CoopMode coop() {
|
||||||
|
return coop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GravityShiftMode gravityShift() {
|
||||||
|
return gravityShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ElytraMode elytra() {
|
||||||
|
return elytra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpeedDemonMode speedDemon() {
|
||||||
|
return speedDemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MinSpeedMode minSpeed() {
|
||||||
|
return minSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimeTrialMode timeTrial() {
|
||||||
|
return timeTrial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CloseMode close() {
|
||||||
|
return close;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObstacleMode obstacle() {
|
||||||
|
return obstacle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DuelsMode duels() {
|
||||||
|
return duels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,6 @@ class BarrierGenerator extends ParkourGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.INVISIBLE_BARRIER;
|
return Modes.invisibleBarrier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -39,8 +40,8 @@ public class InvisibleBarrierMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof BarrierGenerator) {
|
if (pp != null && pp.session.generator() instanceof BarrierGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ class CoopGenerator extends ParkourGenerator {
|
||||||
contributions.compute(pp.getUUID(), (uuid, count) -> (count == null ? 0 : count) + 1);
|
contributions.compute(pp.getUUID(), (uuid, count) -> (count == null ? 0 : count) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.score % MILESTONE_INTERVAL == 0) {
|
if (state.score() % MILESTONE_INTERVAL == 0) {
|
||||||
getPlayers().forEach(pp -> {
|
getPlayers().forEach(pp -> {
|
||||||
pp.player.sendTitle(
|
pp.player.sendTitle(
|
||||||
ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_title").formatted(state.score)),
|
ColorUtil.color(Locales.getString(pp.locale(), "modes.coop.milestone_title").formatted(state.score())),
|
||||||
ColorUtil.color(Locales.getString(pp.locale, "modes.coop.milestone_subtitle")),
|
ColorUtil.color(Locales.getString(pp.locale(), "modes.coop.milestone_subtitle")),
|
||||||
TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT);
|
TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT);
|
||||||
pp.player.playSound(pp.getLocation(),
|
pp.player.playSound(pp.getLocation(),
|
||||||
Sound.ENTITY_PLAYER_LEVELUP, MILESTONE_VOLUME, MILESTONE_PITCH);
|
Sound.ENTITY_PLAYER_LEVELUP, MILESTONE_VOLUME, MILESTONE_PITCH);
|
||||||
|
|
@ -65,9 +65,9 @@ class CoopGenerator extends ParkourGenerator {
|
||||||
public void fall() {
|
public void fall() {
|
||||||
getPlayers().forEach(pp -> {
|
getPlayers().forEach(pp -> {
|
||||||
final int contrib = contributions.getOrDefault(pp.getUUID(), 0);
|
final int contrib = contributions.getOrDefault(pp.getUUID(), 0);
|
||||||
final int pct = state.score > 0 ? (contrib * PERCENT_MULTIPLIER / state.score) : 0;
|
final int pct = state.score() > 0 ? (contrib * PERCENT_MULTIPLIER / state.score()) : 0;
|
||||||
pp.sendTranslated("modes.coop.stats",
|
pp.sendTranslated("modes.coop.stats",
|
||||||
Integer.toString(state.score),
|
Integer.toString(state.score()),
|
||||||
Integer.toString(contrib),
|
Integer.toString(contrib),
|
||||||
Integer.toString(pct));
|
Integer.toString(pct));
|
||||||
});
|
});
|
||||||
|
|
@ -84,6 +84,6 @@ class CoopGenerator extends ParkourGenerator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.COOP;
|
return Modes.coop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import dev.loki.loparkour.mode.base.MultiMode;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.player.core.ParkourUser;
|
import dev.loki.loparkour.player.core.ParkourUser;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
@ -51,8 +52,8 @@ public class CoopMode implements MultiMode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof CoopGenerator) {
|
if (pp != null && pp.session.generator() instanceof CoopGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,14 +71,14 @@ public class CoopMode implements MultiMode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.generator == null) {
|
if (session.generator() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourUser.register(joiningPlayer, session);
|
final ParkourPlayer pp = ParkourUser.register(joiningPlayer, session);
|
||||||
session.addPlayers(pp);
|
session.addPlayers(pp);
|
||||||
pp.updateGeneratorSettings(session.generator);
|
pp.updateGeneratorSettings(session.generator());
|
||||||
if (session.generator instanceof CoopGenerator coopGenerator) {
|
if (session.generator() instanceof CoopGenerator coopGenerator) {
|
||||||
coopGenerator.onPlayerJoin(pp);
|
coopGenerator.onPlayerJoin(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +88,7 @@ public class CoopMode implements MultiMode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leave(Player player, Session session) {
|
public void leave(Player player, Session session) {
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp == null) {
|
if (pp == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -25,7 +26,7 @@ import org.jetbrains.annotations.Nullable;
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@code tick()} called {@code super.tick()} AND checked score every tick —
|
* <li>{@code tick()} called {@code super.tick()} AND checked score every tick —
|
||||||
* but super.tick() already calls {@code score()} internally, so the finish
|
* but super.tick() already calls {@code score()} internally, so the finish
|
||||||
* check ran one tick late causing an off-by-one where state.score could
|
* check ran one tick late causing an off-by-one where state.score() could
|
||||||
* briefly exceed targetScore before finishing. Fixed by overriding
|
* briefly exceed targetScore before finishing. Fixed by overriding
|
||||||
* {@link #score()} instead — finish check fires the exact moment target is hit.</li>
|
* {@link #score()} instead — finish check fires the exact moment target is hit.</li>
|
||||||
* <li>ActionBar was re-sent every tick inside tick() which conflicted with the
|
* <li>ActionBar was re-sent every tick inside tick() which conflicted with the
|
||||||
|
|
@ -64,8 +65,8 @@ public class RaceMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof RaceGenerator) {
|
if (pp != null && pp.session.generator() instanceof RaceGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,25 +103,25 @@ public class RaceMode implements Mode {
|
||||||
// Update action bar on every new block
|
// Update action bar on every new block
|
||||||
updateProgressBar();
|
updateProgressBar();
|
||||||
|
|
||||||
if (!raceFinished && state.score >= targetScore) {
|
if (!raceFinished && state.score() >= targetScore) {
|
||||||
finishRace();
|
finishRace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Spigot Chat API: action bar via TextComponent
|
@SuppressWarnings("deprecation") // Spigot Chat API: action bar via TextComponent
|
||||||
private void updateProgressBar() {
|
private void updateProgressBar() {
|
||||||
final int remaining = Math.max(0, targetScore - state.score);
|
final int remaining = Math.max(0, targetScore - state.score());
|
||||||
final int filledCount = Math.min(BAR_SEGMENTS, state.score * BAR_SEGMENTS / Math.max(targetScore, 1));
|
final int filledCount = Math.min(BAR_SEGMENTS, state.score() * BAR_SEGMENTS / Math.max(targetScore, 1));
|
||||||
final String filled = ColorUtil.color("7FF55") + "█".repeat(filledCount);
|
final String filled = ColorUtil.color("7FF55") + "█".repeat(filledCount);
|
||||||
final String empty = ColorUtil.color("&#A0A0A0") + "█".repeat(BAR_SEGMENTS - filledCount);
|
final String empty = ColorUtil.color("&#A0A0A0") + "█".repeat(BAR_SEGMENTS - filledCount);
|
||||||
|
|
||||||
getPlayers().forEach(pp -> {
|
getPlayers().forEach(pp -> {
|
||||||
final String suffix = remaining > 0
|
final String suffix = remaining > 0
|
||||||
? Locales.getString(pp.locale, "modes.race.action_bar_left").formatted(remaining)
|
? Locales.getString(pp.locale(), "modes.race.action_bar_left").formatted(remaining)
|
||||||
: Locales.getString(pp.locale, "modes.race.action_bar_done");
|
: Locales.getString(pp.locale(), "modes.race.action_bar_done");
|
||||||
final String message = ColorUtil.color(
|
final String message = ColorUtil.color(
|
||||||
Locales.getString(pp.locale, "modes.race.action_bar")
|
Locales.getString(pp.locale(), "modes.race.action_bar")
|
||||||
.formatted(filled + empty, state.score, targetScore, suffix));
|
.formatted(filled + empty, state.score(), targetScore, suffix));
|
||||||
pp.player.spigot().sendMessage(
|
pp.player.spigot().sendMessage(
|
||||||
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
|
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
|
||||||
net.md_5.bungee.api.chat.TextComponent.fromLegacyText(message));
|
net.md_5.bungee.api.chat.TextComponent.fromLegacyText(message));
|
||||||
|
|
@ -133,8 +134,8 @@ public class RaceMode implements Mode {
|
||||||
|
|
||||||
getPlayers().forEach(pp -> {
|
getPlayers().forEach(pp -> {
|
||||||
pp.player.sendTitle(
|
pp.player.sendTitle(
|
||||||
ColorUtil.color(Locales.getString(pp.locale, "modes.race.title")),
|
ColorUtil.color(Locales.getString(pp.locale(), "modes.race.title")),
|
||||||
ColorUtil.color(Locales.getString(pp.locale, "modes.race.subtitle").formatted(time)),
|
ColorUtil.color(Locales.getString(pp.locale(), "modes.race.subtitle").formatted(time)),
|
||||||
TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT);
|
TITLE_FADE_IN, TITLE_STAY, TITLE_FADE_OUT);
|
||||||
pp.player.playSound(pp.getLocation(),
|
pp.player.playSound(pp.getLocation(),
|
||||||
Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f);
|
Sound.UI_TOAST_CHALLENGE_COMPLETE, 1f, 1f);
|
||||||
|
|
@ -148,7 +149,7 @@ public class RaceMode implements Mode {
|
||||||
// Return to lobby after 5 seconds — guarded against null/closed session
|
// Return to lobby after 5 seconds — guarded against null/closed session
|
||||||
dev.loki.loparkour.scheduler.Scheduler.get(LoParkour.getPlugin())
|
dev.loki.loparkour.scheduler.Scheduler.get(LoParkour.getPlugin())
|
||||||
.runLater(() -> {
|
.runLater(() -> {
|
||||||
if (session != null && !state.stopped) {
|
if (session != null && !state.stopped()) {
|
||||||
reset(false);
|
reset(false);
|
||||||
}
|
}
|
||||||
}, LOBBY_RESET_DELAY_TICKS);
|
}, LOBBY_RESET_DELAY_TICKS);
|
||||||
|
|
@ -162,7 +163,7 @@ public class RaceMode implements Mode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.RACE;
|
return Modes.race();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public class SpectatorMode implements Mode {
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
ParkourUser.unregister(user, false, false, false);
|
ParkourUser.unregister(user, false, false, false);
|
||||||
spectator = new ParkourSpectator(player, session, user.previousData);
|
spectator = new ParkourSpectator(player, session, user.previousData());
|
||||||
} else {
|
} else {
|
||||||
spectator = new ParkourSpectator(player, session, null);
|
spectator = new ParkourSpectator(player, session, null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.ModeMessages;
|
||||||
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
@ -43,8 +44,8 @@ public class DefaultMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator != null && pp.session.generator.getMode() instanceof DefaultMode) {
|
if (pp != null && pp.session.generator() != null && pp.session.generator().getMode() instanceof DefaultMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.util.item.Item;
|
import dev.loki.loparkour.util.item.Item;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
|
@ -56,8 +57,8 @@ public class GravityShiftMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof GravityShiftGenerator) {
|
if (pp != null && pp.session.generator() instanceof GravityShiftGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +171,7 @@ public class GravityShiftMode implements Mode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.GRAVITY_SHIFT;
|
return Modes.gravityShift();
|
||||||
}
|
}
|
||||||
|
|
||||||
private record EffectConfig(int amplifier, int durationTicks) { }
|
private record EffectConfig(int amplifier, int durationTicks) { }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
import dev.loki.loparkour.player.service.UserRegistry;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
import org.bukkit.Sound;
|
import org.bukkit.Sound;
|
||||||
|
|
@ -67,8 +68,8 @@ public class SpeedrunMode implements Mode {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParkourPlayer pp = ParkourPlayer.getPlayer(player);
|
final ParkourPlayer pp = UserRegistry.getPlayer(player);
|
||||||
if (pp != null && pp.session.generator instanceof SpeedrunGenerator) {
|
if (pp != null && pp.session.generator() instanceof SpeedrunGenerator) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,7 +171,7 @@ public class SpeedrunMode implements Mode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mode getMode() {
|
public Mode getMode() {
|
||||||
return Modes.SPEEDRUN;
|
return Modes.speedrun();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,111 +1,50 @@
|
||||||
package dev.loki.loparkour.player.core;
|
package dev.loki.loparkour.player.core;
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose;
|
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.generator.core.model.Profile;
|
|
||||||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
|
||||||
import dev.loki.loparkour.mode.base.MultiMode;
|
import dev.loki.loparkour.mode.base.MultiMode;
|
||||||
|
import dev.loki.loparkour.player.data.PlayerSettings;
|
||||||
import dev.loki.loparkour.player.data.PreviousData;
|
import dev.loki.loparkour.player.data.PreviousData;
|
||||||
import dev.loki.loparkour.player.service.PlayerSettingsManager;
|
import dev.loki.loparkour.player.service.PlayerSettingsManager;
|
||||||
import dev.loki.loparkour.player.service.PlayerSpawnSetup;
|
import dev.loki.loparkour.player.service.PlayerSpawnSetup;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
import dev.loki.loparkour.world.core.Divider;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.potion.PotionEffect;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclass of {@link ParkourUser}. This class is used for players who are actively playing Parkour in any (default) mode.
|
* Subclass of {@link ParkourUser} for players actively playing Parkour in any (default) mode.
|
||||||
*
|
*
|
||||||
* @author loki
|
* @author loki
|
||||||
*/
|
*/
|
||||||
public class ParkourPlayer extends ParkourUser {
|
public class ParkourPlayer extends ParkourUser {
|
||||||
|
|
||||||
public static final Map<String, PlayerSettingsManager.OptionContainer> PLAYER_COLUMNS = PlayerSettingsManager.getColumnMappings();
|
private final PlayerSettings settings = new PlayerSettings();
|
||||||
|
|
||||||
public @Expose Double schematicDifficulty;
|
|
||||||
public @Expose Integer blockLead;
|
|
||||||
public @Expose Boolean particles;
|
|
||||||
public @Expose Boolean sound;
|
|
||||||
public @Expose Boolean useSpecialBlocks;
|
|
||||||
public @Expose Boolean showFallMessage;
|
|
||||||
public @Expose Boolean showScoreboard;
|
|
||||||
public @Expose Boolean ghostRecording;
|
|
||||||
public @Expose Integer selectedTime;
|
|
||||||
public @Expose String style;
|
|
||||||
public @Expose String locale;
|
|
||||||
public @Expose List<String> collectedRewards;
|
|
||||||
|
|
||||||
private final PlayerScoringTracker scoring = new PlayerScoringTracker();
|
private final PlayerScoringTracker scoring = new PlayerScoringTracker();
|
||||||
private final PlayerPersistence persistence = new PlayerPersistence();
|
private final PlayerPersistence persistence = new PlayerPersistence();
|
||||||
private final PlayerSpawnSetup spawnSetup = new PlayerSpawnSetup();
|
private final PlayerSpawnSetup spawnSetup = new PlayerSpawnSetup();
|
||||||
|
|
||||||
/**
|
/** Creates a ParkourPlayer; API users should call {@link ParkourUser#register(Player, Session)} instead. */
|
||||||
* Creates a new instance of a ParkourPlayer<br>
|
|
||||||
* If you are using the API, please use {@link ParkourPlayer#register(Player, Session)} instead
|
|
||||||
*/
|
|
||||||
public ParkourPlayer(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
public ParkourPlayer(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
||||||
super(player, session, previousData);
|
super(player, session, previousData);
|
||||||
|
|
||||||
// generic player settings
|
|
||||||
player.setFlying(false);
|
player.setFlying(false);
|
||||||
player.setAllowFlight(false);
|
player.setAllowFlight(false);
|
||||||
player.setInvisible(false);
|
player.setInvisible(false);
|
||||||
|
player.getActivePotionEffects().forEach(effect -> player.removePotionEffect(effect.getType()));
|
||||||
for (final PotionEffect effect : player.getActivePotionEffects()) {
|
|
||||||
player.removePotionEffect(effect.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param player The player.
|
|
||||||
* @return True when this player is a {@link ParkourPlayer}, false if not.
|
|
||||||
*/
|
|
||||||
public static boolean isPlayer(@Nullable Player player) {
|
|
||||||
return player != null && getPlayers().stream().anyMatch(other -> other.player == player);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param player The player.
|
|
||||||
* @return player as a {@link ParkourPlayer}, null if not found.
|
|
||||||
*/
|
|
||||||
public static @Nullable ParkourPlayer getPlayer(@NotNull Player player) {
|
|
||||||
// Optimized: search directly in sessions without creating intermediate list
|
|
||||||
return Divider.SECTIONS.keySet().stream()
|
|
||||||
.flatMap(session -> session.getPlayers().stream())
|
|
||||||
.filter(other -> other.getUUID().equals(player.getUniqueId()))
|
|
||||||
.findAny()
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return List with all players.
|
|
||||||
*/
|
|
||||||
public static List<ParkourPlayer> getPlayers() {
|
|
||||||
return Divider.SECTIONS.keySet().stream()
|
|
||||||
.flatMap(session -> session.getPlayers().stream())
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unregister() {
|
public void unregister() {
|
||||||
if (session.generator != null &&
|
if (session.generator() != null &&
|
||||||
session.generator.getMode() != null &&
|
session.generator().getMode() != null &&
|
||||||
session.generator.getMode() instanceof MultiMode mode) {
|
session.generator().getMode() instanceof MultiMode mode) {
|
||||||
mode.leave(player, session);
|
mode.leave(player, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.removePlayers(this);
|
session.removePlayers(this);
|
||||||
|
|
||||||
// Clear scored blocks to prevent memory leak
|
|
||||||
scoring.clear();
|
scoring.clear();
|
||||||
|
|
||||||
if (LoParkour.getGhostService() != null) {
|
if (LoParkour.getGhostService() != null) {
|
||||||
|
|
@ -115,59 +54,32 @@ public class ParkourPlayer extends ParkourUser {
|
||||||
persistence.save(this, LoParkour.getPlugin().isEnabled());
|
persistence.save(this, LoParkour.getPlugin().isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Clear scored blocks set, typically on fall/respawn so same coordinates can be re-scored. */
|
||||||
* Clear scored blocks set, typically on fall/respawn so same coordinates can be re-scored.
|
|
||||||
*/
|
|
||||||
public void clearScoredBlocks() {
|
public void clearScoredBlocks() {
|
||||||
scoring.clear();
|
scoring.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Whether this player has already scored on this block. */
|
||||||
* Check if player has already scored on this block.
|
|
||||||
*/
|
|
||||||
public boolean hasScored(Block block) {
|
public boolean hasScored(Block block) {
|
||||||
return scoring.hasScored(block);
|
return scoring.hasScored(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Mark block as scored to prevent duplicate scoring. */
|
||||||
* Mark block as scored to prevent duplicate scoring.
|
|
||||||
*/
|
|
||||||
public void markScored(Block block) {
|
public void markScored(Block block) {
|
||||||
scoring.markScored(block);
|
scoring.markScored(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Preferences of this player: what to show, which style, how hard the schematics may be. */
|
||||||
* Sets the user's settings. If an item is not included, the setting gets reset.
|
public PlayerSettings settings() {
|
||||||
*
|
return settings;
|
||||||
* @param settings The settings map.
|
|
||||||
*/
|
|
||||||
public void setSettings(@NotNull Map<String, Object> settings) {
|
|
||||||
PlayerSettingsManager.applySettings(this, settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Forces this player's generator to match the settings of this player. */
|
||||||
* Forces this player's generator to match the settings of this player.
|
|
||||||
*/
|
|
||||||
public void updateGeneratorSettings(ParkourGenerator generator) {
|
public void updateGeneratorSettings(ParkourGenerator generator) {
|
||||||
final Profile profile = generator.profile;
|
PlayerSettingsManager.applyToGenerator(this, generator);
|
||||||
|
|
||||||
profile.set("schematicDifficulty", schematicDifficulty.toString())
|
|
||||||
.set("blockLead", blockLead.toString())
|
|
||||||
.set("particles", particles.toString())
|
|
||||||
.set("sound", sound.toString())
|
|
||||||
.set("useSpecialBlocks", useSpecialBlocks.toString())
|
|
||||||
.set("showFallMessage", showFallMessage.toString())
|
|
||||||
.set("showScoreboard", showScoreboard.toString())
|
|
||||||
.set("selectedTime", selectedTime.toString())
|
|
||||||
.set("style", style);
|
|
||||||
|
|
||||||
generator.profileManager.overrideProfile();
|
|
||||||
generator.profileManager.calculateChances();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Saves the player's data to their file. */
|
||||||
* Saves the player's data to their file
|
|
||||||
*/
|
|
||||||
public void save(boolean async) {
|
public void save(boolean async) {
|
||||||
persistence.save(this, async);
|
persistence.save(this, async);
|
||||||
}
|
}
|
||||||
|
|
@ -176,13 +88,8 @@ public class ParkourPlayer extends ParkourUser {
|
||||||
spawnSetup.setup(this, to);
|
spawnSetup.setup(this, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Updates hotbar items with the current language. */
|
||||||
* Updates hotbar items with current language
|
|
||||||
*/
|
|
||||||
public void updateHotbar() {
|
public void updateHotbar() {
|
||||||
spawnSetup.updateHotbar(this);
|
spawnSetup.updateHotbar(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record OptionContainer(ParkourOption option, BiConsumer<ParkourPlayer, String> consumer) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,16 @@ public abstract class ParkourUser {
|
||||||
|
|
||||||
// ─── Fields ───────────────────────────────────────────────────────────────
|
// ─── Fields ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@NotNull public String locale = OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
@NotNull private String locale = OptionGeneral.optionsDefaults().get(ParkourOption.LANG);
|
||||||
@Nullable public Scoreboard board;
|
@Nullable private Scoreboard board;
|
||||||
@NotNull public PreviousData previousData;
|
@NotNull private PreviousData previousData;
|
||||||
public Session.ChatType chatType = Session.ChatType.PUBLIC;
|
private Session.ChatType chatType = Session.ChatType.PUBLIC;
|
||||||
public final Session session;
|
public final Session session;
|
||||||
public final Player player;
|
public final Player player;
|
||||||
public final Instant joined;
|
public final Instant joined;
|
||||||
private ScoreboardManager scoreboardManager;
|
private ScoreboardManager scoreboardManager;
|
||||||
/** True while unregister is in progress; re-entrant unregister calls become no-ops. */
|
/** True while unregister is in progress; re-entrant unregister calls become no-ops. */
|
||||||
public boolean unregistering;
|
private boolean unregistering;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Scoreboard: legacy registerNewObjective(String,String,String)
|
@SuppressWarnings("deprecation") // Scoreboard: legacy registerNewObjective(String,String,String)
|
||||||
public ParkourUser(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
public ParkourUser(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
||||||
|
|
@ -93,6 +93,38 @@ public abstract class ParkourUser {
|
||||||
|
|
||||||
public abstract void unregister();
|
public abstract void unregister();
|
||||||
|
|
||||||
|
public @NotNull String locale() {
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocale(@NotNull String locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Scoreboard board() {
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBoard(@Nullable Scoreboard board) {
|
||||||
|
this.board = board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull PreviousData previousData() {
|
||||||
|
return previousData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Session.ChatType chatType() {
|
||||||
|
return chatType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean unregistering() {
|
||||||
|
return unregistering;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnregistering(boolean unregistering) {
|
||||||
|
this.unregistering = unregistering;
|
||||||
|
}
|
||||||
|
|
||||||
public void teleport(@NotNull Location to) {
|
public void teleport(@NotNull Location to) {
|
||||||
player.teleport(to);
|
player.teleport(to);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
115
src/main/java/dev/loki/loparkour/player/data/PlayerSettings.java
Normal file
115
src/main/java/dev/loki/loparkour/player/data/PlayerSettings.java
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
package dev.loki.loparkour.player.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Per-player parkour preferences: what to show, which style to play, how hard the schematics may be.
|
||||||
|
*
|
||||||
|
* <p>Persistence goes through the flat key/value map built by PlayerSettingsManager, so the storage keys
|
||||||
|
* are stable and independent of these field names.
|
||||||
|
*
|
||||||
|
* @author loki
|
||||||
|
*/
|
||||||
|
public class PlayerSettings {
|
||||||
|
|
||||||
|
private Double schematicDifficulty;
|
||||||
|
private Integer blockLead;
|
||||||
|
private Boolean particles;
|
||||||
|
private Boolean sound;
|
||||||
|
private Boolean useSpecialBlocks;
|
||||||
|
private Boolean showFallMessage;
|
||||||
|
private Boolean showScoreboard;
|
||||||
|
private Boolean ghostRecording;
|
||||||
|
private Integer selectedTime;
|
||||||
|
private String style;
|
||||||
|
private List<String> collectedRewards;
|
||||||
|
|
||||||
|
public Double schematicDifficulty() {
|
||||||
|
return schematicDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchematicDifficulty(Double schematicDifficulty) {
|
||||||
|
this.schematicDifficulty = schematicDifficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer blockLead() {
|
||||||
|
return blockLead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockLead(Integer blockLead) {
|
||||||
|
this.blockLead = blockLead;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean particles() {
|
||||||
|
return particles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParticles(Boolean particles) {
|
||||||
|
this.particles = particles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean sound() {
|
||||||
|
return sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSound(Boolean sound) {
|
||||||
|
this.sound = sound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean useSpecialBlocks() {
|
||||||
|
return useSpecialBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseSpecialBlocks(Boolean useSpecialBlocks) {
|
||||||
|
this.useSpecialBlocks = useSpecialBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean showFallMessage() {
|
||||||
|
return showFallMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowFallMessage(Boolean showFallMessage) {
|
||||||
|
this.showFallMessage = showFallMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean showScoreboard() {
|
||||||
|
return showScoreboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowScoreboard(Boolean showScoreboard) {
|
||||||
|
this.showScoreboard = showScoreboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean ghostRecording() {
|
||||||
|
return ghostRecording;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGhostRecording(Boolean ghostRecording) {
|
||||||
|
this.ghostRecording = ghostRecording;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer selectedTime() {
|
||||||
|
return selectedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelectedTime(Integer selectedTime) {
|
||||||
|
this.selectedTime = selectedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String style() {
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStyle(String style) {
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reward keys already claimed by this player; never null after first access. */
|
||||||
|
public List<String> collectedRewards() {
|
||||||
|
if (collectedRewards == null) {
|
||||||
|
collectedRewards = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return collectedRewards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ public class PreviousData {
|
||||||
/**
|
/**
|
||||||
* List of all {@link Reward} to execute on leave.
|
* List of all {@link Reward} to execute on leave.
|
||||||
*/
|
*/
|
||||||
public List<Reward> onLeave = new ArrayList<>();
|
public final List<Reward> onLeave = new ArrayList<>();
|
||||||
|
|
||||||
public PreviousData(@NotNull Player player) {
|
public PreviousData(@NotNull Player player) {
|
||||||
gamemode = player.getGameMode();
|
gamemode = player.getGameMode();
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package dev.loki.loparkour.player.service;
|
package dev.loki.loparkour.player.service;
|
||||||
|
|
||||||
import dev.loki.loparkour.config.options.OptionGeneral;
|
import dev.loki.loparkour.config.options.OptionGeneral;
|
||||||
|
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
|
import dev.loki.loparkour.generator.core.model.Profile;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.player.data.PlayerSettings;
|
||||||
|
|
||||||
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
import dev.loki.loparkour.menu.core.screen.ParkourOption;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
|
@ -30,44 +33,98 @@ public final class PlayerSettingsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initializeColumnMappings() {
|
private static void initializeColumnMappings() {
|
||||||
COLUMN_MAPPINGS.put("uuid", new OptionContainer(null, null));
|
put("uuid", null, null);
|
||||||
COLUMN_MAPPINGS.put("style", new OptionContainer(ParkourOption.STYLES, (p, v) -> p.style = v));
|
put("style", ParkourOption.STYLES, (p, v) -> p.settings().setStyle(v));
|
||||||
COLUMN_MAPPINGS.put("blockLead", new OptionContainer(ParkourOption.LEADS, (p, v) ->
|
put("blockLead", ParkourOption.LEADS, (p, v) -> p.settings().setBlockLead(parseIntSafe(v, defaultBlockLead())));
|
||||||
p.blockLead = parseIntSafe(v, defaultBlockLead())));
|
put("useParticles", ParkourOption.PARTICLES, (p, v) -> p.settings().setParticles(parseBoolean(v)));
|
||||||
COLUMN_MAPPINGS.put("useParticles", new OptionContainer(ParkourOption.PARTICLES, (p, v) -> p.particles = parseBoolean(v)));
|
put("useSpecial", ParkourOption.SPECIAL_BLOCKS, (p, v) -> p.settings().setUseSpecialBlocks(parseBoolean(v)));
|
||||||
COLUMN_MAPPINGS.put("useSpecial", new OptionContainer(ParkourOption.SPECIAL_BLOCKS, (p, v) -> p.useSpecialBlocks = parseBoolean(v)));
|
put("showFallMsg", ParkourOption.FALL_MESSAGE, (p, v) -> p.settings().setShowFallMessage(parseBoolean(v)));
|
||||||
COLUMN_MAPPINGS.put("showFallMsg", new OptionContainer(ParkourOption.FALL_MESSAGE, (p, v) -> p.showFallMessage = parseBoolean(v)));
|
put("showScoreboard", ParkourOption.SCOREBOARD, (p, v) -> p.settings().setShowScoreboard(parseBoolean(v)));
|
||||||
COLUMN_MAPPINGS.put("showScoreboard", new OptionContainer(ParkourOption.SCOREBOARD, (p, v) -> p.showScoreboard = parseBoolean(v)));
|
put("selectedTime", ParkourOption.TIME, (p, v) -> p.settings().setSelectedTime(parseIntSafe(v, defaultSelectedTime())));
|
||||||
COLUMN_MAPPINGS.put("selectedTime", new OptionContainer(ParkourOption.TIME, (p, v) ->
|
put("collectedRewards", null, PlayerSettingsManager::applyCollectedRewards);
|
||||||
p.selectedTime = parseIntSafe(v, defaultSelectedTime())));
|
put("locale", ParkourOption.LANG, (p, v) -> p.setLocale(sanitizeLocale(v)));
|
||||||
COLUMN_MAPPINGS.put("collectedRewards", new OptionContainer(null, PlayerSettingsManager::applyCollectedRewards));
|
put("schematicDifficulty", ParkourOption.SCHEMATICS, (p, v) ->
|
||||||
COLUMN_MAPPINGS.put("locale", new OptionContainer(ParkourOption.LANG, (p, v) -> {
|
p.settings().setSchematicDifficulty(parseDoubleSafe(v, defaultSchematicDifficulty())));
|
||||||
// Sanitize legacy Boolean values from old config parsing
|
put("sound", ParkourOption.SOUND, (p, v) -> p.settings().setSound(parseBoolean(v)));
|
||||||
String sanitized = v;
|
put("ghostRecording", ParkourOption.GHOST_RECORDING, (p, v) -> p.settings().setGhostRecording(parseBoolean(v)));
|
||||||
if ("true".equals(v) || "false".equals(v)) {
|
|
||||||
sanitized = OptionGeneral.optionsDefaults().getOrDefault(ParkourOption.LANG, "en");
|
|
||||||
}
|
|
||||||
p.locale = sanitized;
|
|
||||||
}));
|
|
||||||
COLUMN_MAPPINGS.put("schematicDifficulty", new OptionContainer(ParkourOption.SCHEMATICS, (p, v) ->
|
|
||||||
p.schematicDifficulty = parseDoubleSafe(v, defaultSchematicDifficulty())));
|
|
||||||
COLUMN_MAPPINGS.put("sound", new OptionContainer(ParkourOption.SOUND, (p, v) -> p.sound = parseBoolean(v)));
|
|
||||||
COLUMN_MAPPINGS.put("ghostRecording", new OptionContainer(ParkourOption.GHOST_RECORDING, (p, v) -> p.ghostRecording = parseBoolean(v)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, OptionContainer> getColumnMappings() {
|
private static void put(String key, ParkourOption option, BiConsumer<ParkourPlayer, String> consumer) {
|
||||||
return COLUMN_MAPPINGS;
|
COLUMN_MAPPINGS.put(key, new OptionContainer(option, consumer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Language code safe to use: legacy data stored booleans and numbers where a code belonged.
|
||||||
|
*/
|
||||||
|
public static String sanitizeLocale(String value) {
|
||||||
|
if (value == null || value.isEmpty() || "true".equals(value) || "false".equals(value)
|
||||||
|
|| "1".equals(value) || "0".equals(value)) {
|
||||||
|
return OptionGeneral.optionsDefaults().getOrDefault(ParkourOption.LANG, "en");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The persisted settings of a player, keyed identically for the disk and the SQL backends.
|
||||||
|
*/
|
||||||
|
public static Map<String, Object> toSettingsMap(ParkourPlayer player) {
|
||||||
|
final PlayerSettings settings = player.settings();
|
||||||
|
final Map<String, Object> persisted = new HashMap<>();
|
||||||
|
|
||||||
|
persisted.put("style", settings.style());
|
||||||
|
persisted.put("blockLead", settings.blockLead());
|
||||||
|
persisted.put("useParticles", settings.particles());
|
||||||
|
persisted.put("useSpecial", settings.useSpecialBlocks());
|
||||||
|
persisted.put("showFallMsg", settings.showFallMessage());
|
||||||
|
persisted.put("showScoreboard", settings.showScoreboard());
|
||||||
|
persisted.put("selectedTime", settings.selectedTime());
|
||||||
|
persisted.put("ghostRecording", settings.ghostRecording());
|
||||||
|
persisted.put("collectedRewards", settings.collectedRewards());
|
||||||
|
persisted.put("locale", sanitizeLocale(player.locale()));
|
||||||
|
persisted.put("schematicDifficulty", settings.schematicDifficulty());
|
||||||
|
persisted.put("sound", settings.sound());
|
||||||
|
|
||||||
|
return persisted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push a player's settings into their generator profile and recompute the jump chances.
|
||||||
|
*/
|
||||||
|
public static void applyToGenerator(ParkourPlayer player, ParkourGenerator generator) {
|
||||||
|
final Profile profile = generator.profile;
|
||||||
|
final PlayerSettings settings = player.settings();
|
||||||
|
|
||||||
|
profile.set("schematicDifficulty", settings.schematicDifficulty().toString())
|
||||||
|
.set("blockLead", settings.blockLead().toString())
|
||||||
|
.set("particles", settings.particles().toString())
|
||||||
|
.set("sound", settings.sound().toString())
|
||||||
|
.set("useSpecialBlocks", settings.useSpecialBlocks().toString())
|
||||||
|
.set("showFallMessage", settings.showFallMessage().toString())
|
||||||
|
.set("showScoreboard", settings.showScoreboard().toString())
|
||||||
|
.set("selectedTime", settings.selectedTime().toString())
|
||||||
|
.set("style", settings.style());
|
||||||
|
|
||||||
|
generator.profileManager.overrideProfile();
|
||||||
|
generator.profileManager.calculateChances();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets every setting of this player to the configured default (or to the option store value when the option is disabled).
|
||||||
|
*/
|
||||||
|
public static void applyDefaults(ParkourPlayer player) {
|
||||||
|
applySettings(player, Map.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applySettings(ParkourPlayer player, Map<String, Object> settings) {
|
public static void applySettings(ParkourPlayer player, Map<String, Object> settings) {
|
||||||
for (final String key : COLUMN_MAPPINGS.keySet()) {
|
for (final String key : COLUMN_MAPPINGS.keySet()) {
|
||||||
final Object value = settings.get(key);
|
|
||||||
final OptionContainer container = COLUMN_MAPPINGS.get(key);
|
final OptionContainer container = COLUMN_MAPPINGS.get(key);
|
||||||
|
|
||||||
if (container.consumer == null) {
|
if (container.consumer == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Object value = settings.get(key);
|
||||||
|
|
||||||
if (value == null || !OptionGeneral.optionsEnabled().getOrDefault(container.option, true)) {
|
if (value == null || !OptionGeneral.optionsEnabled().getOrDefault(container.option, true)) {
|
||||||
final String defaultValue = OptionGeneral.optionsDefaults().getOrDefault(container.option, "");
|
final String defaultValue = OptionGeneral.optionsDefaults().getOrDefault(container.option, "");
|
||||||
container.consumer.accept(player, defaultValue);
|
container.consumer.accept(player, defaultValue);
|
||||||
|
|
@ -95,21 +152,23 @@ public final class PlayerSettingsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int parseIntSafe(String v, int fallback) {
|
private static int parseIntSafe(String v, int fallback) {
|
||||||
if (v == null) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
final String s = v.trim();
|
|
||||||
if (s.isEmpty() || "true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
return Integer.parseInt(s);
|
return Integer.parseInt(numericOrFallback(v, String.valueOf(fallback)));
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double parseDoubleSafe(String v, double fallback) {
|
private static double parseDoubleSafe(String v, double fallback) {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(numericOrFallback(v, String.valueOf(fallback)));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Legacy rows kept booleans and empty strings where a number belonged. */
|
||||||
|
private static String numericOrFallback(String v, String fallback) {
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
@ -117,23 +176,20 @@ public final class PlayerSettingsManager {
|
||||||
if (s.isEmpty() || "true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) {
|
if (s.isEmpty() || "true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
try {
|
return s;
|
||||||
return Double.parseDouble(s);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyCollectedRewards(ParkourPlayer player, String value) {
|
private static void applyCollectedRewards(ParkourPlayer player, String value) {
|
||||||
player.collectedRewards = new ArrayList<>();
|
final List<String> rewards = player.settings().collectedRewards();
|
||||||
|
rewards.clear();
|
||||||
|
|
||||||
if (!value.isEmpty()) {
|
if (!value.isEmpty()) {
|
||||||
player.collectedRewards.addAll(Arrays.stream(value.replaceAll("[ \\[\\]]", "").split(","))
|
rewards.addAll(Arrays.stream(value.replaceAll("[ \\[\\]]", "").split(","))
|
||||||
.distinct()
|
.distinct()
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record OptionContainer(ParkourOption option, BiConsumer<ParkourPlayer, String> consumer) {
|
record OptionContainer(ParkourOption option, BiConsumer<ParkourPlayer, String> consumer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ public final class UserRegistry {
|
||||||
final ParkourUser existing = getUser(player);
|
final ParkourUser existing = getUser(player);
|
||||||
|
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
data = existing.previousData;
|
data = existing.previousData();
|
||||||
unregister(existing, false, false, false);
|
unregister(existing, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,11 +62,11 @@ public final class UserRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unregister(@NotNull ParkourUser user, boolean restorePreviousData, boolean kickIfBungee, boolean urgent) {
|
public static void unregister(@NotNull ParkourUser user, boolean restorePreviousData, boolean kickIfBungee, boolean urgent) {
|
||||||
if (user.unregistering) {
|
if (user.unregistering()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.unregistering = true;
|
user.setUnregistering(true);
|
||||||
try {
|
try {
|
||||||
dev.loki.loparkour.adaptive.bootstrap.AdaptiveServices.unloadPlayer(user.getUUID());
|
dev.loki.loparkour.adaptive.bootstrap.AdaptiveServices.unloadPlayer(user.getUUID());
|
||||||
new ParkourLeaveEvent(user).call();
|
new ParkourLeaveEvent(user).call();
|
||||||
|
|
@ -86,7 +87,7 @@ public final class UserRegistry {
|
||||||
restorePreviousState(user, urgent);
|
restorePreviousState(user, urgent);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
user.unregistering = false;
|
user.setUnregistering(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +108,25 @@ public final class UserRegistry {
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isPlayer(@Nullable Player player) {
|
||||||
|
return player != null && getPlayers().stream().anyMatch(other -> other.player == player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable ParkourPlayer getPlayer(@NotNull Player player) {
|
||||||
|
// Optimized: search directly in sessions without creating intermediate list
|
||||||
|
return Divider.SECTIONS.keySet().stream()
|
||||||
|
.flatMap(session -> session.getPlayers().stream())
|
||||||
|
.filter(other -> other.getUUID().equals(player.getUniqueId()))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<ParkourPlayer> getPlayers() {
|
||||||
|
return Divider.SECTIONS.keySet().stream()
|
||||||
|
.flatMap(session -> session.getPlayers().stream())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
public static int getJoinCount() {
|
public static int getJoinCount() {
|
||||||
final int count = joinCount;
|
final int count = joinCount;
|
||||||
joinCount = 0;
|
joinCount = 0;
|
||||||
|
|
@ -114,9 +134,9 @@ public final class UserRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void resetScoreboard(ParkourUser user) {
|
private static void resetScoreboard(ParkourUser user) {
|
||||||
if (user.board != null) {
|
if (user.board() != null) {
|
||||||
user.player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
user.player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
|
||||||
user.board = null;
|
user.setBoard(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,16 +151,16 @@ public final class UserRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void restorePreviousState(ParkourUser user, boolean urgent) {
|
private static void restorePreviousState(ParkourUser user, boolean urgent) {
|
||||||
user.previousData.apply(user.player, urgent);
|
user.previousData().apply(user.player, urgent);
|
||||||
|
|
||||||
Mode mode = user.session.generator.getMode();
|
Mode mode = user.session.generator().getMode();
|
||||||
if (mode == null) {
|
if (mode == null) {
|
||||||
mode = Modes.DEFAULT;
|
mode = Modes.defaultMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user instanceof ParkourPlayer player) {
|
if (user instanceof ParkourPlayer player) {
|
||||||
final Mode finalMode = mode;
|
final Mode finalMode = mode;
|
||||||
user.previousData.onLeave.forEach(r -> r.execute(player, finalMode));
|
user.previousData().onLeave.forEach(r -> r.execute(player, finalMode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,6 @@ public final class ParkourHotbar {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int slot = Config.CONFIG.getInt(slotPath);
|
final int slot = Config.CONFIG.getInt(slotPath);
|
||||||
player.player.getInventory().setItem(slot, Locales.getItem(player.locale, itemPath).build());
|
player.player.getInventory().setItem(slot, Locales.getItem(player.locale(), itemPath).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ public class ScoreboardManager {
|
||||||
|
|
||||||
private String replacePlaceholders(String text, Score top, Score high, ParkourGenerator generator) {
|
private String replacePlaceholders(String text, Score top, Score high, ParkourGenerator generator) {
|
||||||
final String replaced = text
|
final String replaced = text
|
||||||
.replace("%score%", Integer.toString(generator.state.score))
|
.replace("%score%", Integer.toString(generator.state.score()))
|
||||||
.replace("%time%", generator.getFormattedTime())
|
.replace("%time%", generator.getFormattedTime())
|
||||||
.replace("%difficulty%", Double.toString(generator.getDifficultyScore()))
|
.replace("%difficulty%", Double.toString(generator.getDifficultyScore()))
|
||||||
.replace("%top_score%", Integer.toString(top.score()))
|
.replace("%top_score%", Integer.toString(top.score()))
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public class ParkourSpectator extends ParkourUser {
|
||||||
* The closest player.
|
* The closest player.
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public ParkourPlayer closest;
|
private ParkourPlayer closest;
|
||||||
|
|
||||||
public ParkourSpectator(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
public ParkourSpectator(@NotNull Player player, @NotNull Session session, @Nullable PreviousData previousData) {
|
||||||
super(player, session, previousData);
|
super(player, session, previousData);
|
||||||
|
|
@ -79,7 +79,7 @@ public class ParkourSpectator extends ParkourUser {
|
||||||
final String actionBar = ColorUtil.color(Locales.getString(player, "play.spectator.action_bar"));
|
final String actionBar = ColorUtil.color(Locales.getString(player, "play.spectator.action_bar"));
|
||||||
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(actionBar));
|
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(actionBar));
|
||||||
player.setGameMode(GameMode.SPECTATOR);
|
player.setGameMode(GameMode.SPECTATOR);
|
||||||
updateScoreboard(session.generator);
|
updateScoreboard(session.generator());
|
||||||
|
|
||||||
// spectator is still being teleported to world
|
// spectator is still being teleported to world
|
||||||
if (closest.getLocation().getWorld() != player.getLocation().getWorld()) {
|
if (closest.getLocation().getWorld() != player.getLocation().getWorld()) {
|
||||||
|
|
@ -100,6 +100,16 @@ public class ParkourSpectator extends ParkourUser {
|
||||||
player.setSpectatorTarget(player.getSpectatorTarget());
|
player.setSpectatorTarget(player.getSpectatorTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Player this spectator is currently following.
|
||||||
|
*
|
||||||
|
* @return The closest parkour player.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public ParkourPlayer closest() {
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the closest checker runnable.
|
* Stops the closest checker runnable.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -26,48 +26,48 @@ public record Reward(@NotNull String string) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String string = this.string;
|
String command = this.string;
|
||||||
|
|
||||||
final Mode rewardMode = Registry.getModes().stream()
|
final Mode rewardMode = Registry.getModes().stream()
|
||||||
.filter(m -> this.string.contains("%s:".formatted(m.getName().toLowerCase())))
|
.filter(m -> this.string.contains("%s:".formatted(m.getName().toLowerCase())))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(Modes.DEFAULT);
|
.orElse(Modes.defaultMode());
|
||||||
|
|
||||||
if (mode != rewardMode) {
|
if (mode != rewardMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string = string.replaceFirst("%s:".formatted(rewardMode.getName().toLowerCase()), "");
|
command = command.replaceFirst("%s:".formatted(rewardMode.getName().toLowerCase()), "");
|
||||||
|
|
||||||
// check for placeholders
|
// check for placeholders
|
||||||
if (string.toLowerCase().contains("%player%")) {
|
if (command.toLowerCase().contains("%player%")) {
|
||||||
string = string.replaceAll("%player%", player.getName());
|
command = command.replaceAll("%player%", player.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for extra data
|
// check for extra data
|
||||||
if (string.toLowerCase().contains("leave:")) { // leave:
|
if (command.toLowerCase().contains("leave:")) { // leave:
|
||||||
string = string.replaceFirst("leave:", "");
|
command = command.replaceFirst("leave:", "");
|
||||||
player.previousData.onLeave.add(new Reward(string));
|
player.previousData().onLeave.add(new Reward(command));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for things to perform
|
// check for things to perform
|
||||||
if (string.toLowerCase().contains("send:")) {
|
if (command.toLowerCase().contains("send:")) {
|
||||||
string = string.replaceFirst("send:", "");
|
command = command.replaceFirst("send:", "");
|
||||||
|
|
||||||
player.send(string);
|
player.send(command);
|
||||||
} else if (string.toLowerCase().contains("vault:")) {
|
} else if (command.toLowerCase().contains("vault:")) {
|
||||||
string = string.replaceFirst("vault:", "");
|
command = command.replaceFirst("vault:", "");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
VaultHook.deposit(player.player, Double.parseDouble(string));
|
VaultHook.deposit(player.player, Double.parseDouble(command));
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
LoParkour.getPlugin().getLogger().severe(
|
LoParkour.getPlugin().getLogger().severe(
|
||||||
"Error while trying to process Vault reward - check your rewards file for incorrect numbers - "
|
"Error while trying to process Vault reward - check your rewards file for incorrect numbers - "
|
||||||
+ ex.getMessage());
|
+ ex.getMessage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), string);
|
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package dev.loki.loparkour.reward.core;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -15,40 +16,42 @@ public final class Rewards {
|
||||||
private Rewards() {
|
private Rewards() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean REWARDS_ENABLED;
|
private static boolean rewardsEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map with all Score-type score rewards.
|
* A map with all Score-type score rewards.
|
||||||
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
||||||
*/
|
*/
|
||||||
public static Map<Integer, List<Reward>> SCORE_REWARDS = new HashMap<>();
|
private static final Map<Integer, List<Reward>> SCORE_REWARDS = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map with all Interval-type score rewards.
|
* A map with all Interval-type score rewards.
|
||||||
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
||||||
*/
|
*/
|
||||||
public static Map<Integer, List<Reward>> INTERVAL_REWARDS = new HashMap<>();
|
private static final Map<Integer, List<Reward>> INTERVAL_REWARDS = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map with all One time-type score rewards.
|
* A map with all One time-type score rewards.
|
||||||
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
* The key is the score, and the value are the commands that will be executed once this score is reached.
|
||||||
*/
|
*/
|
||||||
public static Map<Integer, List<Reward>> ONE_TIME_REWARDS = new HashMap<>();
|
private static final Map<Integer, List<Reward>> ONE_TIME_REWARDS = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the rewards from the rewards-v2.yml file
|
* Reads the rewards from the rewards-v2.yml file
|
||||||
*/
|
*/
|
||||||
public static void init() {
|
public static void init() {
|
||||||
// init options
|
// init options
|
||||||
REWARDS_ENABLED = Config.REWARDS.getBoolean("enabled");
|
rewardsEnabled = Config.REWARDS.getBoolean("enabled");
|
||||||
|
|
||||||
if (!REWARDS_ENABLED) {
|
clear();
|
||||||
|
|
||||||
|
if (!rewardsEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCORE_REWARDS = parseScores("score-rewards");
|
SCORE_REWARDS.putAll(parseScores("score-rewards"));
|
||||||
INTERVAL_REWARDS = parseScores("interval-rewards");
|
INTERVAL_REWARDS.putAll(parseScores("interval-rewards"));
|
||||||
ONE_TIME_REWARDS = parseScores("one-time-rewards");
|
ONE_TIME_REWARDS.putAll(parseScores("one-time-rewards"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,6 +63,42 @@ public final class Rewards {
|
||||||
ONE_TIME_REWARDS.clear();
|
ONE_TIME_REWARDS.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the reward system is active.
|
||||||
|
*
|
||||||
|
* @return True when the rewards system is enabled in rewards-v2.yml.
|
||||||
|
*/
|
||||||
|
public static boolean enabled() {
|
||||||
|
return rewardsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Score rewards.
|
||||||
|
*
|
||||||
|
* @return Read-only view of the score rewards, keyed by score.
|
||||||
|
*/
|
||||||
|
public static Map<Integer, List<Reward>> scoreRewards() {
|
||||||
|
return Collections.unmodifiableMap(SCORE_REWARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interval rewards.
|
||||||
|
*
|
||||||
|
* @return Read-only view of the interval rewards, keyed by score.
|
||||||
|
*/
|
||||||
|
public static Map<Integer, List<Reward>> intervalRewards() {
|
||||||
|
return Collections.unmodifiableMap(INTERVAL_REWARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-time rewards.
|
||||||
|
*
|
||||||
|
* @return Read-only view of the one-time rewards, keyed by score.
|
||||||
|
*/
|
||||||
|
public static Map<Integer, List<Reward>> oneTimeRewards() {
|
||||||
|
return Collections.unmodifiableMap(ONE_TIME_REWARDS);
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<Integer, List<Reward>> parseScores(String path) {
|
private static Map<Integer, List<Reward>> parseScores(String path) {
|
||||||
final Map<Integer, List<Reward>> rewardMap = new HashMap<>();
|
final Map<Integer, List<Reward>> rewardMap = new HashMap<>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import dev.loki.loparkour.schematic.core.SchematicManager;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,16 +17,16 @@ public final class SchematicDifficultyConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setDifficulty(@NotNull String fileStem, double difficulty) throws IOException {
|
public static void setDifficulty(@NotNull String fileStem, double difficulty) throws IOException {
|
||||||
if (Config.SCHEMATICS.path == null) {
|
final File configFile = Config.SCHEMATICS.path();
|
||||||
|
if (configFile == null) {
|
||||||
throw new IOException("Schematics config path is not initialized");
|
throw new IOException("Schematics config path is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
final YamlConfiguration yaml = YamlConfiguration.loadConfiguration(Config.SCHEMATICS.path);
|
final YamlConfiguration yaml = YamlConfiguration.loadConfiguration(configFile);
|
||||||
final String key = SchematicManager.configKey(fileStem);
|
final String key = SchematicManager.configKey(fileStem);
|
||||||
yaml.set("difficulty." + key, difficulty);
|
yaml.set("difficulty." + key, difficulty);
|
||||||
yaml.save(Config.SCHEMATICS.path);
|
yaml.save(configFile);
|
||||||
|
|
||||||
Config.SCHEMATICS.fileConfiguration = YamlConfiguration.loadConfiguration(Config.SCHEMATICS.path);
|
|
||||||
Config.SCHEMATICS.load();
|
Config.SCHEMATICS.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -171,9 +171,10 @@ public final class SchematicManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double resolveDifficulty(@NotNull String fileStem) {
|
private static double resolveDifficulty(@NotNull String fileStem) {
|
||||||
final ConfigurationSection section = Config.SCHEMATICS.fileConfiguration == null
|
final var config = Config.SCHEMATICS.fileConfiguration();
|
||||||
|
final ConfigurationSection section = config == null
|
||||||
? null
|
? null
|
||||||
: Config.SCHEMATICS.fileConfiguration.getConfigurationSection("difficulty");
|
: config.getConfigurationSection("difficulty");
|
||||||
if (section == null) {
|
if (section == null) {
|
||||||
return DEFAULT_DIFFICULTY;
|
return DEFAULT_DIFFICULTY;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import java.util.function.Function;
|
||||||
*/
|
*/
|
||||||
public final class Session {
|
public final class Session {
|
||||||
|
|
||||||
public ParkourGenerator generator;
|
private ParkourGenerator generator;
|
||||||
|
|
||||||
private final SessionPlayerManager playerManager;
|
private final SessionPlayerManager playerManager;
|
||||||
private final SessionStateManager stateManager;
|
private final SessionStateManager stateManager;
|
||||||
|
|
@ -65,8 +65,8 @@ public final class Session {
|
||||||
session.stateManager.initialize(generatorFunction);
|
session.stateManager.initialize(generatorFunction);
|
||||||
|
|
||||||
// Update generator settings for players
|
// Update generator settings for players
|
||||||
if (session.generator != null) {
|
if (session.generator() != null) {
|
||||||
parkourPlayers.forEach(p -> p.updateGeneratorSettings(session.generator));
|
parkourPlayers.forEach(p -> p.updateGeneratorSettings(session.generator()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
|
|
@ -142,6 +142,15 @@ public final class Session {
|
||||||
stateManager.cleanup();
|
stateManager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator driving this session.
|
||||||
|
*
|
||||||
|
* @return The generator, null until the session state is initialized.
|
||||||
|
*/
|
||||||
|
public ParkourGenerator generator() {
|
||||||
|
return generator;
|
||||||
|
}
|
||||||
|
|
||||||
public void setGenerator(@NotNull ParkourGenerator generator) {
|
public void setGenerator(@NotNull ParkourGenerator generator) {
|
||||||
this.generator = generator;
|
this.generator = generator;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ public class SessionStateManager {
|
||||||
// Stop tick cycle
|
// Stop tick cycle
|
||||||
stopTickCycle();
|
stopTickCycle();
|
||||||
|
|
||||||
if (session.generator != null) {
|
if (session.generator() != null) {
|
||||||
session.generator.reset(false);
|
session.generator().reset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider.remove(session);
|
Divider.remove(session);
|
||||||
|
|
@ -70,19 +70,19 @@ public class SessionStateManager {
|
||||||
* Start tick cycle for generator.
|
* Start tick cycle for generator.
|
||||||
*/
|
*/
|
||||||
private void startTickCycle() {
|
private void startTickCycle() {
|
||||||
if (session.generator == null || tickTask != null) {
|
if (session.generator() == null || tickTask != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tickTask = Scheduler.get(LoParkour.getPlugin()).runTimer(() -> {
|
tickTask = Scheduler.get(LoParkour.getPlugin()).runTimer(() -> {
|
||||||
if (session.generator != null) {
|
if (session.generator() != null) {
|
||||||
try {
|
try {
|
||||||
session.generator.tick();
|
session.generator().tick();
|
||||||
if (LoParkour.getGhostService() != null) {
|
if (LoParkour.getGhostService() != null) {
|
||||||
LoParkour.getGhostService().recordPlayers(session.generator);
|
LoParkour.getGhostService().recordPlayers(session.generator());
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
session.generator.session.getPlayers().forEach(p ->
|
session.generator().session.getPlayers().forEach(p ->
|
||||||
p.player.sendMessage("§c[LoParkour] Tick error: " + t.getMessage()));
|
p.player.sendMessage("§c[LoParkour] Tick error: " + t.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,14 +92,14 @@ class SessionUserManager {
|
||||||
|
|
||||||
private void notifyJoin(ParkourPlayer player) {
|
private void notifyJoin(ParkourPlayer player) {
|
||||||
for (final ParkourPlayer to : getPlayers()) {
|
for (final ParkourPlayer to : getPlayers()) {
|
||||||
to.send(Locales.getString(player.locale, "lobby.other_join").formatted(player.getName()));
|
to.send(Locales.getString(player.locale(), "lobby.other_join").formatted(player.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyLeave(ParkourPlayer[] toRemove, List<ParkourPlayer> remainingPlayers) {
|
private void notifyLeave(ParkourPlayer[] toRemove, List<ParkourPlayer> remainingPlayers) {
|
||||||
for (final ParkourPlayer player : toRemove) {
|
for (final ParkourPlayer player : toRemove) {
|
||||||
for (final ParkourPlayer to : remainingPlayers) {
|
for (final ParkourPlayer to : remainingPlayers) {
|
||||||
to.send(Locales.getString(player.locale, "lobby.other_leave").formatted(player.getName()));
|
to.send(Locales.getString(player.locale(), "lobby.other_leave").formatted(player.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
package dev.loki.loparkour.storage.disk;
|
package dev.loki.loparkour.storage.disk;
|
||||||
|
|
||||||
import com.google.gson.annotations.Expose;
|
import com.google.gson.annotations.Expose;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.leaderboard.model.Score;
|
import dev.loki.loparkour.leaderboard.model.Score;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.player.service.PlayerSettingsManager;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
@ -25,6 +28,14 @@ public final class StorageDisk {
|
||||||
private StorageDisk() {
|
private StorageDisk() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Type SETTINGS_TYPE = new TypeToken<Map<String, Object>>() { }.getType();
|
||||||
|
|
||||||
|
/** Files written before the settings had their own object named three of them differently. */
|
||||||
|
private static final Map<String, String> LEGACY_KEYS = Map.of(
|
||||||
|
"particles", "useParticles",
|
||||||
|
"useSpecialBlocks", "useSpecial",
|
||||||
|
"showFallMessage", "showFallMsg");
|
||||||
|
|
||||||
public static @NotNull Map<UUID, Score> readScores(@NotNull String mode) {
|
public static @NotNull Map<UUID, Score> readScores(@NotNull String mode) {
|
||||||
final File file = getLeaderboardFile(mode);
|
final File file = getLeaderboardFile(mode);
|
||||||
|
|
||||||
|
|
@ -77,56 +88,46 @@ public final class StorageDisk {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void readPlayer(@NotNull ParkourPlayer player) {
|
public static void readPlayer(@NotNull ParkourPlayer player) {
|
||||||
if (!getPlayerFile(player).exists()) {
|
final File file = getPlayerFile(player);
|
||||||
player.setSettings(new HashMap<>());
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
PlayerSettingsManager.applyDefaults(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileReader reader = new FileReader(getPlayerFile(player))) {
|
try (FileReader reader = new FileReader(file)) {
|
||||||
final ParkourPlayer from = LoParkour.getGson().fromJson(reader, ParkourPlayer.class);
|
final Map<String, Object> settings = LoParkour.getGson().fromJson(reader, SETTINGS_TYPE);
|
||||||
|
|
||||||
if (from == null) {
|
if (settings == null) {
|
||||||
LoParkour.getPlugin().getLogger().warning(
|
LoParkour.getPlugin().getLogger().warning(
|
||||||
"Player data file for %s is empty or corrupted, using defaults".formatted(player.getName()));
|
"Player data file for %s is empty or corrupted, using defaults".formatted(player.getName()));
|
||||||
player.setSettings(new HashMap<>());
|
PlayerSettingsManager.applyDefaults(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, Object> settings = new HashMap<>();
|
normalizeLegacyKeys(settings);
|
||||||
|
PlayerSettingsManager.applySettings(player, settings);
|
||||||
settings.put("style", from.style);
|
|
||||||
settings.put("blockLead", from.blockLead);
|
|
||||||
settings.put("useParticles", from.particles);
|
|
||||||
settings.put("useSpecial", from.useSpecialBlocks);
|
|
||||||
settings.put("showFallMsg", from.showFallMessage);
|
|
||||||
settings.put("showScoreboard", from.showScoreboard);
|
|
||||||
settings.put("selectedTime", from.selectedTime);
|
|
||||||
settings.put("ghostRecording", from.ghostRecording);
|
|
||||||
settings.put("collectedRewards", from.collectedRewards);
|
|
||||||
|
|
||||||
// Sanitize legacy Boolean locale values
|
|
||||||
String locale = from.locale;
|
|
||||||
if (locale == null || "true".equals(locale) || "false".equals(locale) || "1".equals(locale) || "0".equals(locale)) {
|
|
||||||
locale = "en";
|
|
||||||
}
|
|
||||||
settings.put("locale", locale);
|
|
||||||
settings.put("schematicDifficulty", from.schematicDifficulty);
|
|
||||||
settings.put("sound", from.sound);
|
|
||||||
|
|
||||||
player.setSettings(settings);
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LoParkour.getPlugin().getLogger().severe(
|
LoParkour.getPlugin().getLogger().severe(
|
||||||
"Error while trying to read disk data of %s".formatted(player.getName()) + " - " + ex.getMessage());
|
"Error while trying to read disk data of %s".formatted(player.getName()) + " - " + ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void normalizeLegacyKeys(Map<String, Object> settings) {
|
||||||
|
LEGACY_KEYS.forEach((legacy, key) -> {
|
||||||
|
if (!settings.containsKey(key) && settings.containsKey(legacy)) {
|
||||||
|
settings.put(key, settings.remove(legacy));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void writePlayer(@NotNull ParkourPlayer player) {
|
public static void writePlayer(@NotNull ParkourPlayer player) {
|
||||||
final File file = getPlayerFile(player);
|
final File file = getPlayerFile(player);
|
||||||
|
|
||||||
createFile(file);
|
createFile(file);
|
||||||
|
|
||||||
try (FileWriter writer = new FileWriter(file)) {
|
try (FileWriter writer = new FileWriter(file)) {
|
||||||
LoParkour.getGson().toJson(player, writer);
|
LoParkour.getGson().toJson(PlayerSettingsManager.toSettingsMap(player), writer);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LoParkour.getPlugin().getLogger().severe(
|
LoParkour.getPlugin().getLogger().severe(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.google.gson.reflect.TypeToken;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.leaderboard.model.Score;
|
import dev.loki.loparkour.leaderboard.model.Score;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.player.service.PlayerSettingsManager;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
|
@ -89,8 +90,8 @@ public final class SQLDataMapper {
|
||||||
public static void setPlayerParameters(@NotNull PreparedStatement stmt, @NotNull ParkourPlayer player) throws SQLException {
|
public static void setPlayerParameters(@NotNull PreparedStatement stmt, @NotNull ParkourPlayer player) throws SQLException {
|
||||||
stmt.setString(1, player.player.getUniqueId().toString());
|
stmt.setString(1, player.player.getUniqueId().toString());
|
||||||
stmt.setString(2, player.player.getName());
|
stmt.setString(2, player.player.getName());
|
||||||
stmt.setString(LOCALE_PARAM_INDEX, player.locale);
|
stmt.setString(LOCALE_PARAM_INDEX, player.locale());
|
||||||
stmt.setString(STYLE_PARAM_INDEX, player.style);
|
stmt.setString(STYLE_PARAM_INDEX, player.settings().style());
|
||||||
stmt.setString(SETTINGS_PARAM_INDEX, serializeSettings(player));
|
stmt.setString(SETTINGS_PARAM_INDEX, serializeSettings(player));
|
||||||
stmt.setLong(TIMESTAMP_PARAM_INDEX, System.currentTimeMillis());
|
stmt.setLong(TIMESTAMP_PARAM_INDEX, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
@ -99,49 +100,31 @@ public final class SQLDataMapper {
|
||||||
* Apply player data to ParkourPlayer object.
|
* Apply player data to ParkourPlayer object.
|
||||||
*/
|
*/
|
||||||
public static void applyPlayerData(@NotNull ParkourPlayer player, @NotNull PlayerData data) {
|
public static void applyPlayerData(@NotNull ParkourPlayer player, @NotNull PlayerData data) {
|
||||||
// Sanitize legacy Boolean locale values
|
player.setLocale(PlayerSettingsManager.sanitizeLocale(data.locale()));
|
||||||
String locale = data.locale();
|
player.settings().setStyle(data.styleName());
|
||||||
if (locale == null || "true".equals(locale) || "false".equals(locale) || "1".equals(locale) || "0".equals(locale)) {
|
|
||||||
locale = "en";
|
|
||||||
}
|
|
||||||
player.locale = locale;
|
|
||||||
player.style = data.styleName();
|
|
||||||
|
|
||||||
// Apply settings if available
|
// Apply settings if available
|
||||||
if (data.settings() != null && !data.settings().isEmpty()) {
|
if (data.settings() != null && !data.settings().isEmpty()) {
|
||||||
deserializeSettings(player, data.settings());
|
deserializeSettings(player, data.settings());
|
||||||
} else {
|
} else {
|
||||||
player.setSettings(new HashMap<>());
|
PlayerSettingsManager.applyDefaults(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String serializeSettings(@NotNull ParkourPlayer player) {
|
private static String serializeSettings(@NotNull ParkourPlayer player) {
|
||||||
final Map<String, Object> settings = new HashMap<>();
|
return LoParkour.getGson().toJson(PlayerSettingsManager.toSettingsMap(player));
|
||||||
settings.put("style", player.style);
|
|
||||||
settings.put("blockLead", player.blockLead);
|
|
||||||
settings.put("useParticles", player.particles);
|
|
||||||
settings.put("useSpecial", player.useSpecialBlocks);
|
|
||||||
settings.put("showFallMsg", player.showFallMessage);
|
|
||||||
settings.put("showScoreboard", player.showScoreboard);
|
|
||||||
settings.put("selectedTime", player.selectedTime);
|
|
||||||
settings.put("ghostRecording", player.ghostRecording);
|
|
||||||
settings.put("collectedRewards", player.collectedRewards);
|
|
||||||
settings.put("locale", player.locale);
|
|
||||||
settings.put("schematicDifficulty", player.schematicDifficulty);
|
|
||||||
settings.put("sound", player.sound);
|
|
||||||
return LoParkour.getGson().toJson(settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deserializeSettings(@NotNull ParkourPlayer player, @NotNull String settings) {
|
private static void deserializeSettings(@NotNull ParkourPlayer player, @NotNull String settings) {
|
||||||
final Type type = new TypeToken<Map<String, Object>>() { }.getType();
|
final Type type = new TypeToken<Map<String, Object>>() { }.getType();
|
||||||
final Map<String, Object> parsed = LoParkour.getGson().fromJson(settings, type);
|
final Map<String, Object> parsed = LoParkour.getGson().fromJson(settings, type);
|
||||||
if (parsed == null) {
|
if (parsed == null) {
|
||||||
player.setSettings(new HashMap<>());
|
PlayerSettingsManager.applyDefaults(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
normalizeNumericSettings(parsed);
|
normalizeNumericSettings(parsed);
|
||||||
player.setSettings(parsed);
|
PlayerSettingsManager.applySettings(player, parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void normalizeNumericSettings(@NotNull Map<String, Object> settings) {
|
private static void normalizeNumericSettings(@NotNull Map<String, Object> settings) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.loki.loparkour.storage.sql.repo;
|
package dev.loki.loparkour.storage.sql.repo;
|
||||||
|
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.player.service.PlayerSettingsManager;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
|
@ -39,7 +40,7 @@ public final class SQLPlayerRepository {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
SQLDataMapper.applyPlayerData(player, data);
|
SQLDataMapper.applyPlayerData(player, data);
|
||||||
} else {
|
} else {
|
||||||
player.setSettings(new java.util.HashMap<>());
|
PlayerSettingsManager.applyDefaults(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,28 +22,28 @@ public class ParticleData<T> {
|
||||||
this.size = size;
|
this.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParticleData<T> size(int size) {
|
public ParticleData<T> size(int value) {
|
||||||
this.size = size;
|
size = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParticleData<T> speed(double speed) {
|
public ParticleData<T> speed(double value) {
|
||||||
this.speed = speed;
|
speed = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParticleData<T> offsetX(double offsetX) {
|
public ParticleData<T> offsetX(double value) {
|
||||||
this.offsetX = offsetX;
|
offsetX = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParticleData<T> offsetY(double offsetY) {
|
public ParticleData<T> offsetY(double value) {
|
||||||
this.offsetY = offsetY;
|
offsetY = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParticleData<T> offsetZ(double offsetZ) {
|
public ParticleData<T> offsetZ(double value) {
|
||||||
this.offsetZ = offsetZ;
|
offsetZ = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,22 +38,22 @@ public final class World {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var world = Bukkit.getWorld(name);
|
final var existingWorld = Bukkit.getWorld(name);
|
||||||
|
|
||||||
if (!Config.CONFIG.getBoolean("joining")) {
|
if (!Config.CONFIG.getBoolean("joining")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (world != null) {
|
if (existingWorld != null) {
|
||||||
LoParkour.getPlugin().getLogger().warning(
|
LoParkour.getPlugin().getLogger().warning(
|
||||||
"Crash detected! The parkour world loading twice is not usual behaviour. " +
|
"Crash detected! The parkour world loading twice is not usual behaviour. " +
|
||||||
"This only happens after a server crash.");
|
"This only happens after a server crash.");
|
||||||
World.world = world; // ensure static field is populated
|
world = existingWorld; // ensure static field is populated
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.CONFIG.getBoolean("world.delete-on-reload")) {
|
if (Config.CONFIG.getBoolean("world.delete-on-reload")) {
|
||||||
deleteWorld();
|
deleteWorld();
|
||||||
} else if (world != null) {
|
} else if (existingWorld != null) {
|
||||||
// Already loaded (e.g. pre-created by a worlds plugin, required on Folia) — do not recreate.
|
// Already loaded (e.g. pre-created by a worlds plugin, required on Folia) — do not recreate.
|
||||||
LoParkour.log("Using already loaded world " + name);
|
LoParkour.log("Using already loaded world " + name);
|
||||||
setup();
|
setup();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue