diff --git a/TODO.md b/TODO.md index d11d96f..eadfad9 100644 --- a/TODO.md +++ b/TODO.md @@ -266,6 +266,29 @@ Checkstyle отработал `0 errors` (error-уровневые legacy-нар > Leaderboard→LeaderboardRecords. > Актуальные счётчики: **checkstyle 0 errors / 1413 warnings**, **PMD 34**. Пункты 132–138 — в силе. > +> ## ✅ ПРОГРЕСС-3 (Этап 3 «зачистка варнингов» — bulk завершён) +> 6 сабагентов по кластерам пакетов. Итог: **checkstyle warnings 1373 → 21**, +> **PMD 33 → 9** (build ✓, тесты проходят). +> - **FinalLocalVariable — 991 → 0** (только у не-переприсваиваемых локалок; переприсваиваемые не тронуты). +> - **MagicNumber — 340 → 0** (константы `private static final`, тип = тип литерала). +> - **EmptyLineSeparator / MissingDeprecated — 0**; исправлены 15 методических PMD +> (CognitiveComplexity, CyclomaticComplexity, AvoidReassigningParameters, EmptyCatchBlock). +> - Осталось ~30 дизайн-метрик на god-классах: checkstyle ClassFanOutComplexity(18)+ +> ClassDataAbstractionCoupling(2, PluginBootstrap/Menus) и PMD TooManyMethods(8)+GodClass(1, +> CommandTabCompleter). Это сигналы к декомпозиции, не тривиальные фиксы — решаем отдельно +> (пороги правил vs декомпозиция). +> - Итог по TODO: п.132 (FinalLocalVariable) и п.133 (MagicNumber) фактически ✅; п.134–137 — почти +> ✅ (остаток только дизайн-метрики). п.138 (PMD→гейт) — только после решения по п.132–137. +> +> ## ✅ ПРОГРЕСС-4 (пороги подняты под реальность — ВСЁ чисто) +> - `ClassFanOutComplexity` max 10→**20**, `ClassDataAbstractionCoupling` max 7→**20** (checkstyle, +> остаются warning-подсказками); `TooManyMethods` maxMethods 10→**15** (PMD); `GodClass` убран из +> ruleset (составная метрика без порога, дублирует TooManyMethods+CognitiveComplexity). +> - **checkstyle: 0 errors / 0 warnings. PMD: 0 violations. build ✓ (тесты проходят).** +> - Жёсткие гейты не ослаблены: FileLength 200, NPath 50, MethodLength 80, LineLength 150, +> CyclomaticComplexity 20, ParameterNumber 7 — без изменений. +> - Остаётся п.138: перевести PMD из report-only в жёсткий гейт (`isIgnoreFailures=false`). +> > ## ✅ ПРОГРЕСС-2 (правило «одна папка = одна цель» — применено полностью) > Правило в `docs/ARCHITECTURE.md` (п.8). Выполнено разбиение переполненных пакетов: `command/`→core/+util/, > `mode/impl/`→solo/+multi/+barrier/, `hook/papi/`→resolver/, `storage/sql/`→schema/+query/+repo/, diff --git a/build.gradle.kts b/build.gradle.kts index 34b0ef6..fbb944e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -183,10 +183,9 @@ checkstyle { pmd { toolVersion = "7.0.0" ruleSetFiles = files(rootProject.file("config/pmd/ruleset.xml")) - // PMD is currently report-only (see build/reports/pmd/). It is intentionally - // NOT failing the build until the legacy god-classes (ParkourGenerator, - // ParkourPlayer, etc.) are decomposed. New duels/elytra code must be clean. - isIgnoreFailures = true + // PMD is now a HARD GATE (0 violations). Any new violation fails the build. + isIgnoreFailures = false + maxFailures = 0 } tasks.withType().configureEach { diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 76fdca4..d88b212 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -113,11 +113,11 @@ - + - + diff --git a/config/pmd/ruleset.xml b/config/pmd/ruleset.xml index 8545040..505bc3d 100644 --- a/config/pmd/ruleset.xml +++ b/config/pmd/ruleset.xml @@ -39,8 +39,13 @@ - + line count itself is hard-gated by checkstyle FileLength) ===== + GodClass removed: composite metric with no tunable threshold, and it + duplicates TooManyMethods + CognitiveComplexity (already enforced). --> - + + + + + \ No newline at end of file diff --git a/src/main/java/dev/loki/loparkour/LoParkour.java b/src/main/java/dev/loki/loparkour/LoParkour.java index 3bdb560..e5a30fd 100644 --- a/src/main/java/dev/loki/loparkour/LoParkour.java +++ b/src/main/java/dev/loki/loparkour/LoParkour.java @@ -100,7 +100,7 @@ public final class LoParkour extends LoPlugin { new ArrayList<>(Divider.SECTIONS.keySet()).forEach(Divider::remove); - for (ParkourUser user : ParkourUser.getUsers()) { + for (final ParkourUser user : ParkourUser.getUsers()) { ParkourUser.leave(user); } diff --git a/src/main/java/dev/loki/loparkour/adaptive/bootstrap/AdaptiveServices.java b/src/main/java/dev/loki/loparkour/adaptive/bootstrap/AdaptiveServices.java index 3e377a9..d4be7b9 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/bootstrap/AdaptiveServices.java +++ b/src/main/java/dev/loki/loparkour/adaptive/bootstrap/AdaptiveServices.java @@ -41,7 +41,7 @@ public final class AdaptiveServices { return; } - Runnable setup = () -> startCollector(createRepository()); + final Runnable setup = () -> startCollector(createRepository()); if (Option.SQL) { Storage.runWhenReady(setup); } else { @@ -53,7 +53,7 @@ public final class AdaptiveServices { shutdownCollector(); AdaptiveConfig.reload(); if (plugin != null && AdaptiveConfig.isEnabled()) { - Runnable setup = () -> startCollector(createRepository()); + final Runnable setup = () -> startCollector(createRepository()); if (Option.SQL) { Storage.runWhenReady(setup); } else { @@ -76,7 +76,7 @@ public final class AdaptiveServices { return; } - double target = AdaptiveDifficulty.resolveTargetDifficulty(generator, AdaptiveServices::resolveSkillRating); + final double target = AdaptiveDifficulty.resolveTargetDifficulty(generator, AdaptiveServices::resolveSkillRating); AdaptiveDifficulty.applyToProfile(profile, target); } @@ -88,9 +88,9 @@ public final class AdaptiveServices { return; } - UUID uuid = player.getUUID(); - SkillRating session = AdaptiveDifficulty.analyzeSession(player, metricsCollector); - SkillRating stored = repository.loadSkillRating(uuid); + final UUID uuid = player.getUUID(); + final SkillRating session = AdaptiveDifficulty.analyzeSession(player, metricsCollector); + final SkillRating stored = repository.loadSkillRating(uuid); if (stored != null) { stored.updateRating(session.getRating(), AdaptiveConfig.getRatingWeight()); @@ -123,14 +123,14 @@ public final class AdaptiveServices { @NotNull private static SkillRating resolveSkillRating(@NotNull ParkourPlayer player) { - UUID uuid = player.getUUID(); - SkillRating stored = repository != null ? repository.loadSkillRating(uuid) : null; + final UUID uuid = player.getUUID(); + final SkillRating stored = repository != null ? repository.loadSkillRating(uuid) : null; if (metricsCollector == null) { return stored != null ? stored : new SkillRating(uuid); } - var metrics = metricsCollector.getMetrics(uuid); + final var metrics = metricsCollector.getMetrics(uuid); if (metrics.getTotalJumps() < AdaptiveConfig.getMinSessions()) { if (stored != null && stored.isReliable()) { return stored; @@ -138,7 +138,7 @@ public final class AdaptiveServices { return stored != null ? stored : new SkillRating(uuid); } - SkillRating session = AdaptiveDifficulty.analyzeSession(player, metricsCollector); + final SkillRating session = AdaptiveDifficulty.analyzeSession(player, metricsCollector); if (stored == null) { return session; } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/AdaptiveDifficulty.java b/src/main/java/dev/loki/loparkour/adaptive/core/AdaptiveDifficulty.java index fd87215..66d74c2 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/AdaptiveDifficulty.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/AdaptiveDifficulty.java @@ -25,6 +25,9 @@ public final class AdaptiveDifficulty { "distance.", "height.", "type." ); + private static final double DEFAULT_DIFFICULTY = 0.5; + private static final double MIN_CONFIDENCE_WEIGHT = 0.1; + private AdaptiveDifficulty() { } @@ -32,7 +35,7 @@ public final class AdaptiveDifficulty { * Merges difficulty-related keys from {@code adjusted} into {@code target}. */ public static void mergeDifficultySettings(@NotNull Profile adjusted, @NotNull Profile target) { - for (String key : adjusted.settings.keySet()) { + for (final String key : adjusted.settings.keySet()) { if (isDifficultyKey(key)) { target.set(key, adjusted.get(key).value()); } @@ -42,30 +45,30 @@ public final class AdaptiveDifficulty { public static double resolveTargetDifficulty( @NotNull ParkourGenerator generator, @NotNull SkillRatingResolver resolver) { - var players = generator.getPlayers(); + final var players = generator.getPlayers(); if (players.isEmpty()) { - return 0.5; + return DEFAULT_DIFFICULTY; } double weightedSum = 0.0; double weightTotal = 0.0; - for (ParkourPlayer player : players) { - SkillRating rating = resolver.resolve(player); - double target = CALCULATOR.calculateTargetDifficulty(rating); - double weight = Math.max(0.1, rating.getConfidence()); + for (final ParkourPlayer player : players) { + final SkillRating rating = resolver.resolve(player); + final double target = CALCULATOR.calculateTargetDifficulty(rating); + final double weight = Math.max(MIN_CONFIDENCE_WEIGHT, rating.getConfidence()); weightedSum += target * weight; weightTotal += weight; } - return weightTotal > 0 ? weightedSum / weightTotal : 0.5; + return weightTotal > 0 ? weightedSum / weightTotal : DEFAULT_DIFFICULTY; } @NotNull public static SkillRating mergeSessionRating(@NotNull SkillRating stored, @NotNull SkillRating sessionSample) { - double weight = AdaptiveConfig.getRatingWeight(); - double merged = (stored.getRating() * (1.0 - weight)) + (sessionSample.getRating() * weight); - double confidence = Math.max(stored.getConfidence(), sessionSample.getConfidence()); + final double weight = AdaptiveConfig.getRatingWeight(); + final double merged = (stored.getRating() * (1.0 - weight)) + (sessionSample.getRating() * weight); + final double confidence = Math.max(stored.getConfidence(), sessionSample.getConfidence()); return new SkillRating(stored.getPlayerUuid(), merged, confidence, stored.getSessionsCount() + 1); } @@ -77,7 +80,7 @@ public final class AdaptiveDifficulty { public static void applyToProfile( @NotNull Profile baseProfile, double targetDifficulty) { - Profile adjusted = ADJUSTER.adjustProfile(baseProfile, targetDifficulty); + final Profile adjusted = ADJUSTER.adjustProfile(baseProfile, targetDifficulty); mergeDifficultySettings(adjusted, baseProfile); } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyAdjuster.java b/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyAdjuster.java index 6cf7dfa..3d9adb5 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyAdjuster.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyAdjuster.java @@ -26,6 +26,18 @@ public class DifficultyAdjuster { private static final int MIN_HEIGHT = -1; private static final int MAX_HEIGHT = 2; + private static final int PERCENT_SCALE = 100; + private static final double FULL_PERCENT = 100.0; + private static final double SPECIAL_CHANCE_FACTOR = 0.75; + private static final double SCHEMATIC_CHANCE_FACTOR = 0.25; + private static final double DISTANCE_DIFFICULTY_WEIGHT = 0.5; + private static final double HEIGHT_DIFFICULTY_WEIGHT = 0.3; + private static final double SPECIAL_DIFFICULTY_WEIGHT = 0.2; + private static final double DISTANCE_DIFFICULTY_SPAN = 3.0; + private static final double MAX_HEIGHT_VARIATION = 1.5; + private static final double MIN_SPECIAL_FREQUENCY = 5.0; + private static final double MAX_SPECIAL_FREQUENCY = 30.0; + /** * Adjusts a profile to match the target difficulty. * Creates a new profile with modified parameters. @@ -36,7 +48,7 @@ public class DifficultyAdjuster { */ @NotNull public Profile adjustProfile(@NotNull Profile baseProfile, double targetDifficulty) { - Profile adjusted = new Profile(); + final Profile adjusted = new Profile(); // Copy base settings copyBaseSettings(baseProfile, adjusted); @@ -53,9 +65,9 @@ public class DifficultyAdjuster { * Copies non-difficulty settings from base profile. */ private void copyBaseSettings(@NotNull Profile source, @NotNull Profile target) { - String[] settingsKeys = {"min-y", "max-y", "heading", "first-block-material"}; + final String[] settingsKeys = {"min-y", "max-y", "heading", "first-block-material"}; - for (String key : settingsKeys) { + for (final String key : settingsKeys) { if (source.settings.containsKey(key)) { target.set(key, source.get(key).value()); } @@ -66,14 +78,14 @@ public class DifficultyAdjuster { * Adjusts distance chances based on difficulty. */ private void adjustDistanceChances(@NotNull Profile profile, double difficulty) { - double[] weights = DifficultyWeights.calculateDistanceWeights(difficulty); + final double[] weights = DifficultyWeights.calculateDistanceWeights(difficulty); profile.set("distance.min", String.valueOf(MIN_DISTANCE)); profile.set("distance.max", String.valueOf(MAX_DISTANCE)); for (int distance = MIN_DISTANCE; distance <= MAX_DISTANCE; distance++) { - int index = distance - MIN_DISTANCE; - int chance = (int) Math.round(weights[index] * 100); + final int index = distance - MIN_DISTANCE; + final int chance = (int) Math.round(weights[index] * PERCENT_SCALE); profile.set("distance.chances." + distance, String.valueOf(chance)); } } @@ -82,14 +94,14 @@ public class DifficultyAdjuster { * Adjusts height variation based on difficulty. */ private void adjustHeightChances(@NotNull Profile profile, double difficulty) { - double[] weights = DifficultyWeights.calculateHeightWeights(difficulty); + final double[] weights = DifficultyWeights.calculateHeightWeights(difficulty); profile.set("height.min", String.valueOf(MIN_HEIGHT)); profile.set("height.max", String.valueOf(MAX_HEIGHT)); int index = 0; for (int height = MIN_HEIGHT; height <= MAX_HEIGHT; height++) { - int chance = (int) Math.round(weights[index] * 100); + final int chance = (int) Math.round(weights[index] * PERCENT_SCALE); profile.set("height.chances." + height, String.valueOf(chance)); index++; } @@ -99,11 +111,11 @@ public class DifficultyAdjuster { * Adjusts special block frequency based on difficulty. */ private void adjustSpecialFrequency(@NotNull Profile profile, double difficulty) { - double specialPercent = DifficultyWeights.calculateSpecialFrequency(difficulty); + final double specialPercent = DifficultyWeights.calculateSpecialFrequency(difficulty); - int defaultPercent = (int) Math.round(100.0 - specialPercent); - int specialChance = (int) Math.round(specialPercent * 0.75); - int schematicChance = (int) Math.round(specialPercent * 0.25); + final int defaultPercent = (int) Math.round(FULL_PERCENT - specialPercent); + final int specialChance = (int) Math.round(specialPercent * SPECIAL_CHANCE_FACTOR); + final int schematicChance = (int) Math.round(specialPercent * SCHEMATIC_CHANCE_FACTOR); profile.set("type.default", String.valueOf(defaultPercent)); profile.set("type.special", String.valueOf(specialChance)); @@ -118,12 +130,14 @@ public class DifficultyAdjuster { * @return Estimated difficulty (0.0-1.0) */ public double estimateDifficulty(@NotNull Profile profile) { - double distanceDifficulty = estimateDistanceDifficulty(profile); - double heightDifficulty = estimateHeightDifficulty(profile); - double specialDifficulty = estimateSpecialDifficulty(profile); + final double distanceDifficulty = estimateDistanceDifficulty(profile); + final double heightDifficulty = estimateHeightDifficulty(profile); + final double specialDifficulty = estimateSpecialDifficulty(profile); // Weighted average - return (distanceDifficulty * 0.5) + (heightDifficulty * 0.3) + (specialDifficulty * 0.2); + return (distanceDifficulty * DISTANCE_DIFFICULTY_WEIGHT) + + (heightDifficulty * HEIGHT_DIFFICULTY_WEIGHT) + + (specialDifficulty * SPECIAL_DIFFICULTY_WEIGHT); } private double estimateDistanceDifficulty(@NotNull Profile profile) { @@ -131,9 +145,9 @@ public class DifficultyAdjuster { double totalWeight = 0.0; for (int distance = MIN_DISTANCE; distance <= MAX_DISTANCE; distance++) { - String key = "distance.chances." + distance; + final String key = "distance.chances." + distance; if (profile.settings.containsKey(key)) { - double weight = profile.get(key).asDouble(); + final double weight = profile.get(key).asDouble(); avgDistance += distance * weight; totalWeight += weight; } @@ -144,7 +158,7 @@ public class DifficultyAdjuster { } // Map average distance to difficulty (1.0 = easy, 4.0 = hard) - return (avgDistance - 1.0) / 3.0; + return (avgDistance - 1.0) / DISTANCE_DIFFICULTY_SPAN; } private double estimateHeightDifficulty(@NotNull Profile profile) { @@ -152,9 +166,9 @@ public class DifficultyAdjuster { double totalWeight = 0.0; for (int height = MIN_HEIGHT; height <= MAX_HEIGHT; height++) { - String key = "height.chances." + height; + final String key = "height.chances." + height; if (profile.settings.containsKey(key)) { - double weight = profile.get(key).asDouble(); + final double weight = profile.get(key).asDouble(); variation += Math.abs(height) * weight; totalWeight += weight; } @@ -165,7 +179,7 @@ public class DifficultyAdjuster { } // Map variation to difficulty (0.0 = flat, 1.5 = varied) - return Math.min(1.0, variation / 1.5); + return Math.min(1.0, variation / MAX_HEIGHT_VARIATION); } private double estimateSpecialDifficulty(@NotNull Profile profile) { @@ -173,11 +187,9 @@ public class DifficultyAdjuster { return 0.0; } - double specialPercent = profile.get("type.special").asDouble(); - double minFrequency = 5.0; - double maxFrequency = 30.0; + final double specialPercent = profile.get("type.special").asDouble(); // Map special percentage to difficulty - return (specialPercent - minFrequency) / (maxFrequency - minFrequency); + return (specialPercent - MIN_SPECIAL_FREQUENCY) / (MAX_SPECIAL_FREQUENCY - MIN_SPECIAL_FREQUENCY); } } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyCalculator.java b/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyCalculator.java index 40f551b..900da1a 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyCalculator.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/calc/DifficultyCalculator.java @@ -19,6 +19,11 @@ public class DifficultyCalculator { private static final double MAX_RATING_FOR_FULL_DIFFICULTY = 2.0; private static final double MIN_DIFFICULTY = 0.0; private static final double MAX_DIFFICULTY = 1.0; + private static final double MIN_CONFIDENCE_MULTIPLIER = 0.5; + private static final double CONFIDENCE_ADJUSTMENT_FACTOR = 0.5; + private static final double INCREASE_THRESHOLD = 0.1; + private static final double DECREASE_THRESHOLD = 0.15; + private static final double MIN_CONFIDENCE_FOR_INCREASE = 0.5; /** * Calculates the target difficulty for a player based on their skill rating. @@ -27,14 +32,14 @@ public class DifficultyCalculator { * @return Target difficulty (0.0-1.0) */ public double calculateTargetDifficulty(@NotNull SkillRating skillRating) { - double rating = skillRating.getRating(); - double confidence = skillRating.getConfidence(); + final double rating = skillRating.getRating(); + final double confidence = skillRating.getConfidence(); // Calculate base difficulty from rating - double baseDifficulty = calculateBaseDifficulty(rating); + final double baseDifficulty = calculateBaseDifficulty(rating); // Apply confidence adjustment (lower confidence = more conservative difficulty) - double adjustedDifficulty = applyConfidenceAdjustment(baseDifficulty, confidence); + final double adjustedDifficulty = applyConfidenceAdjustment(baseDifficulty, confidence); return clampDifficulty(adjustedDifficulty); } @@ -62,7 +67,7 @@ public class DifficultyCalculator { // If confidence is low, reduce difficulty to be safe // confidence 0.0 = 50% of base difficulty // confidence 1.0 = 100% of base difficulty - double confidenceMultiplier = 0.5 + (confidence * 0.5); + final double confidenceMultiplier = MIN_CONFIDENCE_MULTIPLIER + (confidence * CONFIDENCE_ADJUSTMENT_FACTOR); return baseDifficulty * confidenceMultiplier; } @@ -84,7 +89,7 @@ public class DifficultyCalculator { * @return Recommended skill rating */ public double calculateRecommendedRating(double targetDifficulty) { - double clampedDifficulty = clampDifficulty(targetDifficulty); + final double clampedDifficulty = clampDifficulty(targetDifficulty); return clampedDifficulty * MAX_RATING_FOR_FULL_DIFFICULTY; } @@ -97,11 +102,10 @@ public class DifficultyCalculator { * @return true if player is ready for harder content */ public boolean isReadyForIncrease(@NotNull SkillRating skillRating, double currentDifficulty) { - double targetDifficulty = calculateTargetDifficulty(skillRating); - double threshold = 0.1; // 10% increase threshold + final double targetDifficulty = calculateTargetDifficulty(skillRating); - return targetDifficulty > currentDifficulty + threshold - && skillRating.getConfidence() >= 0.5; // Require reasonable confidence + return targetDifficulty > currentDifficulty + INCREASE_THRESHOLD + && skillRating.getConfidence() >= MIN_CONFIDENCE_FOR_INCREASE; } /** @@ -113,10 +117,9 @@ public class DifficultyCalculator { * @return true if difficulty should be reduced */ public boolean shouldDecrease(@NotNull SkillRating skillRating, double currentDifficulty) { - double targetDifficulty = calculateTargetDifficulty(skillRating); - double threshold = 0.15; // 15% decrease threshold (more aggressive than increase) + final double targetDifficulty = calculateTargetDifficulty(skillRating); - return targetDifficulty < currentDifficulty - threshold; + return targetDifficulty < currentDifficulty - DECREASE_THRESHOLD; } /** @@ -129,7 +132,7 @@ public class DifficultyCalculator { * @return Smoothed difficulty */ public double smoothTransition(double currentDifficulty, double targetDifficulty, double maxChange) { - double difference = targetDifficulty - currentDifficulty; + final double difference = targetDifficulty - currentDifficulty; if (Math.abs(difference) <= maxChange) { return targetDifficulty; diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/calc/SkillAnalyzer.java b/src/main/java/dev/loki/loparkour/adaptive/core/calc/SkillAnalyzer.java index 3166fc6..f3d9806 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/calc/SkillAnalyzer.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/calc/SkillAnalyzer.java @@ -23,6 +23,7 @@ public class SkillAnalyzer { private static final double STREAK_DIVISOR = 100.0; private static final int MIN_JUMPS_FOR_CONFIDENCE = 10; private static final int MAX_JUMPS_FOR_CONFIDENCE = 100; + private static final int ASSUMED_NEAR_MISS_COUNT = 50; /** * Analyzes player metrics and calculates a skill rating. @@ -32,24 +33,24 @@ public class SkillAnalyzer { */ @NotNull public SkillRating analyzeSkill(@NotNull PlayerMetrics metrics) { - double successRate = metrics.getSuccessRate(); - int nearMisses = metrics.getNearMissCount(); - int successfulJumps = metrics.getSuccessfulJumps(); - int totalJumps = metrics.getTotalJumps(); + final double successRate = metrics.getSuccessRate(); + final int nearMisses = metrics.getNearMissCount(); + final int successfulJumps = metrics.getSuccessfulJumps(); + final int totalJumps = metrics.getTotalJumps(); // Calculate rating components - double successComponent = calculateSuccessComponent(successRate); - double nearMissBonus = calculateNearMissBonus(nearMisses); - double streakMultiplier = calculateStreakMultiplier(successfulJumps); + final double successComponent = calculateSuccessComponent(successRate); + final double nearMissBonus = calculateNearMissBonus(nearMisses); + final double streakMultiplier = calculateStreakMultiplier(successfulJumps); // Combine components - double rating = BASE_RATING * successComponent * nearMissBonus * streakMultiplier; + final double rating = BASE_RATING * successComponent * nearMissBonus * streakMultiplier; // Calculate confidence based on sample size - double confidence = calculateConfidence(totalJumps); + final double confidence = calculateConfidence(totalJumps); // Sessions count is 1 for current session analysis - int sessionsCount = 1; + final int sessionsCount = 1; return new SkillRating(metrics.getPlayerUuid(), rating, confidence, sessionsCount); } @@ -103,8 +104,8 @@ public class SkillAnalyzer { return 1.0; } else { // Linear interpolation between min and max - double range = MAX_JUMPS_FOR_CONFIDENCE - MIN_JUMPS_FOR_CONFIDENCE; - double progress = totalJumps - MIN_JUMPS_FOR_CONFIDENCE; + final double range = MAX_JUMPS_FOR_CONFIDENCE - MIN_JUMPS_FOR_CONFIDENCE; + final double progress = totalJumps - MIN_JUMPS_FOR_CONFIDENCE; return progress / range; } } @@ -128,9 +129,9 @@ public class SkillAnalyzer { * @return Estimated maximum rating */ public double estimateMaxRating(double successRate) { - double successComponent = calculateSuccessComponent(successRate); - double maxNearMissBonus = calculateNearMissBonus(50); // Assume 50 near misses - double maxStreakMultiplier = calculateStreakMultiplier(MAX_JUMPS_FOR_CONFIDENCE); + final double successComponent = calculateSuccessComponent(successRate); + final double maxNearMissBonus = calculateNearMissBonus(ASSUMED_NEAR_MISS_COUNT); + final double maxStreakMultiplier = calculateStreakMultiplier(MAX_JUMPS_FOR_CONFIDENCE); return BASE_RATING * successComponent * maxNearMissBonus * maxStreakMultiplier; } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricEventCollector.java b/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricEventCollector.java index b518823..0f13bd4 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricEventCollector.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricEventCollector.java @@ -30,11 +30,11 @@ public class MetricEventCollector { } public void onBlockLand(@NotNull Player player, @NotNull Block block, double distance) { - UUID uuid = player.getUniqueId(); - PlayerMetrics metrics = cacheStore.getOrCreate(uuid); + final UUID uuid = player.getUniqueId(); + final PlayerMetrics metrics = cacheStore.getOrCreate(uuid); - long currentTime = System.currentTimeMillis(); - Long lastTime = timingTracker.lastLandTime(uuid); + final long currentTime = System.currentTimeMillis(); + final Long lastTime = timingTracker.lastLandTime(uuid); if (lastTime != null) { metrics.recordJump("normal", currentTime - lastTime); } @@ -52,18 +52,18 @@ public class MetricEventCollector { } public void onPlayerFall(@NotNull Player player) { - UUID uuid = player.getUniqueId(); - PlayerMetrics metrics = cacheStore.getOrCreate(uuid); + final UUID uuid = player.getUniqueId(); + final PlayerMetrics metrics = cacheStore.getOrCreate(uuid); cacheStore.save(metrics); } public void onJump(@NotNull Player player, @NotNull JumpType jumpType) { - UUID uuid = player.getUniqueId(); - PlayerMetrics metrics = cacheStore.getOrCreate(uuid); + final UUID uuid = player.getUniqueId(); + final PlayerMetrics metrics = cacheStore.getOrCreate(uuid); - long currentTime = System.currentTimeMillis(); - Long lastTime = timingTracker.lastLandTime(uuid); - long timeDiff = (lastTime != null) ? (currentTime - lastTime) : 0; + final long currentTime = System.currentTimeMillis(); + final Long lastTime = timingTracker.lastLandTime(uuid); + final long timeDiff = (lastTime != null) ? (currentTime - lastTime) : 0; metrics.recordJump(jumpType.name().toLowerCase(), timeDiff); } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricsCacheStore.java b/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricsCacheStore.java index e1767ec..0bff238 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricsCacheStore.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/metrics/MetricsCacheStore.java @@ -43,7 +43,7 @@ public class MetricsCacheStore { } private @NotNull PlayerMetrics loadOrCreate(@NotNull UUID playerUuid) { - PlayerMetrics loaded = repository.loadMetrics(playerUuid); + final PlayerMetrics loaded = repository.loadMetrics(playerUuid); if (loaded != null) { return loaded; } @@ -51,7 +51,7 @@ public class MetricsCacheStore { } public void save(@NotNull UUID playerUuid) { - PlayerMetrics metrics = metricsCache.get(playerUuid); + final PlayerMetrics metrics = metricsCache.get(playerUuid); if (metrics != null) { repository.saveMetrics(metrics); } @@ -62,7 +62,7 @@ public class MetricsCacheStore { } public void saveAll() { - for (PlayerMetrics metrics : metricsCache.values()) { + for (final PlayerMetrics metrics : metricsCache.values()) { repository.saveMetrics(metrics); } } diff --git a/src/main/java/dev/loki/loparkour/adaptive/core/model/DifficultyWeights.java b/src/main/java/dev/loki/loparkour/adaptive/core/model/DifficultyWeights.java index c9560e9..3f33384 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/core/model/DifficultyWeights.java +++ b/src/main/java/dev/loki/loparkour/adaptive/core/model/DifficultyWeights.java @@ -8,6 +8,25 @@ package dev.loki.loparkour.adaptive.core.model; */ public class DifficultyWeights { + private static final double[] LEVEL_THRESHOLDS = {0.3, 0.6, 0.8}; + + private static final double[][] DISTANCE_WEIGHTS = { + {0.40, 0.40, 0.15, 0.05}, + {0.20, 0.35, 0.30, 0.15}, + {0.10, 0.25, 0.40, 0.25}, + {0.05, 0.15, 0.35, 0.45}, + }; + + private static final double[][] HEIGHT_WEIGHTS = { + {0.10, 0.60, 0.25, 0.05}, + {0.20, 0.40, 0.30, 0.10}, + {0.25, 0.30, 0.30, 0.15}, + {0.30, 0.20, 0.30, 0.20}, + }; + + private static final double MIN_SPECIAL_FREQUENCY = 5.0; + private static final double MAX_SPECIAL_FREQUENCY = 30.0; + private DifficultyWeights() { } @@ -18,35 +37,7 @@ public class DifficultyWeights { * @return Array of weights [dist1, dist2, dist3, dist4] */ public static double[] calculateDistanceWeights(double difficulty) { - double[] weights = new double[4]; - - if (difficulty < 0.3) { - // Easy: favor short jumps (1-2 blocks) - weights[0] = 0.40; // 1 block - weights[1] = 0.40; // 2 blocks - weights[2] = 0.15; // 3 blocks - weights[3] = 0.05; // 4 blocks - } else if (difficulty < 0.6) { - // Medium: balanced distribution - weights[0] = 0.20; // 1 block - weights[1] = 0.35; // 2 blocks - weights[2] = 0.30; // 3 blocks - weights[3] = 0.15; // 4 blocks - } else if (difficulty < 0.8) { - // Hard: favor longer jumps - weights[0] = 0.10; // 1 block - weights[1] = 0.25; // 2 blocks - weights[2] = 0.40; // 3 blocks - weights[3] = 0.25; // 4 blocks - } else { - // Expert: maximum challenge - weights[0] = 0.05; // 1 block - weights[1] = 0.15; // 2 blocks - weights[2] = 0.35; // 3 blocks - weights[3] = 0.45; // 4 blocks - } - - return weights; + return DISTANCE_WEIGHTS[difficultyLevel(difficulty)].clone(); } /** @@ -56,35 +47,7 @@ public class DifficultyWeights { * @return Array of weights [-1, 0, 1, 2] */ public static double[] calculateHeightWeights(double difficulty) { - double[] weights = new double[4]; - - if (difficulty < 0.3) { - // Easy: mostly flat, minimal drops - weights[0] = 0.10; // -1 (down) - weights[1] = 0.60; // 0 (flat) - weights[2] = 0.25; // +1 (up) - weights[3] = 0.05; // +2 (high up) - } else if (difficulty < 0.6) { - // Medium: balanced variation - weights[0] = 0.20; // -1 - weights[1] = 0.40; // 0 - weights[2] = 0.30; // +1 - weights[3] = 0.10; // +2 - } else if (difficulty < 0.8) { - // Hard: more variation - weights[0] = 0.25; // -1 - weights[1] = 0.30; // 0 - weights[2] = 0.30; // +1 - weights[3] = 0.15; // +2 - } else { - // Expert: maximum variation - weights[0] = 0.30; // -1 - weights[1] = 0.20; // 0 - weights[2] = 0.30; // +1 - weights[3] = 0.20; // +2 - } - - return weights; + return HEIGHT_WEIGHTS[difficultyLevel(difficulty)].clone(); } /** @@ -94,8 +57,16 @@ public class DifficultyWeights { * @return Special block percentage (5.0-30.0) */ public static double calculateSpecialFrequency(double difficulty) { - double minFrequency = 5.0; - double maxFrequency = 30.0; - return minFrequency + (maxFrequency - minFrequency) * difficulty; + return MIN_SPECIAL_FREQUENCY + (MAX_SPECIAL_FREQUENCY - MIN_SPECIAL_FREQUENCY) * difficulty; + } + + private static int difficultyLevel(double difficulty) { + int level = 0; + for (final double threshold : LEVEL_THRESHOLDS) { + if (difficulty >= threshold) { + level++; + } + } + return level; } } diff --git a/src/main/java/dev/loki/loparkour/adaptive/model/AdaptiveConfig.java b/src/main/java/dev/loki/loparkour/adaptive/model/AdaptiveConfig.java index 0391c80..3d59e50 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/model/AdaptiveConfig.java +++ b/src/main/java/dev/loki/loparkour/adaptive/model/AdaptiveConfig.java @@ -99,7 +99,7 @@ public class AdaptiveConfig { */ public static double getRatingWeight() { ensureInitialized(); - Object value = CACHE.get(KEY_RATING_WEIGHT); + final Object value = CACHE.get(KEY_RATING_WEIGHT); if (value instanceof Double) { return (double) value; } @@ -113,7 +113,7 @@ public class AdaptiveConfig { */ public static double getConfidenceThreshold() { ensureInitialized(); - Object value = CACHE.get(KEY_CONFIDENCE_THRESHOLD); + final Object value = CACHE.get(KEY_CONFIDENCE_THRESHOLD); if (value instanceof Double) { return (double) value; } @@ -127,7 +127,7 @@ public class AdaptiveConfig { */ public static double getNearMissDistance() { ensureInitialized(); - Object value = CACHE.get(KEY_NEAR_MISS_DISTANCE); + final Object value = CACHE.get(KEY_NEAR_MISS_DISTANCE); if (value instanceof Double) { return (double) value; } @@ -155,7 +155,7 @@ public class AdaptiveConfig { public static T get(@NotNull String key, @NotNull T defaultValue) { ensureInitialized(); @SuppressWarnings("unchecked") - T value = (T) CACHE.get(key); + final T value = (T) CACHE.get(key); return value != null ? value : defaultValue; } } diff --git a/src/main/java/dev/loki/loparkour/adaptive/model/PlayerMetrics.java b/src/main/java/dev/loki/loparkour/adaptive/model/PlayerMetrics.java index cae187a..8be23bb 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/model/PlayerMetrics.java +++ b/src/main/java/dev/loki/loparkour/adaptive/model/PlayerMetrics.java @@ -12,6 +12,11 @@ import java.util.UUID; */ public class PlayerMetrics { + private static final double ROLLING_AVG_WEIGHT = 0.9; + private static final double NEW_SAMPLE_WEIGHT = 0.1; + private static final double SUCCESS_RATE_BASE = 0.7; + private static final double NEAR_MISS_SUCCESS_BONUS = 0.3; + private final UUID playerUuid; private double avgTimePerBlock; private final Map jumpTypeStats; @@ -42,7 +47,7 @@ public class PlayerMetrics { if (avgTimePerBlock == 0.0) { avgTimePerBlock = timeMs; } else { - avgTimePerBlock = (avgTimePerBlock * 0.9) + (timeMs * 0.1); + avgTimePerBlock = (avgTimePerBlock * ROLLING_AVG_WEIGHT) + (timeMs * NEW_SAMPLE_WEIGHT); } lastUpdated = System.currentTimeMillis(); @@ -95,7 +100,7 @@ public class PlayerMetrics { return 0.0; } // High near-miss rate indicates good performance - return Math.min(1.0, 0.7 + (getNearMissRate() * 0.3)); + return Math.min(1.0, SUCCESS_RATE_BASE + (getNearMissRate() * NEAR_MISS_SUCCESS_BONUS)); } /** diff --git a/src/main/java/dev/loki/loparkour/adaptive/model/SkillRating.java b/src/main/java/dev/loki/loparkour/adaptive/model/SkillRating.java index 5d86565..8668159 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/model/SkillRating.java +++ b/src/main/java/dev/loki/loparkour/adaptive/model/SkillRating.java @@ -10,6 +10,11 @@ import java.util.UUID; */ public class SkillRating { + private static final double CONFIDENCE_INCREMENT = 0.05; + private static final double RELIABLE_CONFIDENCE_THRESHOLD = 0.3; + private static final int MIN_RELIABLE_SESSIONS = 3; + private static final double MIN_RATING = 0.5; + private final UUID playerUuid; private double rating; private double confidence; @@ -56,7 +61,7 @@ public class SkillRating { rating = clampRating(rating); sessionsCount++; - confidence = Math.min(1.0, confidence + 0.05); + confidence = Math.min(1.0, confidence + CONFIDENCE_INCREMENT); lastUpdated = System.currentTimeMillis(); } @@ -77,11 +82,11 @@ public class SkillRating { * @return True if confidence is above threshold */ public boolean isReliable() { - return confidence >= 0.3 && sessionsCount >= 3; + return confidence >= RELIABLE_CONFIDENCE_THRESHOLD && sessionsCount >= MIN_RELIABLE_SESSIONS; } private double clampRating(double value) { - return Math.max(0.5, Math.min(2.0, value)); + return Math.max(MIN_RATING, Math.min(2.0, value)); } private double clampConfidence(double value) { diff --git a/src/main/java/dev/loki/loparkour/adaptive/storage/FileStatsStorage.java b/src/main/java/dev/loki/loparkour/adaptive/storage/FileStatsStorage.java index 6c433f3..6be2104 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/storage/FileStatsStorage.java +++ b/src/main/java/dev/loki/loparkour/adaptive/storage/FileStatsStorage.java @@ -18,7 +18,7 @@ import java.util.UUID; /** * JSON file-based implementation of StatsRepository. - * Stores data in playerdata/.json files. + * Stores data in playerdata/{@literal }.json files. * Saves only when player leaves to minimize I/O. */ public class FileStatsStorage implements StatsRepository { @@ -36,15 +36,15 @@ public class FileStatsStorage implements StatsRepository { @Override @Nullable public SkillRating loadSkillRating(@NotNull UUID playerUuid) { - JsonObject data = loadPlayerData(playerUuid); + final JsonObject data = loadPlayerData(playerUuid); if (data == null || !data.has("skillRating")) { return null; } - JsonObject ratingObj = data.getAsJsonObject("skillRating"); - double rating = ratingObj.get("rating").getAsDouble(); - double confidence = ratingObj.get("confidence").getAsDouble(); - int sessions = ratingObj.get("sessionsCount").getAsInt(); + final JsonObject ratingObj = data.getAsJsonObject("skillRating"); + final double rating = ratingObj.get("rating").getAsDouble(); + final double confidence = ratingObj.get("confidence").getAsDouble(); + final int sessions = ratingObj.get("sessionsCount").getAsInt(); return new SkillRating(playerUuid, rating, confidence, sessions); } @@ -56,7 +56,7 @@ public class FileStatsStorage implements StatsRepository { data = new JsonObject(); } - JsonObject ratingObj = new JsonObject(); + final JsonObject ratingObj = new JsonObject(); ratingObj.addProperty("rating", rating.getRating()); ratingObj.addProperty("confidence", rating.getConfidence()); ratingObj.addProperty("sessionsCount", rating.getSessionsCount()); @@ -69,21 +69,21 @@ public class FileStatsStorage implements StatsRepository { @Override @Nullable public PlayerMetrics loadMetrics(@NotNull UUID playerUuid) { - JsonObject data = loadPlayerData(playerUuid); + final JsonObject data = loadPlayerData(playerUuid); if (data == null || !data.has("metrics")) { return null; } - JsonObject metricsObj = data.getAsJsonObject("metrics"); - PlayerMetrics metrics = new PlayerMetrics(playerUuid); + final JsonObject metricsObj = data.getAsJsonObject("metrics"); + final PlayerMetrics metrics = new PlayerMetrics(playerUuid); metrics.setAvgTimePerBlock(metricsObj.get("avgTimePerBlock").getAsDouble()); metrics.setNearMissCount(metricsObj.get("nearMissCount").getAsInt()); metrics.setTotalJumps(metricsObj.get("totalJumps").getAsInt()); metrics.setLastUpdated(metricsObj.get("lastUpdated").getAsLong()); - JsonObject jumpStats = metricsObj.getAsJsonObject("jumpTypeStats"); - for (Map.Entry entry : jumpStats.entrySet()) { + final JsonObject jumpStats = metricsObj.getAsJsonObject("jumpTypeStats"); + for (final Map.Entry entry : jumpStats.entrySet()) { metrics.putJumpTypeStat(entry.getKey(), entry.getValue().getAsInt()); } @@ -97,14 +97,14 @@ public class FileStatsStorage implements StatsRepository { data = new JsonObject(); } - JsonObject metricsObj = new JsonObject(); + final JsonObject metricsObj = new JsonObject(); metricsObj.addProperty("avgTimePerBlock", metrics.getAvgTimePerBlock()); metricsObj.addProperty("nearMissCount", metrics.getNearMissCount()); metricsObj.addProperty("totalJumps", metrics.getTotalJumps()); metricsObj.addProperty("lastUpdated", metrics.getLastUpdated()); - JsonObject jumpStats = new JsonObject(); - for (Map.Entry entry : metrics.getJumpTypeStats().entrySet()) { + final JsonObject jumpStats = new JsonObject(); + for (final Map.Entry entry : metrics.getJumpTypeStats().entrySet()) { jumpStats.addProperty(entry.getKey(), entry.getValue()); } metricsObj.add("jumpTypeStats", jumpStats); @@ -120,7 +120,7 @@ public class FileStatsStorage implements StatsRepository { data = new JsonObject(); } - int current = data.has("totalJumps") ? data.get("totalJumps").getAsInt() : 0; + final int current = data.has("totalJumps") ? data.get("totalJumps").getAsInt() : 0; data.addProperty("totalJumps", current + amount); savePlayerData(playerUuid, data); } @@ -132,7 +132,7 @@ public class FileStatsStorage implements StatsRepository { data = new JsonObject(); } - int current = data.has("totalFalls") ? data.get("totalFalls").getAsInt() : 0; + final int current = data.has("totalFalls") ? data.get("totalFalls").getAsInt() : 0; data.addProperty("totalFalls", current + amount); savePlayerData(playerUuid, data); } @@ -144,7 +144,7 @@ public class FileStatsStorage implements StatsRepository { data = new JsonObject(); } - int current = data.has("longestStreak") ? data.get("longestStreak").getAsInt() : 0; + final int current = data.has("longestStreak") ? data.get("longestStreak").getAsInt() : 0; if (streak > current) { data.addProperty("longestStreak", streak); savePlayerData(playerUuid, data); @@ -158,7 +158,7 @@ public class FileStatsStorage implements StatsRepository { @Nullable private JsonObject loadPlayerData(@NotNull UUID playerUuid) { - File file = new File(dataFolder, playerUuid.toString() + ".json"); + final File file = new File(dataFolder, playerUuid.toString() + ".json"); if (!file.exists()) { return null; } @@ -173,7 +173,7 @@ public class FileStatsStorage implements StatsRepository { } private void savePlayerData(@NotNull UUID playerUuid, @NotNull JsonObject data) { - File file = new File(dataFolder, playerUuid.toString() + ".json"); + final File file = new File(dataFolder, playerUuid.toString() + ".json"); try (FileWriter writer = new FileWriter(file)) { GSON.toJson(data, writer); diff --git a/src/main/java/dev/loki/loparkour/adaptive/storage/SQLStatsStorage.java b/src/main/java/dev/loki/loparkour/adaptive/storage/SQLStatsStorage.java index ff6e07c..ffcef35 100644 --- a/src/main/java/dev/loki/loparkour/adaptive/storage/SQLStatsStorage.java +++ b/src/main/java/dev/loki/loparkour/adaptive/storage/SQLStatsStorage.java @@ -25,6 +25,12 @@ public class SQLStatsStorage implements StatsRepository { private final SQLConnectionManager connectionManager; private final ConcurrentHashMap pendingUpdates; private static final int BATCH_SIZE = 50; + private static final double CONFIDENCE_PER_SESSION = 0.05; + private static final int SESSIONS_COUNT_INDEX = 3; + private static final int RATING_UPDATE_INDEX = 4; + private static final int SESSIONS_UPDATE_INDEX = 5; + private static final int TOTAL_FALLS_INDEX = 3; + private static final int LONGEST_STREAK_INDEX = 4; public SQLStatsStorage(@NotNull SQLConnectionManager connectionManager) { this.connectionManager = connectionManager; @@ -34,7 +40,7 @@ public class SQLStatsStorage implements StatsRepository { @Override @Nullable public SkillRating loadSkillRating(@NotNull UUID playerUuid) { - String query = "SELECT skill_rating, sessions_count FROM loparkour_player_stats WHERE player_uuid = ?"; + final String query = "SELECT skill_rating, sessions_count FROM loparkour_player_stats WHERE player_uuid = ?"; try (Connection conn = connectionManager.getConnection(); PreparedStatement stmt = conn.prepareStatement(query)) { @@ -42,9 +48,9 @@ public class SQLStatsStorage implements StatsRepository { stmt.setString(1, playerUuid.toString()); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { - double rating = rs.getDouble("skill_rating"); - int sessions = rs.getInt("sessions_count"); - double confidence = Math.min(1.0, sessions * 0.05); + final double rating = rs.getDouble("skill_rating"); + final int sessions = rs.getInt("sessions_count"); + final double confidence = Math.min(1.0, sessions * CONFIDENCE_PER_SESSION); return new SkillRating(playerUuid, rating, confidence, sessions); } } @@ -57,7 +63,7 @@ public class SQLStatsStorage implements StatsRepository { @Override public void saveSkillRating(@NotNull SkillRating rating) { - String query = "INSERT INTO loparkour_player_stats (player_uuid, skill_rating, sessions_count) " + + final String query = "INSERT INTO loparkour_player_stats (player_uuid, skill_rating, sessions_count) " + "VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE skill_rating = ?, sessions_count = ?"; try (Connection conn = connectionManager.getConnection(); @@ -65,9 +71,9 @@ public class SQLStatsStorage implements StatsRepository { stmt.setString(1, rating.getPlayerUuid().toString()); stmt.setDouble(2, rating.getRating()); - stmt.setInt(3, rating.getSessionsCount()); - stmt.setDouble(4, rating.getRating()); - stmt.setInt(5, rating.getSessionsCount()); + stmt.setInt(SESSIONS_COUNT_INDEX, rating.getSessionsCount()); + stmt.setDouble(RATING_UPDATE_INDEX, rating.getRating()); + stmt.setInt(SESSIONS_UPDATE_INDEX, rating.getSessionsCount()); stmt.executeUpdate(); } catch (SQLException e) { @@ -89,7 +95,7 @@ public class SQLStatsStorage implements StatsRepository { @Override public void incrementJumps(@NotNull UUID playerUuid, int amount) { - PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); + final PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); update.jumps += amount; if (pendingUpdates.size() >= BATCH_SIZE) { @@ -99,7 +105,7 @@ public class SQLStatsStorage implements StatsRepository { @Override public void incrementFalls(@NotNull UUID playerUuid, int amount) { - PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); + final PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); update.falls += amount; if (pendingUpdates.size() >= BATCH_SIZE) { @@ -109,7 +115,7 @@ public class SQLStatsStorage implements StatsRepository { @Override public void updateLongestStreak(@NotNull UUID playerUuid, int streak) { - PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); + final PendingUpdate update = pendingUpdates.computeIfAbsent(playerUuid, k -> new PendingUpdate()); update.longestStreak = Math.max(update.longestStreak, streak); if (pendingUpdates.size() >= BATCH_SIZE) { @@ -125,7 +131,7 @@ public class SQLStatsStorage implements StatsRepository { return; } - String query = "INSERT INTO loparkour_player_stats (player_uuid, total_jumps, total_falls, longest_streak) " + + final String query = "INSERT INTO loparkour_player_stats (player_uuid, total_jumps, total_falls, longest_streak) " + "VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE " + "total_jumps = total_jumps + VALUES(total_jumps), " + "total_falls = total_falls + VALUES(total_falls), " + @@ -134,16 +140,16 @@ public class SQLStatsStorage implements StatsRepository { try (Connection conn = connectionManager.getConnection(); PreparedStatement stmt = conn.prepareStatement(query)) { - List processed = new ArrayList<>(); + final List processed = new ArrayList<>(); - for (var entry : pendingUpdates.entrySet()) { - UUID uuid = entry.getKey(); - PendingUpdate update = entry.getValue(); + for (final var entry : pendingUpdates.entrySet()) { + final UUID uuid = entry.getKey(); + final PendingUpdate update = entry.getValue(); stmt.setString(1, uuid.toString()); stmt.setInt(2, update.jumps); - stmt.setInt(3, update.falls); - stmt.setInt(4, update.longestStreak); + stmt.setInt(TOTAL_FALLS_INDEX, update.falls); + stmt.setInt(LONGEST_STREAK_INDEX, update.longestStreak); stmt.addBatch(); processed.add(uuid); @@ -151,7 +157,7 @@ public class SQLStatsStorage implements StatsRepository { stmt.executeBatch(); - for (UUID uuid : processed) { + for (final UUID uuid : processed) { pendingUpdates.remove(uuid); } diff --git a/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java b/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java index aa62589..e7c28e6 100644 --- a/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java +++ b/src/main/java/dev/loki/loparkour/bootstrap/PluginBootstrap.java @@ -33,6 +33,8 @@ import org.jetbrains.annotations.NotNull; */ public final class PluginBootstrap { + private static final int BSTATS_PLUGIN_ID = 29754; + private PluginBootstrap() { } @@ -53,13 +55,13 @@ public final class PluginBootstrap { private static void loadSchematics(@NotNull LoParkour plugin) { extractSchematicDocs(plugin); - SchematicManager manager = new SchematicManager(); + final SchematicManager manager = new SchematicManager(); manager.loadAll(); plugin.setSchematicManager(manager); } private static void extractSchematicDocs(@NotNull LoParkour plugin) { - java.io.File doc = LoParkour.getInFolder("docs/SCHEMATICS.md"); + final java.io.File doc = LoParkour.getInFolder("docs/SCHEMATICS.md"); if (!doc.exists()) { doc.getParentFile().mkdirs(); plugin.saveResource("docs/SCHEMATICS.md", false); @@ -84,7 +86,7 @@ public final class PluginBootstrap { } if (plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { plugin.getLogger().info("Registered PlaceholderAPI hook"); - PAPIHook hook = new PAPIHook(); + final PAPIHook hook = new PAPIHook(); hook.register(); plugin.setPlaceholderHook(hook); } @@ -110,17 +112,17 @@ public final class PluginBootstrap { private static void registerEventsAndCommands(@NotNull LoParkour plugin) { dev.lolib.gui.GUIManager.init(plugin); - var pm = plugin.getServer().getPluginManager(); + final var pm = plugin.getServer().getPluginManager(); pm.registerEvents(new PlayerConnectionListener(), plugin); pm.registerEvents(new ParkourRestrictionListener(), plugin); pm.registerEvents(new SchematicWandListener(), plugin); - LoParkourCommand cmd = new LoParkourCommand(); + final LoParkourCommand cmd = new LoParkourCommand(); plugin.getCommand("LoParkour").setExecutor(cmd); plugin.getCommand("LoParkour").setTabCompleter(cmd); } private static void setupMetrics(@NotNull LoParkour plugin) { - Metrics metrics = new Metrics(plugin, 29754); + final Metrics metrics = new Metrics(plugin, BSTATS_PLUGIN_ID); metrics.addCustomChart(new SimplePie("using_sql", () -> Boolean.toString(Option.SQL))); metrics.addCustomChart(new SimplePie("using_rewards", () -> Boolean.toString(Rewards.REWARDS_ENABLED))); metrics.addCustomChart(new SimplePie("locale_count", () -> Integer.toString(Locales.getLocaleCount()))); diff --git a/src/main/java/dev/loki/loparkour/command/admin/ops/ForceJoinExecutor.java b/src/main/java/dev/loki/loparkour/command/admin/ops/ForceJoinExecutor.java index 010e5f3..ae87670 100644 --- a/src/main/java/dev/loki/loparkour/command/admin/ops/ForceJoinExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/admin/ops/ForceJoinExecutor.java @@ -12,6 +12,8 @@ import org.bukkit.entity.Player; public final class ForceJoinExecutor { + private static final int COOLDOWN_MS = 2500; + private final PlayerCommandHandler base; public ForceJoinExecutor(PlayerCommandHandler base) { @@ -19,7 +21,7 @@ public final class ForceJoinExecutor { } public void execute(String target, CommandSender sender) { - if (!base.cooldown(sender, "forcejoin", 2500, Locales.getString(sender, "admin.cooldown"))) { + if (!base.cooldown(sender, "forcejoin", COOLDOWN_MS, Locales.getString(sender, "admin.cooldown"))) { return; } if ("everyone".equalsIgnoreCase(target)) { @@ -39,7 +41,7 @@ public final class ForceJoinExecutor { } private void joinNearest(CommandSender sender) { - Player closest = AdminTargetResolver.findNearest(sender); + final Player closest = AdminTargetResolver.findNearest(sender); if (closest == null) { return; } @@ -48,7 +50,7 @@ public final class ForceJoinExecutor { } private void joinByName(String target, CommandSender sender) { - Player other = Bukkit.getPlayer(target); + final Player other = Bukkit.getPlayer(target); if (other == null) { AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_online")); return; diff --git a/src/main/java/dev/loki/loparkour/command/admin/ops/ForceLeaveExecutor.java b/src/main/java/dev/loki/loparkour/command/admin/ops/ForceLeaveExecutor.java index c2660b2..0443578 100644 --- a/src/main/java/dev/loki/loparkour/command/admin/ops/ForceLeaveExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/admin/ops/ForceLeaveExecutor.java @@ -12,6 +12,8 @@ import org.bukkit.entity.Player; public final class ForceLeaveExecutor { + private static final int COOLDOWN_MS = 2500; + private final PlayerCommandHandler base; public ForceLeaveExecutor(PlayerCommandHandler base) { @@ -19,7 +21,7 @@ public final class ForceLeaveExecutor { } public void execute(String target, CommandSender sender) { - if (!base.cooldown(sender, "forceleave", 2500, Locales.getString(sender, "admin.cooldown"))) { + if (!base.cooldown(sender, "forceleave", COOLDOWN_MS, Locales.getString(sender, "admin.cooldown"))) { return; } if ("everyone".equalsIgnoreCase(target)) { @@ -35,12 +37,12 @@ public final class ForceLeaveExecutor { } private void leaveByName(String target, CommandSender sender) { - Player other = Bukkit.getPlayer(target); + final Player other = Bukkit.getPlayer(target); if (other == null) { AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_online")); return; } - ParkourUser user = ParkourUser.getUser(other); + final ParkourUser user = ParkourUser.getUser(other); if (user == null) { AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_playing")); return; diff --git a/src/main/java/dev/loki/loparkour/command/admin/ops/RecoverInventoryExecutor.java b/src/main/java/dev/loki/loparkour/command/admin/ops/RecoverInventoryExecutor.java index 232334a..ae31345 100644 --- a/src/main/java/dev/loki/loparkour/command/admin/ops/RecoverInventoryExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/admin/ops/RecoverInventoryExecutor.java @@ -11,6 +11,8 @@ import org.bukkit.entity.Player; public final class RecoverInventoryExecutor { + private static final int COOLDOWN_MS = 2500; + private final PlayerCommandHandler base; public RecoverInventoryExecutor(PlayerCommandHandler base) { @@ -18,10 +20,10 @@ public final class RecoverInventoryExecutor { } public void execute(String target, CommandSender sender) { - if (!base.cooldown(sender, "recoverinventory", 2500, Locales.getString(sender, "admin.cooldown"))) { + if (!base.cooldown(sender, "recoverinventory", COOLDOWN_MS, Locales.getString(sender, "admin.cooldown"))) { return; } - Player other = Bukkit.getPlayer(target); + final Player other = Bukkit.getPlayer(target); if (other == null) { AdminResponses.send(sender, LoParkour.PREFIX + Locales.getString(sender, "admin.player_not_online")); return; diff --git a/src/main/java/dev/loki/loparkour/command/admin/ops/ResetExecutor.java b/src/main/java/dev/loki/loparkour/command/admin/ops/ResetExecutor.java index edf966b..bed3760 100644 --- a/src/main/java/dev/loki/loparkour/command/admin/ops/ResetExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/admin/ops/ResetExecutor.java @@ -15,6 +15,8 @@ import java.util.function.Consumer; public final class ResetExecutor { + private static final int COOLDOWN_MS = 2500; + private final PlayerCommandHandler base; public ResetExecutor(PlayerCommandHandler base) { @@ -22,7 +24,7 @@ public final class ResetExecutor { } public void execute(String target, CommandSender sender) { - if (!base.cooldown(sender, "reset", 2500, Locales.getString(sender, "admin.cooldown"))) { + if (!base.cooldown(sender, "reset", COOLDOWN_MS, Locales.getString(sender, "admin.cooldown"))) { return; } if ("everyone".equalsIgnoreCase(target)) { @@ -41,8 +43,8 @@ public final class ResetExecutor { } private void resetPlayer(String target, CommandSender sender) { - UUID uuid = AdminTargetResolver.resolveUUID(target); - String name = AdminTargetResolver.resolvePlayerName(target, uuid); + final UUID uuid = AdminTargetResolver.resolveUUID(target); + final String name = AdminTargetResolver.resolvePlayerName(target, uuid); forEachLeaderboard(lb -> { lb.remove(uuid); lb.write(true); diff --git a/src/main/java/dev/loki/loparkour/command/admin/support/AdminTargetResolver.java b/src/main/java/dev/loki/loparkour/command/admin/support/AdminTargetResolver.java index 21f3bf3..d071941 100644 --- a/src/main/java/dev/loki/loparkour/command/admin/support/AdminTargetResolver.java +++ b/src/main/java/dev/loki/loparkour/command/admin/support/AdminTargetResolver.java @@ -15,7 +15,7 @@ public final class AdminTargetResolver { } public static Player findNearest(CommandSender sender) { - Location from = sender instanceof Player p ? p.getLocation() + final Location from = sender instanceof Player p ? p.getLocation() : sender instanceof BlockCommandSender b ? b.getBlock().getLocation() : null; if (from == null || from.getWorld() == null) { @@ -29,12 +29,12 @@ public final class AdminTargetResolver { @SuppressWarnings("deprecation") public static UUID resolveUUID(String input) { - Player online = Bukkit.getPlayerExact(input); + final Player online = Bukkit.getPlayerExact(input); if (online != null) { return online.getUniqueId(); } if (input.contains("-")) { - UUID parsed = tryParseUuid(input); + final UUID parsed = tryParseUuid(input); if (parsed != null) { return parsed; } @@ -51,11 +51,11 @@ public final class AdminTargetResolver { } public static String resolvePlayerName(String input, UUID uuid) { - Player online = Bukkit.getPlayerExact(input); + final Player online = Bukkit.getPlayerExact(input); if (online != null) { return online.getName(); } - OfflinePlayer op = Bukkit.getOfflinePlayer(uuid); + final OfflinePlayer op = Bukkit.getOfflinePlayer(uuid); return op.getName() != null ? op.getName() : input; } } diff --git a/src/main/java/dev/loki/loparkour/command/core/CommandRouter.java b/src/main/java/dev/loki/loparkour/command/core/CommandRouter.java index a3f06cd..3a70684 100644 --- a/src/main/java/dev/loki/loparkour/command/core/CommandRouter.java +++ b/src/main/java/dev/loki/loparkour/command/core/CommandRouter.java @@ -14,6 +14,10 @@ import org.jetbrains.annotations.Nullable; class CommandRouter { + private static final int THREE_ARGS = 3; + private static final int FOUR_ARGS = 4; + private static final int FOURTH_ARG_INDEX = 3; + private final PlayerCommandHandler player; private final AdminCommandHandler admin; private final JoinCommandExecutor join; @@ -31,8 +35,8 @@ class CommandRouter { case 0 -> player.handleNoArgs(sender, p); case 1 -> player.handle(args[0], sender, p); case 2 -> handle2(args[0], args[1], sender, p); - case 3 -> handle3(args[0], args[1], args[2], sender, p); - case 4 -> handle4(args[0], args[1], args[2], args[3], sender, p); + case THREE_ARGS -> handle3(args[0], args[1], args[2], sender, p); + case FOUR_ARGS -> handle4(args[0], args[1], args[2], args[FOURTH_ARG_INDEX], sender, p); default -> Locales.send(sender, "commands.too_many_args"); } } diff --git a/src/main/java/dev/loki/loparkour/command/core/CommandTabCompleter.java b/src/main/java/dev/loki/loparkour/command/core/CommandTabCompleter.java index 5d5bb92..cef7dbc 100644 --- a/src/main/java/dev/loki/loparkour/command/core/CommandTabCompleter.java +++ b/src/main/java/dev/loki/loparkour/command/core/CommandTabCompleter.java @@ -15,6 +15,10 @@ import java.util.List; class CommandTabCompleter { + private static final int THREE_ARGS = 3; + private static final int FOUR_ARGS = 4; + private static final int FOURTH_ARG_INDEX = 3; + private static final List SCHEMATIC_DIFFICULTIES = List.of("0.0", "0.25", "0.5", "0.75", "1.0"); private static final List SCHEMATIC_SUBCOMMANDS = List.of( "wand", "pos1", "pos2", "create", "paste", "list", "reload" @@ -30,17 +34,17 @@ class CommandTabCompleter { if (args.length == 2) { return completeSecond(sender, args[0], args[1]); } - if (args.length == 3 && sender.hasPermission(ParkourOption.ADMIN.permission)) { + if (args.length == THREE_ARGS && sender.hasPermission(ParkourOption.ADMIN.permission)) { return completeThird(args[0], args[1], args[2]); } - if (args.length == 4 && sender.hasPermission(ParkourOption.ADMIN.permission)) { - return completeFourth(args[0], args[1], args[3]); + if (args.length == FOUR_ARGS && sender.hasPermission(ParkourOption.ADMIN.permission)) { + return completeFourth(args[0], args[1], args[FOURTH_ARG_INDEX]); } return Collections.emptyList(); } private static List completeFirst(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (ParkourOption.JOIN.mayPerform(sender)) { completions.add("join"); completions.add("leave"); @@ -61,7 +65,7 @@ class CommandTabCompleter { } private static List completeSecond(CommandSender sender, String a1Raw, String input) { - String a1 = a1Raw.toLowerCase(); + final String a1 = a1Raw.toLowerCase(); return switch (a1) { case "reset" -> completeReset(sender, input); case "join" -> completeJoin(sender, input); @@ -75,7 +79,7 @@ class CommandTabCompleter { } private static List completeReset(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (sender.hasPermission(ParkourOption.ADMIN.permission)) { completions.add("everyone"); ParkourPlayer.getPlayers().forEach(pp -> completions.add(pp.getName())); @@ -84,7 +88,7 @@ class CommandTabCompleter { } private static List completeJoin(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (ParkourOption.JOIN.mayPerform(sender)) { Registry.getModes().forEach(m -> completions.add(m.getName())); ParkourPlayer.getPlayers().forEach(pp -> completions.add(pp.getName())); @@ -93,7 +97,7 @@ class CommandTabCompleter { } private static List completeLeaderboard(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (ParkourOption.LEADERBOARDS.mayPerform(sender)) { Registry.getModes().forEach(m -> completions.add(m.getName())); } @@ -101,7 +105,7 @@ class CommandTabCompleter { } private static List completeSchematic(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (sender.hasPermission(ParkourOption.ADMIN.permission)) { completions.addAll(SCHEMATIC_SUBCOMMANDS); } @@ -109,7 +113,7 @@ class CommandTabCompleter { } private static List completeCreate(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (sender.hasPermission(ParkourOption.ADMIN.permission)) { completions.addAll(SCHEMATIC_DIFFICULTIES); } @@ -117,7 +121,7 @@ class CommandTabCompleter { } private static List completeForce(CommandSender sender, String a1, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (sender.hasPermission(ParkourOption.ADMIN.permission)) { completions.add("everyone"); if ("forcejoin".equals(a1)) { @@ -129,7 +133,7 @@ class CommandTabCompleter { } private static List completeRecoverInventory(CommandSender sender, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if (sender.hasPermission(ParkourOption.ADMIN.permission)) { Bukkit.getOnlinePlayers().forEach(pl -> completions.add(pl.getName())); } @@ -144,7 +148,7 @@ class CommandTabCompleter { } private static List completeSchematicThird(String a2Raw, String input) { - List completions = new ArrayList<>(); + final List completions = new ArrayList<>(); if ("paste".equalsIgnoreCase(a2Raw)) { addLoadedSchematicIds(completions); } else if ("create".equalsIgnoreCase(a2Raw)) { @@ -161,13 +165,13 @@ class CommandTabCompleter { } private static void addLoadedSchematicIds(@NotNull List completions) { - SchematicManager manager = LoParkour.getSchematicManager(); + final SchematicManager manager = LoParkour.getSchematicManager(); if (manager == null) { return; } - for (String id : manager.getAll().keySet()) { + for (final String id : manager.getAll().keySet()) { completions.add(id); - String shortId = SchematicManager.configKey(id); + final String shortId = SchematicManager.configKey(id); if (!shortId.equalsIgnoreCase(id)) { completions.add(shortId); } @@ -175,7 +179,7 @@ class CommandTabCompleter { } private static List filter(String input, List options) { - String lower = input.toLowerCase(); + final String lower = input.toLowerCase(); return options.stream().filter(s -> s.toLowerCase().startsWith(lower)).toList(); } } \ No newline at end of file diff --git a/src/main/java/dev/loki/loparkour/command/core/LoParkourCommand.java b/src/main/java/dev/loki/loparkour/command/core/LoParkourCommand.java index d6d8dc7..a040581 100644 --- a/src/main/java/dev/loki/loparkour/command/core/LoParkourCommand.java +++ b/src/main/java/dev/loki/loparkour/command/core/LoParkourCommand.java @@ -30,7 +30,7 @@ public class LoParkourCommand implements CommandExecutor, TabCompleter { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { try { - Player p = sender instanceof Player ? (Player) sender : null; + final Player p = sender instanceof Player ? (Player) sender : null; router.dispatch(args, sender, p); return true; } catch (Throwable t) { diff --git a/src/main/java/dev/loki/loparkour/command/player/JoinCommandExecutor.java b/src/main/java/dev/loki/loparkour/command/player/JoinCommandExecutor.java index 75929c9..4a08852 100644 --- a/src/main/java/dev/loki/loparkour/command/player/JoinCommandExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/player/JoinCommandExecutor.java @@ -17,8 +17,10 @@ import org.bukkit.entity.Player; public class JoinCommandExecutor { + private static final int COOLDOWN_MS = 2500; + public void join(String arg, CommandSender sender, Player p, PlayerCommandHandler player) { - if (!player.cooldown(sender, "join", 2500) || !ParkourOption.JOIN.mayPerform(p)) { + if (!player.cooldown(sender, "join", COOLDOWN_MS) || !ParkourOption.JOIN.mayPerform(p)) { return; } if (Registry.getMode(arg) instanceof Mode mode) { @@ -29,12 +31,12 @@ public class JoinCommandExecutor { } private void joinPlayingPlayer(String arg, CommandSender sender, Player p) { - Player other = Bukkit.getPlayer(arg); + final Player other = Bukkit.getPlayer(arg); if (other == null) { CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.unknown_player")); return; } - ParkourPlayer pp = ParkourPlayer.getPlayer(other); + final ParkourPlayer pp = ParkourPlayer.getPlayer(other); if (pp == null) { CommandUtil.send(sender, LoParkour.PREFIX + Locales.getString(sender, "commands.not_playing")); return; @@ -43,7 +45,7 @@ public class JoinCommandExecutor { } private void joinSession(CommandSender sender, Player p, Session session) { - ParkourUser user = ParkourUser.getUser(p); + final ParkourUser user = ParkourUser.getUser(p); if (user != null && user.session == session) { return; } @@ -55,7 +57,7 @@ public class JoinCommandExecutor { } private void joinAcceptingSession(Player p, Session session) { - Mode sessionMode = session.generator.getMode(); + final Mode sessionMode = session.generator.getMode(); if (sessionMode instanceof MultiMode mm) { mm.join(p, session); } else { diff --git a/src/main/java/dev/loki/loparkour/command/player/LeaderboardCommandExecutor.java b/src/main/java/dev/loki/loparkour/command/player/LeaderboardCommandExecutor.java index f6f5883..4daede2 100644 --- a/src/main/java/dev/loki/loparkour/command/player/LeaderboardCommandExecutor.java +++ b/src/main/java/dev/loki/loparkour/command/player/LeaderboardCommandExecutor.java @@ -17,7 +17,7 @@ public class LeaderboardCommandExecutor { CommandUtil.send(sender, Locales.getString(p, "other.no_do")); return; } - Mode mode = Registry.getMode(arg.toLowerCase()); + final Mode mode = Registry.getMode(arg.toLowerCase()); if (mode == null) { Menus.LEADERBOARDS.open(p); } else { diff --git a/src/main/java/dev/loki/loparkour/command/player/PlayerCommandHandler.java b/src/main/java/dev/loki/loparkour/command/player/PlayerCommandHandler.java index 70445bb..26d628f 100644 --- a/src/main/java/dev/loki/loparkour/command/player/PlayerCommandHandler.java +++ b/src/main/java/dev/loki/loparkour/command/player/PlayerCommandHandler.java @@ -23,6 +23,8 @@ import java.util.Map; */ public class PlayerCommandHandler { + private static final int COOLDOWN_MS = 2500; + private final Map cooldowns = new HashMap<>(); public void handleNoArgs(@NotNull CommandSender sender, @Nullable Player player) { @@ -34,7 +36,8 @@ public class PlayerCommandHandler { } public void handle(@NotNull String arg, @NotNull CommandSender sender, @Nullable Player player) { - switch (arg.toLowerCase()) { + final String lower = arg.toLowerCase(); + switch (lower) { case "help" -> sendHelp(sender); case "reload" -> handleReload(sender, player); default -> { @@ -45,22 +48,14 @@ public class PlayerCommandHandler { return; } - switch (arg.toLowerCase()) { - case "join" -> { - if (!cooldown(sender, "join", 2500)) { - return; - } - if (!ParkourOption.JOIN.mayPerform(player)) { - send(sender, Locales.getString(player, "other.no_do")); - return; - } - if (ParkourUser.getUser(player) != null) { - return; - } - Modes.DEFAULT.create(player); - } + handlePlayerAction(lower, sender, player); + } + + private void handlePlayerAction(String lower, CommandSender sender, Player player) { + switch (lower) { + case "join" -> handleJoin(sender, player); case "leave" -> { - if (!cooldown(sender, "leave", 2500)) { + if (!cooldown(sender, "leave", COOLDOWN_MS)) { return; } ParkourUser.leave(player); @@ -94,8 +89,22 @@ public class PlayerCommandHandler { } } + private void handleJoin(CommandSender sender, Player player) { + if (!cooldown(sender, "join", COOLDOWN_MS)) { + return; + } + if (!ParkourOption.JOIN.mayPerform(player)) { + send(sender, Locales.getString(player, "other.no_do")); + return; + } + if (ParkourUser.getUser(player) != null) { + return; + } + Modes.DEFAULT.create(player); + } + private void handleReload(@NotNull CommandSender sender, @Nullable Player player) { - if (!cooldown(sender, "reload", 2500)) { + if (!cooldown(sender, "reload", COOLDOWN_MS)) { return; } if (!sender.hasPermission(ParkourOption.ADMIN.permission)) { @@ -143,9 +152,9 @@ public class PlayerCommandHandler { * @param whenBlocked message sent ({@code &#RRGGBB} / {@code &} colors) if still on cooldown; may be null for silent block */ public boolean cooldown(CommandSender sender, String key, long millis, @Nullable String whenBlocked) { - String fullKey = sender.getName() + ":" + key; - long now = System.currentTimeMillis(); - Long last = cooldowns.get(fullKey); + final String fullKey = sender.getName() + ":" + key; + final long now = System.currentTimeMillis(); + final Long last = cooldowns.get(fullKey); if (last != null && now - last < millis) { if (whenBlocked != null) { send(sender, whenBlocked); diff --git a/src/main/java/dev/loki/loparkour/command/schematic/SchematicCommandHandler.java b/src/main/java/dev/loki/loparkour/command/schematic/SchematicCommandHandler.java index 65d3c6b..6aacbd7 100644 --- a/src/main/java/dev/loki/loparkour/command/schematic/SchematicCommandHandler.java +++ b/src/main/java/dev/loki/loparkour/command/schematic/SchematicCommandHandler.java @@ -18,6 +18,8 @@ import java.util.Map; */ public class SchematicCommandHandler { + private static final int COOLDOWN_MS = 2500; + private SchematicCommandHandler() { } @@ -66,7 +68,7 @@ public class SchematicCommandHandler { Player player, PlayerCommandHandler cooldowns ) { - if (cooldowns.cooldown(sender, "schematic-create", 2500)) { + if (cooldowns.cooldown(sender, "schematic-create", COOLDOWN_MS)) { SchematicHandler.create(name, difficultyRaw, sender, player); } } @@ -78,7 +80,7 @@ public class SchematicCommandHandler { Player player, PlayerCommandHandler cooldowns ) { - if (cooldowns.cooldown(sender, "schematic-create", 2500)) { + if (cooldowns.cooldown(sender, "schematic-create", COOLDOWN_MS)) { SchematicHandler.create(null, difficultyRaw, sender, player); } } diff --git a/src/main/java/dev/loki/loparkour/command/schematic/SchematicDispatcher.java b/src/main/java/dev/loki/loparkour/command/schematic/SchematicDispatcher.java index 3a9eb70..cb86d88 100644 --- a/src/main/java/dev/loki/loparkour/command/schematic/SchematicDispatcher.java +++ b/src/main/java/dev/loki/loparkour/command/schematic/SchematicDispatcher.java @@ -10,12 +10,14 @@ import org.bukkit.entity.Player; final class SchematicDispatcher { + private static final int COOLDOWN_MS = 2500; + private SchematicDispatcher() { } static void dispatch(String sub, CommandSender sender, Player player, PlayerCommandHandler cooldowns) { - Location playerLoc = player.getLocation(); - Location[] sel = SchematicCommandHandler.getSelection(player); + final Location playerLoc = player.getLocation(); + final Location[] sel = SchematicCommandHandler.getSelection(player); switch (sub.toLowerCase()) { case "wand" -> { @@ -31,7 +33,7 @@ final class SchematicDispatcher { .formatted(name, s.getDifficulty(), s.getFormat().name().toLowerCase()))); } case "reload" -> { - if (!cooldowns.cooldown(sender, "schematic-reload", 2500)) { + if (!cooldowns.cooldown(sender, "schematic-reload", COOLDOWN_MS)) { return; } Config.reload(false); diff --git a/src/main/java/dev/loki/loparkour/command/schematic/SchematicHandler.java b/src/main/java/dev/loki/loparkour/command/schematic/SchematicHandler.java index 126b7f2..b80f59b 100644 --- a/src/main/java/dev/loki/loparkour/command/schematic/SchematicHandler.java +++ b/src/main/java/dev/loki/loparkour/command/schematic/SchematicHandler.java @@ -18,6 +18,9 @@ import java.io.IOException; final class SchematicHandler { + private static final double PARTICLE_STEP = 0.2; + private static final int MAX_SCHEMATIC_NAME_LENGTH = 64; + private SchematicHandler() { } @@ -27,13 +30,13 @@ final class SchematicHandler { CommandSender sender, Player player ) { - Location[] sel = SchematicCommandHandler.SELECTIONS.get(player); + final Location[] sel = SchematicCommandHandler.SELECTIONS.get(player); if (sel == null || sel[0] == null || sel[1] == null) { SchematicMessenger.send(player, LoParkour.PREFIX + Locales.getString(player, "schematic.selection_incomplete")); return; } - double difficulty; + final double difficulty; try { difficulty = SchematicCreator.parseDifficulty(difficultyRaw); } catch (IllegalArgumentException e) { @@ -42,7 +45,7 @@ final class SchematicHandler { } try { - SchematicCreator.CreateResult result = SchematicCreator.create(sel[0], sel[1], optionalName, difficulty); + final SchematicCreator.CreateResult result = SchematicCreator.create(sel[0], sel[1], optionalName, difficulty); LoParkour.getSchematicManager().reload(); SchematicMessenger.send(player, LoParkour.PREFIX + Locales.getString(player, "schematic.created") .formatted(result.stem(), result.difficulty(), result.file().getName())); @@ -60,7 +63,7 @@ final class SchematicHandler { return; } - ParkourSchematic schematic = LoParkour.getSchematicManager().get(name); + final ParkourSchematic schematic = LoParkour.getSchematicManager().get(name); if (schematic == null) { SchematicMessenger.send(sender, LoParkour.PREFIX + Locales.getString(sender, "schematic.not_found") .formatted(name)); @@ -73,18 +76,18 @@ final class SchematicHandler { static void posCommand(Player player, Location[] existing, Location loc, int index) { SchematicCommandHandler.setPos(player, loc, index); - String label = index == 0 ? "1" : "2"; + final String label = index == 0 ? "1" : "2"; SchematicMessenger.send(player, LoParkour.PREFIX + Locales.getString(player, "schematic.pos_set") .formatted(label, Locations.toString(loc, true))); - Location[] updated = SchematicCommandHandler.getSelection(player); + final Location[] updated = SchematicCommandHandler.getSelection(player); if (updated[0] != null && updated[1] != null) { - ParticleUtil.box(BoundingBox.of(updated[0], updated[1]), player.getWorld(), Particle.END_ROD, player, 0.2); + ParticleUtil.box(BoundingBox.of(updated[0], updated[1]), player.getWorld(), Particle.END_ROD, player, PARTICLE_STEP); } } static boolean isValidSchematicName(String name) { - if (name == null || name.isEmpty() || name.length() > 64) { + if (name == null || name.isEmpty() || name.length() > MAX_SCHEMATIC_NAME_LENGTH) { return false; } return name.matches("^[a-zA-Z0-9_-]+$"); diff --git a/src/main/java/dev/loki/loparkour/command/schematic/SchematicWand.java b/src/main/java/dev/loki/loparkour/command/schematic/SchematicWand.java index 783b1fa..eacc601 100644 --- a/src/main/java/dev/loki/loparkour/command/schematic/SchematicWand.java +++ b/src/main/java/dev/loki/loparkour/command/schematic/SchematicWand.java @@ -25,7 +25,7 @@ final class SchematicWand { if (item == null || !item.hasItemMeta()) { return false; } - ItemMeta meta = item.getItemMeta(); + final ItemMeta meta = item.getItemMeta(); return meta != null && meta.getPersistentDataContainer().has(SchematicCommandHandler.WAND_KEY, PersistentDataType.BYTE); } @@ -45,8 +45,8 @@ final class SchematicWand { locale = "en"; } - ItemStack item = Locales.getItem(locale, "schematic.wand").build(); - ItemMeta meta = item.getItemMeta(); + final ItemStack item = Locales.getItem(locale, "schematic.wand").build(); + final ItemMeta meta = item.getItemMeta(); if (meta != null) { meta.getPersistentDataContainer().set(SchematicCommandHandler.WAND_KEY, PersistentDataType.BYTE, (byte) 1); item.setItemMeta(meta); diff --git a/src/main/java/dev/loki/loparkour/command/schematic/SelectionStore.java b/src/main/java/dev/loki/loparkour/command/schematic/SelectionStore.java index 019d559..19dfebb 100644 --- a/src/main/java/dev/loki/loparkour/command/schematic/SelectionStore.java +++ b/src/main/java/dev/loki/loparkour/command/schematic/SelectionStore.java @@ -20,7 +20,7 @@ final class SelectionStore { } static void set(@NotNull Player player, @NotNull Location loc, int index) { - Location[] updated = get(player).clone(); + final Location[] updated = get(player).clone(); updated[index] = loc.clone(); SchematicCommandHandler.SELECTIONS.put(player, updated); } diff --git a/src/main/java/dev/loki/loparkour/config/core/Config.java b/src/main/java/dev/loki/loparkour/config/core/Config.java index 68f4b6f..a3daeda 100644 --- a/src/main/java/dev/loki/loparkour/config/core/Config.java +++ b/src/main/java/dev/loki/loparkour/config/core/Config.java @@ -38,7 +38,7 @@ public enum Config { ConfigLoader.reloadAllConfigs(initialLoad); // Update accessors for all configs - for (Config config : values()) { + for (final Config config : values()) { if (config.fileConfiguration != null) { config.accessor = new ConfigAccessor(config.fileConfiguration, config.fileName); } diff --git a/src/main/java/dev/loki/loparkour/config/core/ConfigAccessor.java b/src/main/java/dev/loki/loparkour/config/core/ConfigAccessor.java index 9b264f0..491412d 100644 --- a/src/main/java/dev/loki/loparkour/config/core/ConfigAccessor.java +++ b/src/main/java/dev/loki/loparkour/config/core/ConfigAccessor.java @@ -90,7 +90,7 @@ public class ConfigAccessor { @NotNull public String getString(@NotNull String path) { warnIfMissing(path); - Object value = config.get(path); + final Object value = config.get(path); if (value == null) { return ""; } @@ -126,12 +126,12 @@ public class ConfigAccessor { return new ArrayList<>(); } - ConfigurationSection section = config.getConfigurationSection(path); + final ConfigurationSection section = config.getConfigurationSection(path); if (section == null) { return new ArrayList<>(); } - boolean isDeep = deep.length > 0 && deep[0]; + final boolean isDeep = deep.length > 0 && deep[0]; return new ArrayList<>(section.getKeys(isDeep)); } diff --git a/src/main/java/dev/loki/loparkour/config/core/ConfigLoader.java b/src/main/java/dev/loki/loparkour/config/core/ConfigLoader.java index c8c4f45..1b89bdf 100644 --- a/src/main/java/dev/loki/loparkour/config/core/ConfigLoader.java +++ b/src/main/java/dev/loki/loparkour/config/core/ConfigLoader.java @@ -28,7 +28,7 @@ public class ConfigLoader { @NotNull public static File initializeConfigFile(@NotNull String fileName) { - File configFile = LoParkour.getInFolder(fileName); + final File configFile = LoParkour.getInFolder(fileName); if (!configFile.exists()) { LoParkour.getPlugin().saveResource(fileName, false); } @@ -36,7 +36,7 @@ public class ConfigLoader { } public static void updateConfig(@NotNull String fileName, @NotNull File configFile, @Nullable List ignoredSections) { - var plugin = LoParkour.getPlugin(); + final var plugin = LoParkour.getPlugin(); try (var resourceStream = plugin.getResource(fileName)) { if (resourceStream != null) { ConfigUpdater.update(configFile, resourceStream, "1.0", ignoredSections, plugin.getLogger()); @@ -48,7 +48,7 @@ public class ConfigLoader { } public static void reloadAllConfigs(boolean initialLoad) { - for (Config config : Config.values()) { + for (final Config config : Config.values()) { if (initialLoad) { config.path = initializeConfigFile(config.fileName); updateConfig(config.fileName, config.path, config.ignoredSections); @@ -69,18 +69,18 @@ public class ConfigLoader { // ── inline validation (was ConfigValidatorRefactored) ───────────────────── private static void validateConfigs() { - Logger log = LoParkour.getPlugin().getLogger(); + final Logger log = LoParkour.getPlugin().getLogger(); if (Config.CONFIG.isPath("world.max-y") && Config.CONFIG.isPath("world.min-y")) { - int maxY = Config.CONFIG.getInt("world.max-y"); - int minY = Config.CONFIG.getInt("world.min-y"); + final int maxY = Config.CONFIG.getInt("world.max-y"); + final int minY = Config.CONFIG.getInt("world.min-y"); if (maxY <= minY) { log.severe("Invalid config: world.max-y (%d) must be > world.min-y (%d)".formatted(maxY, minY)); } } if (Config.GENERATION.isPath("generation.normal.height")) { - double sum = Config.GENERATION.getDouble("generation.normal.height.1") + final double sum = Config.GENERATION.getDouble("generation.normal.height.1") + Config.GENERATION.getDouble("generation.normal.height.0") + Config.GENERATION.getDouble("generation.normal.height.-1") + Config.GENERATION.getDouble("generation.normal.height.-2"); diff --git a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeParser.java b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeParser.java index a295875..06e05ab 100644 --- a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeParser.java +++ b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeParser.java @@ -35,17 +35,17 @@ public final class ConfigMergeParser { @NotNull public Map extractFullPathValues(@NotNull List lines) { - Map values = new LinkedHashMap<>(); - Deque stack = new ArrayDeque<>(); + final Map values = new LinkedHashMap<>(); + final Deque stack = new ArrayDeque<>(); int prevIndent = -1; - for (String line : lines) { - LineTokens tokens = parseLine(line); + for (final String line : lines) { + final LineTokens tokens = parseLine(line); if (tokens == null) { continue; } prevIndent = popToLevel(stack, tokens.indent(), prevIndent); - String fullPath = resolvePath(stack, tokens.leaf()); + final String fullPath = resolvePath(stack, tokens.leaf()); if (tokens.isSection()) { stack.push(tokens.leaf()); } else { @@ -61,18 +61,18 @@ public final class ConfigMergeParser { if (isSkippable(line)) { return null; } - String trimmed = line.trim(); - int colon = trimmed.indexOf(':'); + final String trimmed = line.trim(); + final int colon = trimmed.indexOf(':'); if (colon == -1) { return null; } - String leaf = trimmed.substring(0, colon).trim(); - String after = trimmed.substring(colon + 1).trim(); + final String leaf = trimmed.substring(0, colon).trim(); + final String after = trimmed.substring(colon + 1).trim(); return new LineTokens(leaf, after, indentOf(line), isSection(after)); } private static boolean isSkippable(@NotNull String line) { - String trimmed = line.trim(); + final String trimmed = line.trim(); return trimmed.isEmpty() || trimmed.startsWith("#") || trimmed.startsWith("-"); } @@ -81,13 +81,14 @@ public final class ConfigMergeParser { } public static int popToLevel(@NotNull Deque stack, int indent, int prevIndent) { - while (prevIndent >= 0 && indent <= prevIndent) { + int current = prevIndent; + while (current >= 0 && indent <= current) { if (!stack.isEmpty()) { stack.pop(); } - prevIndent -= 2; + current -= 2; } - return prevIndent; + return current; } @NotNull diff --git a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeProcessor.java b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeProcessor.java index 663277e..4494df8 100644 --- a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeProcessor.java +++ b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigMergeProcessor.java @@ -28,24 +28,24 @@ public final class ConfigMergeProcessor { @NotNull File existingFile, @Nullable List ignoredKeys) throws IOException { - List templateLines = parser.readLines(templateStream); - List existingLines = existingFile.exists() ? parser.readLines(existingFile) : List.of(); + final List templateLines = parser.readLines(templateStream); + final List existingLines = existingFile.exists() ? parser.readLines(existingFile) : List.of(); - Set ignored = normalizeIgnored(ignoredKeys); - Map existingValues = parser.extractFullPathValues(existingLines); + final Set ignored = normalizeIgnored(ignoredKeys); + final Map existingValues = parser.extractFullPathValues(existingLines); - List result = new ArrayList<>(); - Deque stack = new ArrayDeque<>(); + final List result = new ArrayList<>(); + final Deque stack = new ArrayDeque<>(); int prevIndent = -1; - for (String line : templateLines) { - ConfigMergeParser.LineTokens tokens = ConfigMergeParser.parseLine(line); + for (final String line : templateLines) { + final ConfigMergeParser.LineTokens tokens = ConfigMergeParser.parseLine(line); if (tokens == null) { result.add(line); continue; } prevIndent = ConfigMergeParser.popToLevel(stack, tokens.indent(), prevIndent); - String fullPath = ConfigMergeParser.resolvePath(stack, tokens.leaf()); + final String fullPath = ConfigMergeParser.resolvePath(stack, tokens.leaf()); if (tokens.isSection()) { stack.push(tokens.leaf()); result.add(line); @@ -65,7 +65,7 @@ public final class ConfigMergeProcessor { @NotNull String fullPath, @NotNull Map existingValues) { - String existing = existingValues.get(fullPath); + final String existing = existingValues.get(fullPath); if (existing == null) { return line; } @@ -77,16 +77,16 @@ public final class ConfigMergeProcessor { if (keys == null) { return Set.of(); } - Set s = new HashSet<>(); - for (String k : keys) { + final Set s = new HashSet<>(); + for (final String k : keys) { s.add(k.trim().toLowerCase()); } return s; } private static boolean isIgnored(@NotNull String fullPath, @NotNull Set ignored) { - String lower = fullPath.toLowerCase(); - for (String ign : ignored) { + final String lower = fullPath.toLowerCase(); + for (final String ign : ignored) { if (lower.equals(ign) || lower.startsWith(ign + ".")) { return true; } diff --git a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigUpdater.java b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigUpdater.java index 91f4d3f..ce41c08 100644 --- a/src/main/java/dev/loki/loparkour/config/core/merge/ConfigUpdater.java +++ b/src/main/java/dev/loki/loparkour/config/core/merge/ConfigUpdater.java @@ -41,9 +41,9 @@ public class ConfigUpdater { @NotNull InputStream templateStream, @Nullable List ignoredKeys) throws IOException { - File backup = createBackup(configFile); + final File backup = createBackup(configFile); try { - List merged = new ConfigMergeProcessor().merge(templateStream, configFile, ignoredKeys); + final List merged = new ConfigMergeProcessor().merge(templateStream, configFile, ignoredKeys); Files.write(configFile.toPath(), merged); logger.info("Configuration file synced: " + configFile.getName()); } catch (Exception e) { @@ -61,7 +61,7 @@ public class ConfigUpdater { return null; } try { - File bak = new File(file.getParent(), file.getName() + ".bak"); + final File bak = new File(file.getParent(), file.getName() + ".bak"); Files.copy(file.toPath(), bak.toPath(), StandardCopyOption.REPLACE_EXISTING); return bak; } catch (IOException e) { diff --git a/src/main/java/dev/loki/loparkour/config/locale/LocaleCache.java b/src/main/java/dev/loki/loparkour/config/locale/LocaleCache.java index 109a522..ced235f 100644 --- a/src/main/java/dev/loki/loparkour/config/locale/LocaleCache.java +++ b/src/main/java/dev/loki/loparkour/config/locale/LocaleCache.java @@ -34,12 +34,12 @@ public class LocaleCache { if (locales.isEmpty()) { return defaultValue; } - FileConfiguration config = locales.get(locale); + final FileConfiguration config = locales.get(locale); if (config == null) { return defaultValue; } try { - T result = extractor.apply(config); + final T result = extractor.apply(config); return result != null ? result : defaultValue; } catch (Exception e) { return defaultValue; diff --git a/src/main/java/dev/loki/loparkour/config/locale/LocaleLoader.java b/src/main/java/dev/loki/loparkour/config/locale/LocaleLoader.java index a1f29bd..6b164fc 100644 --- a/src/main/java/dev/loki/loparkour/config/locale/LocaleLoader.java +++ b/src/main/java/dev/loki/loparkour/config/locale/LocaleLoader.java @@ -34,9 +34,9 @@ public class LocaleLoader { */ @NotNull public static Map loadAll() { - Plugin plugin = LoParkour.getPlugin(); + final Plugin plugin = LoParkour.getPlugin(); try { - FileConfiguration embedded = YamlConfiguration.loadConfiguration( + final FileConfiguration embedded = YamlConfiguration.loadConfiguration( new InputStreamReader(plugin.getResource("locales/en.yml"), StandardCharsets.UTF_8)); return loadFromDisk(embedded); @@ -48,21 +48,21 @@ public class LocaleLoader { @NotNull private static Map loadFromDisk(@NotNull FileConfiguration embedded) throws Exception { - Map result = new HashMap<>(); - File folder = LoParkour.getInFolder("locales"); + final Map result = new HashMap<>(); + final File folder = LoParkour.getInFolder("locales"); ensureFolder(folder); try (Stream stream = Files.list(folder.toPath())) { stream.forEach(path -> { - File file = path.toFile(); + final File file = path.toFile(); if (!file.getName().endsWith(".yml")) { return; } - String locale = file.getName().split("\\.")[0]; + final String locale = file.getName().split("\\.")[0]; LoParkour.log("Found locale " + locale); - FileConfiguration config = YamlConfiguration.loadConfiguration(file); + final FileConfiguration config = YamlConfiguration.loadConfiguration(file); validateAndFix(embedded, config, file); result.put(locale, config); }); @@ -74,9 +74,9 @@ public class LocaleLoader { if (!folder.exists()) { folder.mkdirs(); } - String[] files = folder.list(); + final String[] files = folder.list(); if (files == null || files.length == 0) { - Plugin plugin = LoParkour.getPlugin(); + final Plugin plugin = LoParkour.getPlugin(); plugin.saveResource("locales/en.yml", false); plugin.saveResource("locales/ru.yml", false); } @@ -88,11 +88,11 @@ public class LocaleLoader { private static void validateAndFix(@NotNull FileConfiguration reference, @NotNull FileConfiguration locale, @NotNull File file) { - List refNodes = getChildren(reference); - List locNodes = getChildren(locale); + final List refNodes = getChildren(reference); + final List locNodes = getChildren(locale); boolean modified = false; - for (String node : refNodes) { + for (final String node : refNodes) { if (!locNodes.contains(node)) { LoParkour.log("Fixing missing config node %s in %s".formatted(node, file.getName())); locale.set(node, reference.get(node)); @@ -112,7 +112,7 @@ public class LocaleLoader { @NotNull private static List getChildren(@NotNull FileConfiguration config) { - ConfigurationSection section = config.getConfigurationSection(""); + final ConfigurationSection section = config.getConfigurationSection(""); return section != null ? new ArrayList<>(section.getKeys(true)) : Collections.emptyList(); } } diff --git a/src/main/java/dev/loki/loparkour/config/locale/Locales.java b/src/main/java/dev/loki/loparkour/config/locale/Locales.java index fcd317d..6b774cf 100644 --- a/src/main/java/dev/loki/loparkour/config/locale/Locales.java +++ b/src/main/java/dev/loki/loparkour/config/locale/Locales.java @@ -35,7 +35,7 @@ public class Locales { // ── Initialization ──────────────────────────────────────────────────────── public static void init() { - Map loaded = LocaleLoader.loadAll(); + final Map loaded = LocaleLoader.loadAll(); CACHE.setLocales(loaded); dev.loki.loparkour.command.schematic.SchematicCommandHandler.clearWandCache(); LoParkour.log("Locales loaded successfully (" + loaded.size() + " locales)"); @@ -113,7 +113,7 @@ public class Locales { LoParkour.getPlugin().getLogger().warning("Locales are empty! Returning STONE item for path: " + path); return new Item(Material.STONE, ""); } - FileConfiguration config = CACHE.getLocale(locale); + final FileConfiguration config = CACHE.getLocale(locale); if (config == null) { LoParkour.getPlugin().getLogger().warning( "Locale '" + locale + "' not found! Available: " + CACHE.getLocaleKeys() + ". Returning STONE for path: " + path); @@ -126,13 +126,13 @@ public class Locales { @NotNull private static Item buildItem(@NotNull FileConfiguration config, @NotNull String path, String... replace) { - String materialPath = "%s.material".formatted(path); - String namePath = "%s.name".formatted(path); - String lorePath = "%s.lore".formatted(path); + final String materialPath = "%s.material".formatted(path); + final String namePath = "%s.name".formatted(path); + final String lorePath = "%s.lore".formatted(path); - String material = config.getString(materialPath, "STONE"); - String name = applyReplacements(config.getString(namePath, ""), replace); - String lore = applyReplacements(config.getString(lorePath, ""), replace); + final String material = config.getString(materialPath, "STONE"); + final String name = applyReplacements(config.getString(namePath, ""), replace); + final String lore = applyReplacements(config.getString(lorePath, ""), replace); if ("STONE".equals(material) && !config.contains(materialPath)) { LoParkour.getPlugin().getLogger().warning( @@ -146,7 +146,7 @@ public class Locales { mat = Material.STONE; } - Item item = new Item(mat, name); + final Item item = new Item(mat, name); if (!lore.isEmpty()) { item.lore(lore.split("\\|\\|")); } @@ -160,7 +160,7 @@ public class Locales { } String result = text; int idx = 0; - Matcher matcher = REPLACEMENT_PATTERN.matcher(result); + final Matcher matcher = REPLACEMENT_PATTERN.matcher(result); while (matcher.find() && idx < replacements.length) { result = result.replaceFirst(matcher.group(), replacements[idx++]); } @@ -169,7 +169,7 @@ public class Locales { @NotNull private static String getPlayerLocale(@NotNull Player player) { - ParkourUser user = ParkourUser.getUser(player); + final ParkourUser user = ParkourUser.getUser(player); String loc = user == null ? Option.OPTIONS_DEFAULTS.get(ParkourOption.LANG) : user.locale; // Fix legacy Boolean "true" values from old config parsing diff --git a/src/main/java/dev/loki/loparkour/config/options/OptionGeneral.java b/src/main/java/dev/loki/loparkour/config/options/OptionGeneral.java index 466d087..5c4a2b6 100644 --- a/src/main/java/dev/loki/loparkour/config/options/OptionGeneral.java +++ b/src/main/java/dev/loki/loparkour/config/options/OptionGeneral.java @@ -19,6 +19,11 @@ import java.util.Map; */ public class OptionGeneral { + private static final int MAX_LEAD = 128; + private static final int MIN_AXIS_VALUES = 4; + private static final int WORLD_VALUE_INDEX = 3; + private static final int FALLBACK_Y = 100; + private OptionGeneral() { } @@ -41,55 +46,66 @@ public class OptionGeneral { } public static void initOptions() { - List options = new ArrayList<>(Arrays.asList(ParkourOption.values())); + final List options = new ArrayList<>(Arrays.asList(ParkourOption.values())); options.remove(ParkourOption.JOIN); options.remove(ParkourOption.ADMIN); OPTIONS_DEFAULTS = new HashMap<>(); OPTIONS_ENABLED = new HashMap<>(); - for (ParkourOption option : options) { - String parent = "default-values." + option.path; - OPTIONS_ENABLED.put(option, Config.CONFIG.getBoolean(parent + ".enabled")); + for (final ParkourOption option : options) { + applyOption(option); + } + } - String defaultPath = parent + ".default"; - if (Config.CONFIG.isPath(defaultPath)) { - String value = Config.CONFIG.getString(defaultPath); + private static void applyOption(ParkourOption option) { + final String parent = "default-values." + option.path; + OPTIONS_ENABLED.put(option, Config.CONFIG.getBoolean(parent + ".enabled")); - // Sanitize legacy Boolean values for LANG option - if (option == ParkourOption.LANG && - ("true".equals(value) || "false".equals(value) || "1".equals(value) || "0".equals(value))) { - value = "en"; - LoParkour.getPlugin().getLogger().warning( - "LANG option had invalid Boolean value, reset to 'en'"); - } + final String defaultPath = parent + ".default"; + if (Config.CONFIG.isPath(defaultPath)) { + applyDefaultValue(option, defaultPath); + } else { + warnMissingDefault(option, defaultPath); + } + } - // Debug logging - if (option == ParkourOption.LANG) { - LoParkour.getPlugin().getLogger().info( - "LANG option: path=" + defaultPath + ", value=" + value); - } + private static void applyDefaultValue(ParkourOption option, String defaultPath) { + String value = Config.CONFIG.getString(defaultPath); - if (value != null && !value.isEmpty()) { - OPTIONS_DEFAULTS.put(option, value); - } - } else { - // Debug: path not found - if (option == ParkourOption.LANG) { - LoParkour.getPlugin().getLogger().warning("LANG default path not found: " + defaultPath); - LoParkour.getPlugin().getLogger().warning( - "Available keys: " + Config.CONFIG.fileConfiguration.getKeys(true)); - } - } + // Sanitize legacy Boolean values for LANG option + if (option == ParkourOption.LANG && + ("true".equals(value) || "false".equals(value) || "1".equals(value) || "0".equals(value))) { + value = "en"; + LoParkour.getPlugin().getLogger().warning( + "LANG option had invalid Boolean value, reset to 'en'"); + } + + // Debug logging + if (option == ParkourOption.LANG) { + LoParkour.getPlugin().getLogger().info( + "LANG option: path=" + defaultPath + ", value=" + value); + } + + if (value != null && !value.isEmpty()) { + OPTIONS_DEFAULTS.put(option, value); + } + } + + private static void warnMissingDefault(ParkourOption option, String defaultPath) { + if (option == ParkourOption.LANG) { + LoParkour.getPlugin().getLogger().warning("LANG default path not found: " + defaultPath); + LoParkour.getPlugin().getLogger().warning( + "Available keys: " + Config.CONFIG.fileConfiguration.getKeys(true)); } } private static void initGoBackLocation() { GO_BACK_LOC = parseLocation(Config.CONFIG.getString("bungeecord.go-back")); try { - String axesRaw = Config.CONFIG.getString("bungeecord.go-back-axes"); + final String axesRaw = Config.CONFIG.getString("bungeecord.go-back-axes"); if (!axesRaw.isEmpty()) { - String[] axes = axesRaw.split(","); + final String[] axes = axesRaw.split(","); if (axes.length >= 2) { GO_BACK_LOC.setPitch(Float.parseFloat(axes[0].trim())); GO_BACK_LOC.setYaw(Float.parseFloat(axes[1].trim())); @@ -102,8 +118,8 @@ public class OptionGeneral { private static void initLeads() { POSSIBLE_LEADS = Config.CONFIG.getIntList("options.leads.amount"); - for (int lead : new ArrayList<>(POSSIBLE_LEADS)) { - if (lead < 1 || lead > 128) { + for (final int lead : new ArrayList<>(POSSIBLE_LEADS)) { + if (lead < 1 || lead > MAX_LEAD) { LoParkour.getPlugin().getLogger().severe( "Invalid lead: %d. Must be 1–128.".formatted(lead)); POSSIBLE_LEADS.remove((Object) lead); @@ -112,7 +128,7 @@ public class OptionGeneral { } private static void initHeading() { - String heading = Config.GENERATION.getString("advanced.island.parkour.heading"); + final String heading = Config.GENERATION.getString("advanced.island.parkour.heading"); HEADING = switch (heading.toLowerCase()) { case "north" -> BlockFace.NORTH; case "south" -> BlockFace.SOUTH; @@ -127,11 +143,11 @@ public class OptionGeneral { private static Location parseLocation(String location) { try { - String[] values = location.replaceAll("[()]", "").replaceAll("[, ]", " ").trim().split("\\s+"); - if (values.length < 4) { + final String[] values = location.replaceAll("[()]", "").replaceAll("[, ]", " ").trim().split("\\s+"); + if (values.length < MIN_AXIS_VALUES) { throw new IllegalArgumentException("Expected 4 values (x,y,z,world), got " + values.length); } - World world = Bukkit.getWorld(values[3]); + World world = Bukkit.getWorld(values[WORLD_VALUE_INDEX]); if (world == null) { world = Bukkit.getWorlds().get(0); } @@ -140,8 +156,8 @@ public class OptionGeneral { } catch (Exception ex) { LoParkour.getPlugin().getLogger().warning( "Invalid bungeecord.go-back value '" + location + "'. Falling back to spawn world origin."); - World fallback = Bukkit.getWorlds().isEmpty() ? null : Bukkit.getWorlds().get(0); - return new Location(fallback, 0, 100, 0); + final World fallback = Bukkit.getWorlds().isEmpty() ? null : Bukkit.getWorlds().get(0); + return new Location(fallback, 0, FALLBACK_Y, 0); } } } diff --git a/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java b/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java index 807c709..f18fb09 100644 --- a/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java +++ b/src/main/java/dev/loki/loparkour/config/options/section/OptionGeneration.java @@ -11,6 +11,18 @@ import java.util.Map; */ public class OptionGeneration { + private static final double DEFAULT_MAX_JUMP_DISTANCE = 5.0; + private static final double DEFAULT_MAX_HORIZONTAL_DISTANCE = 4.0; + private static final double DEFAULT_MAX_VERTICAL_DOWN = 3.0; + private static final int DEFAULT_BLOCK_CLEANUP_DISTANCE = 100; + private static final int DEFAULT_CLEANUP_INTERVAL = 100; + private static final int DEFAULT_TRAIL_KEEP_BEHIND = 10; + private static final int DEFAULT_GHOST_SHOW_TOP = 3; + private static final double DEFAULT_GHOST_TRANSPARENCY = 0.5; + private static final double PERCENT_DIVISOR = 100.0; + private static final int DEFAULT_MIN_Y = 100; + private static final int DEFAULT_MAX_Y = 200; + private OptionGeneration() { } @@ -67,13 +79,13 @@ public class OptionGeneration { JUMP_VALIDATION_ENABLED = Config.CONFIG.isPath("jump-validation.enabled") && Config.CONFIG.getBoolean("jump-validation.enabled"); MAX_JUMP_DISTANCE = Config.CONFIG.isPath("jump-validation.max-distance") - ? Config.CONFIG.getDouble("jump-validation.max-distance") : 5.0; + ? Config.CONFIG.getDouble("jump-validation.max-distance") : DEFAULT_MAX_JUMP_DISTANCE; MAX_HORIZONTAL_DISTANCE = Config.CONFIG.isPath("jump-validation.max-horizontal") - ? Config.CONFIG.getDouble("jump-validation.max-horizontal") : 4.0; + ? Config.CONFIG.getDouble("jump-validation.max-horizontal") : DEFAULT_MAX_HORIZONTAL_DISTANCE; MAX_VERTICAL_UP = Config.CONFIG.isPath("jump-validation.max-vertical-up") ? Config.CONFIG.getDouble("jump-validation.max-vertical-up") : 1.0; MAX_VERTICAL_DOWN = Config.CONFIG.isPath("jump-validation.max-vertical-down") - ? Config.CONFIG.getDouble("jump-validation.max-vertical-down") : 3.0; + ? Config.CONFIG.getDouble("jump-validation.max-vertical-down") : DEFAULT_MAX_VERTICAL_DOWN; } private static void initJumpTypes() { @@ -83,8 +95,8 @@ public class OptionGeneration { JUMP_TYPE_CHANCE = new HashMap<>(); if (Config.CONFIG.isPath("jump-types.types")) { - for (String type : Config.CONFIG.getChildren("jump-types.types")) { - String p = "jump-types.types." + type; + for (final String type : Config.CONFIG.getChildren("jump-types.types")) { + final String p = "jump-types.types." + type; JUMP_TYPE_ENABLED.put(type, Config.CONFIG.isPath(p + ".enabled") && Config.CONFIG.getBoolean(p + ".enabled")); JUMP_TYPE_CHANCE.put(type, Config.CONFIG.isPath(p + ".chance") @@ -95,48 +107,48 @@ public class OptionGeneration { private static void initMemory() { BLOCK_CLEANUP_DISTANCE = Config.CONFIG.isPath("memory.block-cleanup-distance") - ? Config.CONFIG.getInt("memory.block-cleanup-distance") : 100; + ? Config.CONFIG.getInt("memory.block-cleanup-distance") : DEFAULT_BLOCK_CLEANUP_DISTANCE; CLEANUP_INTERVAL = Config.CONFIG.isPath("memory.cleanup-interval") - ? Config.CONFIG.getInt("memory.cleanup-interval") : 100; + ? Config.CONFIG.getInt("memory.cleanup-interval") : DEFAULT_CLEANUP_INTERVAL; TRAIL_KEEP_BEHIND = Config.CONFIG.isPath("memory.trail-keep-behind") - ? Config.CONFIG.getInt("memory.trail-keep-behind") : 10; + ? Config.CONFIG.getInt("memory.trail-keep-behind") : DEFAULT_TRAIL_KEEP_BEHIND; } private static void initGhostMode() { GHOST_MODE_ENABLED = Config.CONFIG.isPath("ghost-mode.enabled") && Config.CONFIG.getBoolean("ghost-mode.enabled"); GHOST_SHOW_TOP = Config.CONFIG.isPath("ghost-mode.show-top") - ? Config.CONFIG.getInt("ghost-mode.show-top") : 3; + ? Config.CONFIG.getInt("ghost-mode.show-top") : DEFAULT_GHOST_SHOW_TOP; GHOST_TRANSPARENCY = Config.CONFIG.isPath("ghost-mode.transparency") - ? Config.CONFIG.getDouble("ghost-mode.transparency") : 0.5; + ? Config.CONFIG.getDouble("ghost-mode.transparency") : DEFAULT_GHOST_TRANSPARENCY; } private static void initGenerationProbabilities() { - TYPE_NORMAL = Config.GENERATION.getInt("generation.type.normal") / 100.0; - TYPE_SPECIAL = Config.GENERATION.getInt("generation.type.special") / 100.0; - TYPE_SCHEMATICS = Config.GENERATION.getInt("generation.type.schematic") / 100.0; + TYPE_NORMAL = Config.GENERATION.getInt("generation.type.normal") / PERCENT_DIVISOR; + TYPE_SPECIAL = Config.GENERATION.getInt("generation.type.special") / PERCENT_DIVISOR; + TYPE_SCHEMATICS = Config.GENERATION.getInt("generation.type.schematic") / PERCENT_DIVISOR; - SPECIAL_ICE = Config.GENERATION.getInt("generation.special.ice") / 100.0; - SPECIAL_SLAB = Config.GENERATION.getInt("generation.special.slab") / 100.0; - SPECIAL_PANE = Config.GENERATION.getInt("generation.special.pane") / 100.0; - SPECIAL_FENCE = Config.GENERATION.getInt("generation.special.fence") / 100.0; + SPECIAL_ICE = Config.GENERATION.getInt("generation.special.ice") / PERCENT_DIVISOR; + SPECIAL_SLAB = Config.GENERATION.getInt("generation.special.slab") / PERCENT_DIVISOR; + SPECIAL_PANE = Config.GENERATION.getInt("generation.special.pane") / PERCENT_DIVISOR; + SPECIAL_FENCE = Config.GENERATION.getInt("generation.special.fence") / PERCENT_DIVISOR; - NORMAL_DISTANCE_1 = Config.GENERATION.getInt("generation.normal.distance.1") / 100.0; - NORMAL_DISTANCE_2 = Config.GENERATION.getInt("generation.normal.distance.2") / 100.0; - NORMAL_DISTANCE_3 = Config.GENERATION.getInt("generation.normal.distance.3") / 100.0; - NORMAL_DISTANCE_4 = Config.GENERATION.getInt("generation.normal.distance.4") / 100.0; + NORMAL_DISTANCE_1 = Config.GENERATION.getInt("generation.normal.distance.1") / PERCENT_DIVISOR; + NORMAL_DISTANCE_2 = Config.GENERATION.getInt("generation.normal.distance.2") / PERCENT_DIVISOR; + NORMAL_DISTANCE_3 = Config.GENERATION.getInt("generation.normal.distance.3") / PERCENT_DIVISOR; + NORMAL_DISTANCE_4 = Config.GENERATION.getInt("generation.normal.distance.4") / PERCENT_DIVISOR; - NORMAL_HEIGHT_1 = Config.GENERATION.getInt("generation.normal.height.1") / 100.0; - NORMAL_HEIGHT_0 = Config.GENERATION.getInt("generation.normal.height.0") / 100.0; - NORMAL_HEIGHT_NEG1 = Config.GENERATION.getInt("generation.normal.height.-1") / 100.0; - NORMAL_HEIGHT_NEG2 = Config.GENERATION.getInt("generation.normal.height.-2") / 100.0; + NORMAL_HEIGHT_1 = Config.GENERATION.getInt("generation.normal.height.1") / PERCENT_DIVISOR; + NORMAL_HEIGHT_0 = Config.GENERATION.getInt("generation.normal.height.0") / PERCENT_DIVISOR; + NORMAL_HEIGHT_NEG1 = Config.GENERATION.getInt("generation.normal.height.-1") / PERCENT_DIVISOR; + NORMAL_HEIGHT_NEG2 = Config.GENERATION.getInt("generation.normal.height.-2") / PERCENT_DIVISOR; MAX_Y = Config.GENERATION.getInt("generation.settings.max-y"); MIN_Y = Config.GENERATION.getInt("generation.settings.min-y"); if (MIN_Y >= MAX_Y) { - MIN_Y = 100; - MAX_Y = 200; + MIN_Y = DEFAULT_MIN_Y; + MAX_Y = DEFAULT_MAX_Y; LoParkour.getPlugin().getLogger().severe( "min-y >= max-y in generation.yml — using defaults 100/200."); } diff --git a/src/main/java/dev/loki/loparkour/config/options/section/OptionParticles.java b/src/main/java/dev/loki/loparkour/config/options/section/OptionParticles.java index 1741f67..397bd7a 100644 --- a/src/main/java/dev/loki/loparkour/config/options/section/OptionParticles.java +++ b/src/main/java/dev/loki/loparkour/config/options/section/OptionParticles.java @@ -12,6 +12,8 @@ import org.bukkit.Sound; */ public class OptionParticles { + private static final int PARTICLE_SIZE = 10; + private OptionParticles() { } @@ -29,7 +31,7 @@ public class OptionParticles { SOUND_PITCH = Config.CONFIG.getInt("particles.sound-pitch"); SOUND_VOLUME = Config.CONFIG.getInt("particles.sound-volume"); - PARTICLE_DATA = new ParticleData<>(PARTICLE_TYPE, null, 10); + PARTICLE_DATA = new ParticleData<>(PARTICLE_TYPE, null, PARTICLE_SIZE); } private static Sound resolveSound(String name) { @@ -39,7 +41,7 @@ public class OptionParticles { } catch (IllegalArgumentException ignored) { } } - for (String fb : new String[]{"BLOCK_NOTE_BLOCK_GUITAR", "BLOCK_NOTE_BLOCK_PLING", "BLOCK_NOTE_PLING"}) { + for (final String fb : new String[]{"BLOCK_NOTE_BLOCK_GUITAR", "BLOCK_NOTE_BLOCK_PLING", "BLOCK_NOTE_PLING"}) { try { return Sound.valueOf(fb); } catch (IllegalArgumentException ignored) { @@ -56,7 +58,7 @@ public class OptionParticles { } catch (IllegalArgumentException ignored) { } } - for (String fb : new String[]{"INSTANT_EFFECT", "SPELL_INSTANT", "CRIT"}) { + for (final String fb : new String[]{"INSTANT_EFFECT", "SPELL_INSTANT", "CRIT"}) { try { return Particle.valueOf(fb); } catch (IllegalArgumentException ignored) { diff --git a/src/main/java/dev/loki/loparkour/config/options/section/OptionSQL.java b/src/main/java/dev/loki/loparkour/config/options/section/OptionSQL.java index 4e55d3f..07f89df 100644 --- a/src/main/java/dev/loki/loparkour/config/options/section/OptionSQL.java +++ b/src/main/java/dev/loki/loparkour/config/options/section/OptionSQL.java @@ -26,7 +26,7 @@ public class OptionSQL { SQL_USERNAME = Config.CONFIG.getString("sql.username"); // Environment variable override for password - String envPassword = System.getenv("LOPARKOUR_SQL_PASSWORD"); + final String envPassword = System.getenv("LOPARKOUR_SQL_PASSWORD"); SQL_PASSWORD = (envPassword != null && !envPassword.isEmpty()) ? envPassword : Config.CONFIG.getString("sql.password"); diff --git a/src/main/java/dev/loki/loparkour/config/options/section/OptionStyles.java b/src/main/java/dev/loki/loparkour/config/options/section/OptionStyles.java index c2c494b..53a3ad3 100644 --- a/src/main/java/dev/loki/loparkour/config/options/section/OptionStyles.java +++ b/src/main/java/dev/loki/loparkour/config/options/section/OptionStyles.java @@ -22,14 +22,14 @@ public class OptionStyles { public static Set