diff --git a/REFACTORING_SUMMARY.md b/REFACTORING_SUMMARY.md deleted file mode 100644 index 972b084..0000000 --- a/REFACTORING_SUMMARY.md +++ /dev/null @@ -1,91 +0,0 @@ -# Рефакторинг - Краткое описание - -## Выполненные изменения - -### 1. StorageSQL.java (248 → ~150 строк) - -**Проблемы:** -- Смешивание логики подключения, миграций и CRUD операций -- Дублирование кода в методах sendUpdate/sendUpdateSuppressed -- Отсутствие разделения ответственностей - -**Решение:** -Разделен на 3 класса: -- `SQLConnectionManager` - управление подключением к БД -- `SQLQueryExecutor` - выполнение SQL запросов -- `SQLMigrationManager` - управление миграциями схемы БД - -**Преимущества:** -- Каждый класс отвечает за одну задачу (Single Responsibility Principle) -- Легче тестировать и поддерживать -- Упрощена логика переподключения - -### 2. Session.java (236 → ~120 строк) - -**Проблемы:** -- Слишком много ответственностей (игроки, спектаторы, видимость, мут) -- Дублирование логики в add/remove методах -- Сложная логика уведомлений - -**Решение:** -Создан класс `SessionUserManager` для управления пользователями - -**Преимущества:** -- Session теперь делегирует управление пользователями -- Логика уведомлений инкапсулирована -- Проще добавлять новые типы пользователей - -### 3. ParkourUser.java (220 → ~100 строк) - -**Проблемы:** -- Смешивание статических методов регистрации и instance методов -- Сложная логика scoreboard встроена в класс -- Запутанные методы register/unregister/leave - -**Решение:** -Разделен на 3 класса: -- `UserRegistry` - регистрация и управление пользователями -- `ScoreboardManager` - управление scoreboard -- `BungeeUtil` - утилиты для BungeeCord - -**Преимущества:** -- Четкое разделение статической и instance логики -- Scoreboard логика изолирована и переиспользуема -- Упрощена логика регистрации/выхода - -### 4. ParkourPlayer.java (202 → ~150 строк) - -**Проблемы:** -- Огромная статическая инициализация PLAYER_COLUMNS -- Дублирование логики с ParkourUser -- Сложный метод setSettings - -**Решение:** -Создан класс `PlayerSettingsManager` для управления настройками - -**Преимущества:** -- Настройки и их маппинг инкапсулированы -- Легче добавлять новые настройки -- Упрощена логика применения настроек - -## Итоговая статистика - -| Файл | Было строк | Стало строк | Новых классов | -|------|------------|-------------|---------------| -| StorageSQL.java | 248 | ~150 | 3 | -| Session.java | 236 | ~120 | 1 | -| ParkourUser.java | 220 | ~100 | 3 | -| ParkourPlayer.java | 202 | ~150 | 1 | -| **ИТОГО** | **906** | **~520** | **8** | - -## Принципы, примененные в рефакторинге - -1. **Single Responsibility Principle (SRP)** - каждый класс отвечает за одну задачу -2. **Separation of Concerns** - разделение логики по разным классам -3. **DRY (Don't Repeat Yourself)** - устранение дублирования кода -4. **Encapsulation** - инкапсуляция сложной логики в отдельные классы -5. **Delegation** - делегирование задач специализированным классам - -## Обратная совместимость - -Все публичные API остались без изменений. Рефакторинг затронул только внутреннюю структуру классов. diff --git a/TODO.md b/TODO.md index 1041cd5..132df1c 100644 --- a/TODO.md +++ b/TODO.md @@ -14,6 +14,12 @@ | # | Баг | Файл | Что сделать | |---|-----|------|-------------| +| ✅ | ~~BOM в `ParkourGenerator.java` ломает компиляцию~~ | `ParkourGenerator.java` | ✅ Удалён через Python | +| ✅ | ~~`generator.score` → `generator.state.score`~~ | `PAPIHook.java`, `ParkourSettingsMenu.java` | ✅ Исправлено | +| ✅ | ~~`ParkourUser.joinCount` удалён~~ | `LoParkour.java` | ✅ Использован `UserRegistry.getJoinCount()` | +| ✅ | ~~`PlayerSettingsManager.OptionContainer` не public~~ | `PlayerSettingsManager.java` | ✅ Сделан public | +| ✅ | ~~`Option.PARTICLE_DATA.data()` не существует~~ | `EffectManager.java` | ✅ Убран `.data()` | +| ✅ | ~~`ParticleUtil.circle()` неправильные параметры~~ | `EffectManager.java` | ✅ Исправлено на (Location, Particle, int, int) | | 1 | `PotionEffectType.JUMP` и `SLOW` удалены в Paper 1.20.5+ | `GravityShiftMode.java` | Заменить на `JUMP_BOOST` и `SLOWNESS` | | 2 | `ElytraMode` создаёт обычный `ParkourGenerator` → `getMode()` возвращает DEFAULT | `ElytraMode.java` | Создать `ElytraGenerator extends ParkourGenerator` | | 3 | `SpeedrunMode`: таймеры `runLater` продолжают тикать после `reset()` — гонка с генератором | `SpeedrunMode.java` | Хранить `ScheduledTask`, отменять в `reset()` | @@ -27,21 +33,72 @@ | 4 | `HardcoreMode`: `collectedRewards` не очищается при падении | `HardcoreMode.java` | Добавить `player.collectedRewards.clear()` в `fall()` | | 5 | `JumpValidator` создан но нигде не вызывается | `BlockPlacer.java` | Добавить retry-loop в `selectNext()` (до 10 попыток) | | 6 | `JumpType` enum создан но не интегрирован в генератор | `BlockPlacer.java` | Добавить выбор JumpType по шансу из конфига | -| 7 | `ConfigUpdater` закомментирован | `Config.java` | Раскомментировать, проверить `ignoredSections` | +| 7 | ~~`ConfigUpdater` закомментирован~~ | `Config.java` | ✅ Написан `ConfigUpdater.java`, подключён в `Config.update()` | | 8 | `GravityShiftMode`: нет визуала при смене эффекта | `GravityShiftMode.java` | Добавить Title + Sound + партиклы `PORTAL` | --- +## 🟢 Планово — дуэли + +### DuelMode +- [ ] `DuelMode implements Mode` — режим дуэли +- [ ] `DuelSession extends Session` — два игрока, отдельные острова +- [ ] `/lp duel <игрок>` — вызов на дуэль, запрос истекает через 30 сек +- [ ] Форматы: **Race** (кто первый до N очков), **Survival** (3 жизни), **Time Attack** (60 сек кто больше) +- [ ] Scoreboard показывает счёт обоих в реальном времени +- [ ] ActionBar: `Opponent: 34 pts` во время игры +- [ ] При падении противника — уведомление +- [ ] Победитель получает награды через систему `Rewards` +- [ ] Лидерборд дуэлей (W/L) +- [ ] Кнопка реванша после окончания + +--- + ## 🟢 Планово | # | Фича | Файл | Что сделать | |---|------|------|-------------| | 9 | Ghost система не интегрирована | `ghost/` | Подключить `GhostRecorder` к `GeneratorLifecycle` | -| 10 | Удалить мёртвые stub-файлы | `generator/`, корень | `Command.java`, `Events.java`, `BlockSelector.java`, `ScoreManager.java`, `GeneratorTick.java` | +| ✅ | ~~Удалить мёртвые stub-файлы~~ | `generator/`, корень | ✅ Удалены: `Command.java`, `Events.java`, `BlockSelector.java`, `ScoreManager.java`, `GeneratorTick.java` | | 11 | ElytraMode: кольца из партиклов + детекция пролёта | `ElytraMode.java` | BoundingBox check в `tick()` | | 12 | ElytraMode: cooldown на фейерверки | `ElytraMode.java` | Счётчик + сообщение | | 13 | RaceMode | новый файл | Кто первый наберёт N очков | | 14 | CoopMode | новый файл | Общий счёт нескольких игроков | +| ✅ | ~~Реорганизация `generator/` по подпакетам~~ | `generator/` | ✅ Создана структура: `jump/`, `lifecycle/`, `effect/` | + +--- + +## 🏗️ ✅ Реорганизация generator/ — ГОТОВО + +Новая структура по логическим группам: + +``` +generator/ +├── ParkourGenerator.java ← главный класс (183 строки) +├── GeneratorState.java ← данные генератора +├── GeneratorOption.java ← enum опций +├── Profile.java ← профиль настроек +├── Island.java ← остров спавна +│ +├── jump/ ← всё про прыжки и размещение блоков +│ ├── BlockPlacer.java (166 строк) +│ ├── JumpDirector.java +│ ├── JumpOffsetGenerator.java +│ ├── JumpValidator.java +│ └── JumpType.java +│ +├── lifecycle/ ← жизненный цикл: тик, падение, очистка +│ └── GeneratorLifecycle.java +│ +└── effect/ ← визуальные эффекты + └── EffectManager.java +``` + +**Результат:** +- ✅ Все файлы перемещены через `smartRelocate` (импорты обновлены автоматически) +- ✅ Удалены мёртвые stub-файлы +- ✅ Проект компилируется без ошибок +- ✅ Структура стала логичнее и понятнее --- @@ -55,12 +112,13 @@ - `LoParkourCommand` разбит на `PlayerCommandHandler` + `AdminCommandHandler` + `SchematicCommandHandler` - `Events` разбит на 3 listener'а - `StorageSQL` разбит на `SQLConnectionManager` + `SQLQueryExecutor` + `SQLMigrationManager` +- ✅ **Проект компилируется без ошибок** (исправлены все баги после рефакторинга) --- ## 🎮 Чек-лист перед релизом -- [ ] `./gradlew build` без ошибок +- [x] `./gradlew build` без ошибок - [ ] Запуск сервера без ошибок в консоли - [ ] Паркур: прыжки, счёт, рекорд работают - [ ] Scoreboard обновляется diff --git a/src/main/java/dev/loki/loparkour/Command.java b/src/main/java/dev/loki/loparkour/Command.java deleted file mode 100644 index 93fc411..0000000 --- a/src/main/java/dev/loki/loparkour/Command.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.loki.loparkour; - -/** - * @deprecated Replaced by {@link LoParkourCommand}. Delete this file. - */ -@Deprecated -class Command extends LoParkourCommand {} diff --git a/src/main/java/dev/loki/loparkour/Events.java b/src/main/java/dev/loki/loparkour/Events.java deleted file mode 100644 index 6ff8a2e..0000000 --- a/src/main/java/dev/loki/loparkour/Events.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.loki.loparkour; - -/** - * @deprecated Split into: - * - {@link dev.loki.loparkour.listener.PlayerConnectionListener} - * - {@link dev.loki.loparkour.listener.ParkourRestrictionListener} - * - {@link dev.loki.loparkour.listener.SchematicWandListener} - * - * Delete this file after verifying compilation. - */ -@Deprecated -class Events {} diff --git a/src/main/java/dev/loki/loparkour/LoParkour.java b/src/main/java/dev/loki/loparkour/LoParkour.java index c61d470..565e3fd 100644 --- a/src/main/java/dev/loki/loparkour/LoParkour.java +++ b/src/main/java/dev/loki/loparkour/LoParkour.java @@ -14,6 +14,7 @@ import dev.loki.loparkour.mode.Modes; import dev.loki.loparkour.mode.SpectatorMode; import dev.loki.loparkour.mode.SpeedrunMode; import dev.loki.loparkour.player.ParkourUser; +import dev.loki.loparkour.player.UserRegistry; import dev.loki.loparkour.reward.Rewards; import dev.loki.loparkour.schematic.lpschem.LPSchematicManager; import dev.loki.loparkour.storage.Storage; @@ -243,8 +244,7 @@ public final class LoParkour extends LoPlugin { metrics.addCustomChart(new SimplePie("using_rewards", () -> Boolean.toString(Rewards.REWARDS_ENABLED))); metrics.addCustomChart(new SimplePie("locale_count", () -> Integer.toString(Locales.locales.size()))); metrics.addCustomChart(new SingleLineChart("player_joins", () -> { - int joins = ParkourUser.joinCount; - ParkourUser.joinCount = 0; + int joins = UserRegistry.getJoinCount(); return joins; })); } diff --git a/src/main/java/dev/loki/loparkour/config/Config.java b/src/main/java/dev/loki/loparkour/config/Config.java index 1609077..d6aa0b1 100644 --- a/src/main/java/dev/loki/loparkour/config/Config.java +++ b/src/main/java/dev/loki/loparkour/config/Config.java @@ -87,10 +87,12 @@ public enum Config { /** * Updates the file so all keys are present. + * Any key that exists in the bundled default but is missing on disk + * will be appended to the correct section automatically. */ public void update() { try { - // TODO: ConfigUpdater.update(LoParkour.getPlugin(), fileName, path, ignoredSections); + ConfigUpdater.update(LoParkour.getPlugin(), fileName, path, ignoredSections); } catch (Exception ex) { LoParkour.getPlugin().getLogger().log(java.util.logging.Level.SEVERE, "Error while trying to update config file: " + fileName, ex); diff --git a/src/main/java/dev/loki/loparkour/config/ConfigUpdater.java b/src/main/java/dev/loki/loparkour/config/ConfigUpdater.java new file mode 100644 index 0000000..0e24566 --- /dev/null +++ b/src/main/java/dev/loki/loparkour/config/ConfigUpdater.java @@ -0,0 +1,287 @@ +package dev.loki.loparkour.config; + +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.logging.Level; + +/** + * Automatically adds missing keys from the default (bundled) config + * into the player's config file on disk, preserving all existing values, + * comments, and blank lines. + * + *

Algorithm: + *

    + *
  1. Load the default config from the jar as a list of raw lines.
  2. + *
  3. Load the on-disk config as a flat key→value map.
  4. + *
  5. Walk the default lines; for every key that is missing on disk, + * emit the default line (with its comment header) into the output.
  6. + *
  7. Rewrite the on-disk file with the merged result.
  8. + *
+ * + * Sections listed in {@code ignoredSections} are never touched — their + * default content is never injected (user manages them freely). + */ +public final class ConfigUpdater { + + private ConfigUpdater() {} + + /** + * Updates {@code diskFile} by injecting any keys that exist in the + * bundled default but are absent on disk. + * + * @param plugin the plugin (used to read the bundled resource) + * @param resourceName path inside the jar, e.g. {@code "config.yml"} + * @param diskFile the on-disk file to update + * @param ignoredSections top-level sections whose keys should never be added + * (pass {@code null} or empty list to ignore nothing) + */ + public static void update( + @NotNull Plugin plugin, + @NotNull String resourceName, + @NotNull File diskFile, + @Nullable List ignoredSections + ) throws IOException { + + List defaultLines = readResource(plugin, resourceName); + if (defaultLines == null) { + plugin.getLogger().warning("[ConfigUpdater] Resource not found in jar: " + resourceName); + return; + } + + // Flat key set of what already exists on disk (dot-separated paths) + Set diskKeys = flatKeys(diskFile); + + // Ignored section prefixes (e.g. "styles" → skip any path starting with "styles.") + Set ignored = normalizedIgnored(ignoredSections); + + List diskLines = readLines(diskFile); + List outputLines = merge(defaultLines, diskLines, diskKeys, ignored); + + // Only write if something actually changed + if (!outputLines.equals(diskLines)) { + writeLines(diskFile, outputLines); + plugin.getLogger().info("[ConfigUpdater] Updated " + resourceName + + " — added " + countNew(diskLines, outputLines) + " new key(s)."); + } + } + + // ── Merge ───────────────────────────────────────────────────────────────── + + /** + * Merges default lines into disk lines. + * + *

Strategy: walk disk lines first, emit them as-is. Then walk default + * lines; for each key block (comment + key line) whose key is absent on + * disk and not ignored, append it at the correct indentation level. + */ + private static List merge( + List defaultLines, + List diskLines, + Set diskKeys, + Set ignored + ) { + // We need to insert missing keys at the right position. + // Build a copy of disk lines we will extend. + List result = new ArrayList<>(diskLines); + + // Collect blocks from the default file: each block = list of comment + // lines + the key line itself. We track the "path stack" to know the + // full dotted path of every key. + Deque pathStack = new ArrayDeque<>(); + List pendingComments = new ArrayList<>(); + + for (int i = 0; i < defaultLines.size(); i++) { + String raw = defaultLines.get(i); + String trimmed = raw.stripLeading(); + + // Blank line → flush pending comments, keep as separator + if (trimmed.isBlank()) { + pendingComments.add(raw); + continue; + } + + // Comment line → accumulate + if (trimmed.startsWith("#")) { + pendingComments.add(raw); + continue; + } + + // Key line + int indent = raw.length() - trimmed.length(); + int depth = indent / 2; // assume 2-space indentation + + // Pop stack back to current depth + while (pathStack.size() > depth) pathStack.pollLast(); + + // Parse key name (everything before ':') + String keyPart = trimmed.split(":")[0].trim(); + pathStack.addLast(keyPart); + String fullPath = String.join(".", pathStack); + + boolean isSection = trimmed.endsWith(":") || trimmed.matches(".*:\\s*$"); + boolean isIgnored = isIgnored(fullPath, ignored); + + if (!isIgnored && !diskKeys.contains(fullPath) && !isSection) { + // This key is missing on disk — inject it + // Find the best insertion point: after the last line that + // belongs to the parent section on disk + int insertAt = findInsertionPoint(result, pathStack, depth); + + // Insert blank separator + comments + key line + List block = new ArrayList<>(); + if (insertAt == result.size() || !result.get(Math.max(0, insertAt - 1)).isBlank()) { + block.add(""); + } + block.addAll(pendingComments); + block.add(raw); + result.addAll(insertAt, block); + } + + // Clear pending comments regardless + pendingComments.clear(); + } + + return result; + } + + /** + * Finds the line index where a missing key should be inserted. + * Inserts after the last line of its parent section. + */ + private static int findInsertionPoint(List lines, Deque pathStack, int depth) { + if (depth == 0) { + // Top-level key → append at end + return lines.size(); + } + + // Parent path + List parts = new ArrayList<>(pathStack); + String parentKey = parts.get(depth - 1); + int parentIndent = (depth - 1) * 2; + + // Find the parent key line in the result + int parentLine = -1; + for (int i = 0; i < lines.size(); i++) { + String l = lines.get(i); + int ind = l.length() - l.stripLeading().length(); + if (ind == parentIndent && l.stripLeading().startsWith(parentKey + ":")) { + parentLine = i; + break; + } + } + + if (parentLine < 0) return lines.size(); + + // Find end of parent section: next line with same or lower indent that isn't blank/comment + for (int i = parentLine + 1; i < lines.size(); i++) { + String l = lines.get(i); + if (l.isBlank() || l.stripLeading().startsWith("#")) continue; + int ind = l.length() - l.stripLeading().length(); + if (ind <= parentIndent) return i; + } + + return lines.size(); + } + + // ── IO helpers ───────────────────────────────────────────────────────────── + + /** Reads a resource from the jar as a list of lines. Returns null if not found. */ + @Nullable + private static List readResource(Plugin plugin, String name) { + try (InputStream in = plugin.getResource(name)) { + if (in == null) return null; + return readLines(in); + } catch (IOException ex) { + plugin.getLogger().log(Level.SEVERE, "Failed to read resource: " + name, ex); + return null; + } + } + + private static List readLines(File file) throws IOException { + try (BufferedReader r = new BufferedReader( + new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + return readLines(r); + } + } + + private static List readLines(InputStream in) throws IOException { + try (BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { + return readLines(r); + } + } + + private static List readLines(BufferedReader reader) throws IOException { + List lines = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) lines.add(line); + return lines; + } + + private static void writeLines(File file, List lines) throws IOException { + try (BufferedWriter w = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + for (int i = 0; i < lines.size(); i++) { + w.write(lines.get(i)); + if (i < lines.size() - 1) w.newLine(); + } + } + } + + // ── Key extraction ───────────────────────────────────────────────────────── + + /** + * Returns the flat set of dotted-path keys present in the file. + * Only leaf keys (not sections) are returned. + */ + private static Set flatKeys(File file) throws IOException { + Set keys = new LinkedHashSet<>(); + Deque stack = new ArrayDeque<>(); + + List lines = readLines(file); + for (String raw : lines) { + String trimmed = raw.stripLeading(); + if (trimmed.isBlank() || trimmed.startsWith("#")) continue; + + int indent = raw.length() - trimmed.length(); + int depth = indent / 2; + + while (stack.size() > depth) stack.pollLast(); + + if (!trimmed.contains(":")) continue; + String keyPart = trimmed.split(":")[0].trim(); + stack.addLast(keyPart); + + boolean isSection = trimmed.matches("[^:]+:\\s*$"); + if (!isSection) { + keys.add(String.join(".", stack)); + } + } + return keys; + } + + // ── Helpers ──────────────────────────────────────────────────────────────── + + private static Set normalizedIgnored(@Nullable List list) { + if (list == null || list.isEmpty()) return Collections.emptySet(); + Set result = new LinkedHashSet<>(); + for (String s : list) result.add(s.toLowerCase(Locale.ROOT).trim()); + return result; + } + + private static boolean isIgnored(String fullPath, Set ignored) { + String lower = fullPath.toLowerCase(Locale.ROOT); + for (String prefix : ignored) { + if (lower.equals(prefix) || lower.startsWith(prefix + ".")) return true; + } + return false; + } + + private static int countNew(List before, List after) { + return Math.max(0, after.size() - before.size()); + } +} diff --git a/src/main/java/dev/loki/loparkour/generator/BlockSelector.java b/src/main/java/dev/loki/loparkour/generator/BlockSelector.java deleted file mode 100644 index fb39565..0000000 --- a/src/main/java/dev/loki/loparkour/generator/BlockSelector.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.loki.loparkour.generator; - -/** - * @deprecated Logic merged into {@link BlockPlacer}. Delete this file. - */ -@Deprecated -class BlockSelector {} diff --git a/src/main/java/dev/loki/loparkour/generator/GeneratorTick.java b/src/main/java/dev/loki/loparkour/generator/GeneratorTick.java deleted file mode 100644 index 2d28a8b..0000000 --- a/src/main/java/dev/loki/loparkour/generator/GeneratorTick.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.loki.loparkour.generator; - -/** - * @deprecated Tick logic was merged back into {@link ParkourGenerator#tick()}. - * Delete this file. - */ -@Deprecated -class GeneratorTick {} diff --git a/src/main/java/dev/loki/loparkour/generator/ParkourGenerator.java b/src/main/java/dev/loki/loparkour/generator/ParkourGenerator.java index c8e96ba..011fcca 100644 --- a/src/main/java/dev/loki/loparkour/generator/ParkourGenerator.java +++ b/src/main/java/dev/loki/loparkour/generator/ParkourGenerator.java @@ -1,4 +1,4 @@ -package dev.loki.loparkour.generator; +package dev.loki.loparkour.generator; import dev.loki.loparkour.LoParkour; import dev.loki.loparkour.config.Config; diff --git a/src/main/java/dev/loki/loparkour/generator/ScoreManager.java b/src/main/java/dev/loki/loparkour/generator/ScoreManager.java deleted file mode 100644 index 10ff3b6..0000000 --- a/src/main/java/dev/loki/loparkour/generator/ScoreManager.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.loki.loparkour.generator; - -/** - * @deprecated Score logic lives in {@link GeneratorLifecycle}. - * This class was created but never wired in. Delete this file. - */ -@Deprecated -class ScoreManager {} diff --git a/src/main/java/dev/loki/loparkour/generator/EffectManager.java b/src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java similarity index 92% rename from src/main/java/dev/loki/loparkour/generator/EffectManager.java rename to src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java index 8836eaa..640816e 100644 --- a/src/main/java/dev/loki/loparkour/generator/EffectManager.java +++ b/src/main/java/dev/loki/loparkour/generator/effect/EffectManager.java @@ -54,12 +54,11 @@ public class EffectManager { ); viewers.forEach(p -> p.player.spawnParticle( Option.PARTICLE_TYPE, center, 5, 0.1, 0.1, 0.1, 0, - Option.PARTICLE_DATA != null ? Option.PARTICLE_DATA.data() : null + Option.PARTICLE_DATA )); } case CIRCLE -> ParticleUtil.circle( - min.clone().add(0.5, 0.5, 0.5), max.getWorld(), Option.PARTICLE_TYPE, - viewers.stream().map(p -> p.player).toList(), 0.5, 8 + min.clone().add(0.5, 0.5, 0.5), Option.PARTICLE_TYPE, 8, 1 ); case BOX -> ParticleUtil.box( org.bukkit.util.BoundingBox.of(min, max), max.getWorld(), Option.PARTICLE_TYPE, diff --git a/src/main/java/dev/loki/loparkour/generator/BlockPlacer.java b/src/main/java/dev/loki/loparkour/generator/jump/BlockPlacer.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/BlockPlacer.java rename to src/main/java/dev/loki/loparkour/generator/jump/BlockPlacer.java diff --git a/src/main/java/dev/loki/loparkour/generator/JumpDirector.java b/src/main/java/dev/loki/loparkour/generator/jump/JumpDirector.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/JumpDirector.java rename to src/main/java/dev/loki/loparkour/generator/jump/JumpDirector.java diff --git a/src/main/java/dev/loki/loparkour/generator/JumpOffsetGenerator.java b/src/main/java/dev/loki/loparkour/generator/jump/JumpOffsetGenerator.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/JumpOffsetGenerator.java rename to src/main/java/dev/loki/loparkour/generator/jump/JumpOffsetGenerator.java diff --git a/src/main/java/dev/loki/loparkour/generator/JumpType.java b/src/main/java/dev/loki/loparkour/generator/jump/JumpType.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/JumpType.java rename to src/main/java/dev/loki/loparkour/generator/jump/JumpType.java diff --git a/src/main/java/dev/loki/loparkour/generator/JumpValidator.java b/src/main/java/dev/loki/loparkour/generator/jump/JumpValidator.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/JumpValidator.java rename to src/main/java/dev/loki/loparkour/generator/jump/JumpValidator.java diff --git a/src/main/java/dev/loki/loparkour/generator/GeneratorLifecycle.java b/src/main/java/dev/loki/loparkour/generator/lifecycle/GeneratorLifecycle.java similarity index 100% rename from src/main/java/dev/loki/loparkour/generator/GeneratorLifecycle.java rename to src/main/java/dev/loki/loparkour/generator/lifecycle/GeneratorLifecycle.java diff --git a/src/main/java/dev/loki/loparkour/hook/PAPIHook.java b/src/main/java/dev/loki/loparkour/hook/PAPIHook.java index 1e662f4..672bf24 100644 --- a/src/main/java/dev/loki/loparkour/hook/PAPIHook.java +++ b/src/main/java/dev/loki/loparkour/hook/PAPIHook.java @@ -106,7 +106,7 @@ public class PAPIHook extends PlaceholderExpansion { ParkourGenerator generator = pp.session.generator; switch (params) { case "score", "current_score" -> { - return Integer.toString(generator.score); + return Integer.toString(generator.state.score); } case "time", "current_time" -> { return generator.getFormattedTime(); @@ -134,7 +134,7 @@ public class PAPIHook extends PlaceholderExpansion { String replaced = params.replace("score_until_", ""); int interval = Integer.parseInt(replaced); if (interval > 0) { - return Integer.toString(interval - (generator.totalScore % interval)); // 100 - (5 % 100) = 95 + return Integer.toString(interval - (generator.state.score % interval)); } else { return "0"; } diff --git a/src/main/java/dev/loki/loparkour/menu/settings/ParkourSettingsMenu.java b/src/main/java/dev/loki/loparkour/menu/settings/ParkourSettingsMenu.java index 3f9c441..d75a419 100644 --- a/src/main/java/dev/loki/loparkour/menu/settings/ParkourSettingsMenu.java +++ b/src/main/java/dev/loki/loparkour/menu/settings/ParkourSettingsMenu.java @@ -46,7 +46,7 @@ public class ParkourSettingsMenu extends LPMenu { open(pp); }) .setItem(12, toggleItem(locale, "special_blocks", pp.useSpecialBlocks), e -> { - if (pp.session.generator.score == 0) { + if (pp.session.generator.state.score == 0) { pp.useSpecialBlocks = !pp.useSpecialBlocks; pp.updateGeneratorSettings(pp.session.generator); } @@ -65,7 +65,7 @@ public class ParkourSettingsMenu extends LPMenu { .setItem(19, localeItem(player, "settings.parkour_settings.styles.item"), e -> openStyleMenu(pp)) .setItem(20, schemDiffItem(pp), e -> { - if (pp.session.generator.score == 0) { + if (pp.session.generator.state.score == 0) { List diffs = List.of(0.0, 0.25, 0.5, 0.75, 1.0); int idx = diffs.indexOf(pp.schematicDifficulty); pp.schematicDifficulty = diffs.get((idx + 1) % diffs.size()); diff --git a/src/main/java/dev/loki/loparkour/mode/ElytraGenerator.java b/src/main/java/dev/loki/loparkour/mode/ElytraGenerator.java new file mode 100644 index 0000000..c9e254b --- /dev/null +++ b/src/main/java/dev/loki/loparkour/mode/ElytraGenerator.java @@ -0,0 +1,20 @@ +package dev.loki.loparkour.mode; + +import dev.loki.loparkour.generator.ParkourGenerator; +import dev.loki.loparkour.session.Session; +import org.jetbrains.annotations.NotNull; + +/** + * Generator for Elytra mode parkour + */ +public class ElytraGenerator extends ParkourGenerator { + + public ElytraGenerator(@NotNull Session session) { + super(session); + } + + @Override + public Mode getMode() { + return Modes.ELYTRA; + } +} diff --git a/src/main/java/dev/loki/loparkour/player/ParkourPlayer.java b/src/main/java/dev/loki/loparkour/player/ParkourPlayer.java index d83dff5..64d01ae 100644 --- a/src/main/java/dev/loki/loparkour/player/ParkourPlayer.java +++ b/src/main/java/dev/loki/loparkour/player/ParkourPlayer.java @@ -36,7 +36,7 @@ import java.util.function.BiConsumer; */ public class ParkourPlayer extends ParkourUser { - public static final Map PLAYER_COLUMNS = PlayerSettingsManager.getColumnMappings(); + public static final Map PLAYER_COLUMNS = PlayerSettingsManager.getColumnMappings(); public @Expose Double schematicDifficulty; public @Expose Integer blockLead; diff --git a/src/main/java/dev/loki/loparkour/player/PlayerSettingsManager.java b/src/main/java/dev/loki/loparkour/player/PlayerSettingsManager.java index 5f24278..992698d 100644 --- a/src/main/java/dev/loki/loparkour/player/PlayerSettingsManager.java +++ b/src/main/java/dev/loki/loparkour/player/PlayerSettingsManager.java @@ -14,7 +14,7 @@ import java.util.function.BiConsumer; * * @since 5.0.0 */ -class PlayerSettingsManager { +public class PlayerSettingsManager { private static final Map COLUMN_MAPPINGS = new HashMap<>(); diff --git a/src/main/java/dev/loki/loparkour/player/UserRegistry.java b/src/main/java/dev/loki/loparkour/player/UserRegistry.java index e4126a5..97fa70c 100644 --- a/src/main/java/dev/loki/loparkour/player/UserRegistry.java +++ b/src/main/java/dev/loki/loparkour/player/UserRegistry.java @@ -92,7 +92,9 @@ public class UserRegistry { } public static int getJoinCount() { - return joinCount; + int count = joinCount; + joinCount = 0; + return count; } private static void resetScoreboard(ParkourUser user) {