From 6a9061450f3850500ac46569c7a04cd509b1f302 Mon Sep 17 00:00:00 2001 From: A5H73Y Date: Sun, 3 Sep 2023 19:54:35 +0100 Subject: [PATCH] Added Player stats (deaths & time) Added 5 new placeholders Bump dependencies Checkstyle improvements --- docs/files/parkourPlaceholders.json | 25 +++ pom.xml | 22 ++- .../io/github/a5h73y/parkour/Parkour.java | 2 +- .../a5h73y/parkour/ParkourPlaceholders.java | 178 +++++++++++++----- .../commands/ParkourConsoleCommands.java | 4 + .../parkour/configuration/ConfigManager.java | 31 +-- .../configuration/impl/DefaultConfig.java | 4 + .../parkourkit/ParkourKitConversation.java | 1 + .../parkour/database/DatabaseManager.java | 71 +++++++ .../listener/PlayerInteractListener.java | 19 +- .../listener/move/ParkourBlockListener.java | 3 +- .../a5h73y/parkour/plugin/EconomyApi.java | 8 +- .../a5h73y/parkour/plugin/ParkoinsVault.java | 9 +- .../parkour/plugin/PermissionVault.java | 7 +- .../a5h73y/parkour/plugin/PlaceholderApi.java | 14 ++ .../parkour/type/course/CourseConfig.java | 21 ++- .../parkour/type/course/CourseManager.java | 10 + .../type/course/CourseSettingsManager.java | 64 ++++--- .../parkour/type/player/PlayerConfig.java | 77 +++++--- .../parkour/type/player/PlayerManager.java | 28 +-- .../upgrade/major/CourseDataUpgradeTask.java | 3 +- .../upgrade/minor/PlayerMinorUpgradeTask.java | 16 +- .../upgrade/minor/TimedConfigUpgradeTask.java | 16 +- .../a5h73y/parkour/utility/PlayerUtils.java | 3 +- .../parkour/utility/cache/CacheValue.java | 9 + .../parkour/utility/cache/GenericCache.java | 16 +- src/main/resources/parkourCheckstyles.xml | 122 +++++++++--- src/main/resources/parkourPlaceholders.json | 25 +++ 28 files changed, 594 insertions(+), 214 deletions(-) create mode 100644 src/main/java/io/github/a5h73y/parkour/utility/cache/CacheValue.java diff --git a/docs/files/parkourPlaceholders.json b/docs/files/parkourPlaceholders.json index 25bba2ac..c6e90b2d 100644 --- a/docs/files/parkourPlaceholders.json +++ b/docs/files/parkourPlaceholders.json @@ -90,6 +90,31 @@ "placeholder": "%parkour_player_personal_best_(course)_deaths%", "output": "3", "description": "The number of deaths accumulated for the Player's personal best time on the specified Course." + }, + { + "placeholder": "%parkour_player_total_leaderboard_deaths%", + "output": "53", + "description": "The number of recorded deaths accumulated by the Player across all Courses in the Leaderboards." + }, + { + "placeholder": "%parkour_player_total_leaderboard_time%", + "output": "01:02:34", + "description": "The accumulated recorded time by the Player across all Courses in the Leaderboards." + }, + { + "placeholder": "%parkour_player_total_leaderboard_times%", + "output": "9", + "description": "The accumulated number of leaderboard times recorded by the Player across all Courses." + }, + { + "placeholder": "%parkour_player_total_playing_deaths%", + "output": "153", + "description": "The total number of recorded deaths accumulated by the Player across all Courses." + }, + { + "placeholder": "%parkour_player_total_playing_time%", + "output": "02:03:45", + "description": "The total recorded time by the Player across all Courses." } ] }, diff --git a/pom.xml b/pom.xml index c391ad1a..c2cfc433 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ com.github.cryptomorin XSeries - 9.4.0 + 9.5.0 compile @@ -158,7 +158,7 @@ org.yaml snakeyaml - 2.0 + 2.2 com.google.code.gson @@ -187,7 +187,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.11.0 ${java.version} ${java.version} @@ -197,7 +197,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.3.0 ${export_dir} @@ -206,7 +206,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.5.0 false true @@ -252,7 +252,7 @@ org.apache.maven.plugins maven-resources-plugin - 3.2.0 + 3.3.1 copy-my-resources @@ -280,14 +280,20 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.3.0 src/main/resources/parkourCheckstyles.xml - ${project.build.sourceEncoding} true true false + + + com.puppycrawl.tools + checkstyle + 10.9.1 + + validate diff --git a/src/main/java/io/github/a5h73y/parkour/Parkour.java b/src/main/java/io/github/a5h73y/parkour/Parkour.java index b4a62469..b97d201f 100644 --- a/src/main/java/io/github/a5h73y/parkour/Parkour.java +++ b/src/main/java/io/github/a5h73y/parkour/Parkour.java @@ -141,9 +141,9 @@ public void onDisable() { /** * Get the Default config. * Overrides the default getConfig() method. - * @deprecated use getDefaultConfig() * * @return default config + * @deprecated use getDefaultConfig() */ @Deprecated @Override diff --git a/src/main/java/io/github/a5h73y/parkour/ParkourPlaceholders.java b/src/main/java/io/github/a5h73y/parkour/ParkourPlaceholders.java index 3c87f8c2..342e71ab 100644 --- a/src/main/java/io/github/a5h73y/parkour/ParkourPlaceholders.java +++ b/src/main/java/io/github/a5h73y/parkour/ParkourPlaceholders.java @@ -36,6 +36,32 @@ public class ParkourPlaceholders extends PlaceholderExpansion { private static final String COURSE_INACTIVE = TranslationUtils.getTranslation("PlaceholderAPI.CourseInactive", false); private static final String NO_COOLDOWN_REMAINING = TranslationUtils.getTranslation("PlaceholderAPI.NoPrizeCooldown", false); + private static final String COMPLETED = "completed"; + private static final String POSITION = "position"; + private static final String PRIZE = "prize"; + private static final String DELAY = "delay"; + private static final String PERSONAL = "personal"; + private static final String BEST = "best"; + private static final String COURSE = "course"; + private static final String UNCOMPLETED = "uncompleted"; + private static final String COURSES = "courses"; + private static final String JOINED = "joined"; + private static final String LAST = "last"; + private static final String TOTAL = "total"; + private static final String LEADERBOARD = "leaderboard"; + private static final String DEATHS = "deaths"; + private static final String TIME = "time"; + private static final String TIMES = "times"; + private static final String PLAYING = "playing"; + private static final String CHECKPOINT = "checkpoint"; + private static final String MILLISECONDS = "milliseconds"; + private static final String PLAYER = "player"; + private static final String DELIMITER = "/"; + + private static final String PLACEHOLDER_API_CHECKPOINT_HOLOGRAM = "PlaceholderAPI.CheckpointHologram"; + private static final String PLACEHOLDER_API_CURRENT_COURSE_COMPLETED = "PlaceholderAPI.CurrentCourseCompleted"; + private static final String PLACEHOLDER_API_CURRENT_COURSE_NOT_COMPLETED = "PlaceholderAPI.CurrentCourseNotCompleted"; + private final Parkour parkour; private final GenericCache cache; @@ -78,6 +104,14 @@ public String onRequest(final OfflinePlayer offlinePlayer, return retrieveValue(offlinePlayer, placeholder.toLowerCase(), placeholder.toLowerCase().split("_")); } + public void clearCache() { + this.cache.clear(); + } + + public GenericCache getCache() { + return this.cache; + } + private String retrieveValue(OfflinePlayer offlinePlayer, @NotNull String placeholder, @NotNull String... arguments) { @@ -94,11 +128,11 @@ private String retrieveValue(OfflinePlayer offlinePlayer, return getGlobalPlaceholderValue(placeholder); case "pl": - case "player": + case PLAYER: return getPlayerPlaceholderValue(offlinePlayer, arguments); case "co": - case "course": + case COURSE: if (arguments.length < 3) { return INVALID_SYNTAX; } @@ -109,7 +143,7 @@ private String retrieveValue(OfflinePlayer offlinePlayer, return getCurrentPlaceholderValue(offlinePlayer, arguments); case "lb": - case "leaderboard": + case LEADERBOARD: if (arguments.length != 4 || !ValidationUtils.isPositiveInteger(arguments[2])) { return INVALID_SYNTAX; } @@ -160,67 +194,67 @@ private String getPlayerPlaceholderValue(OfflinePlayer offlinePlayer, String... case "parkoins": return String.valueOf(playerConfig.getParkoins()); - case "last": + case LAST: if (arguments.length < 3) { return INVALID_SYNTAX; } switch (arguments[2]) { - case "completed": + case COMPLETED: return playerConfig.getLastCompletedCourse(); - case "joined": + case JOINED: return playerConfig.getLastPlayedCourse(); default: return INVALID_SYNTAX; } - case "courses": + case COURSES: if (arguments.length < 3) { return INVALID_SYNTAX; } switch (arguments[2]) { - case "completed": + case COMPLETED: return String.valueOf(parkour.getConfigManager().getCourseCompletionsConfig() .getNumberOfCompletedCourses(offlinePlayer)); - case "uncompleted": + case UNCOMPLETED: return String.valueOf(parkour.getPlayerManager().getNumberOfUncompletedCourses(offlinePlayer)); default: return INVALID_SYNTAX; } - case "course": + case COURSE: if (arguments.length < 4) { return INVALID_SYNTAX; } switch (arguments[2]) { - case "completed": - return getOrRetrieveCache(offlinePlayer.getName() + arguments[2] + arguments[3], - () -> getCompletedMessage(offlinePlayer, arguments[3])); + case COMPLETED: + return getOrRetrieveCache(() -> getCompletedMessage(offlinePlayer, arguments[3]), + offlinePlayer.getName(), arguments[1], arguments[2], arguments[3]); - case "position": - return getOrRetrieveCache(offlinePlayer.getName() + arguments[2] + arguments[3], - () -> getLeaderboardPosition(offlinePlayer, arguments[3])); + case POSITION: + return getOrRetrieveCache(() -> getLeaderboardPosition(offlinePlayer, arguments[3]), + offlinePlayer.getName(),arguments[1], arguments[2], arguments[3]); default: return INVALID_SYNTAX; } - case "prize": + case PRIZE: if (arguments.length < 4) { return INVALID_SYNTAX; } - if ("delay".equals(arguments[2])) { - return getOrRetrieveCache(offlinePlayer.getName() + arguments[2] + arguments[3], - () -> getDelayCooldown(offlinePlayer, arguments[3])); + if (DELAY.equals(arguments[2])) { + return getOrRetrieveCache(() -> getDelayCooldown(offlinePlayer, arguments[3]), + offlinePlayer.getName(), arguments[1], arguments[2], arguments[3]); } return INVALID_SYNTAX; - case "personal": - if (arguments.length != 5 || !arguments[2].equals("best")) { + case PERSONAL: + if (arguments.length != 5 || !arguments[2].equals(BEST)) { return INVALID_SYNTAX; } Player player = offlinePlayer.getPlayer(); @@ -229,6 +263,45 @@ private String getPlayerPlaceholderValue(OfflinePlayer offlinePlayer, String... } return getPersonalCourseRecord(player, arguments[3], arguments[4]); + case TOTAL: + if (arguments.length < 4) { + return INVALID_SYNTAX; + } + switch (arguments[2]) { + case LEADERBOARD: + switch (arguments[3]) { + case DEATHS: + return getOrRetrieveCache(() -> String.valueOf(parkour.getDatabaseManager().getAccumulatedDeaths(offlinePlayer)), + offlinePlayer.getName(), arguments[1], arguments[2], arguments[3]); + + case TIME: + return getOrRetrieveCache(() -> DateTimeUtils.displayCurrentTime(parkour.getDatabaseManager().getAccumulatedTime(offlinePlayer)), + offlinePlayer.getName(), arguments[1], arguments[2], arguments[3]); + + case TIMES: + return getOrRetrieveCache(() -> String.valueOf(parkour.getDatabaseManager().getAccumulatedTimes(offlinePlayer)), + offlinePlayer.getName(), arguments[1], arguments[2], arguments[3]); + + default: + return INVALID_SYNTAX; + } + + case PLAYING: + switch (arguments[3]) { + case DEATHS: + return String.valueOf(playerConfig.getTotalDeaths()); + + case TIME: + return DateTimeUtils.displayCurrentTime(playerConfig.getTotalTime()); + + default: + return INVALID_SYNTAX; + } + + default: + return INVALID_SYNTAX; + } + default: return INVALID_SYNTAX; } @@ -323,13 +396,13 @@ private String getCurrentPlaceholderValue(OfflinePlayer offlinePlayer, String... } switch (arguments[1]) { - case "course": + case COURSE: if (arguments.length < 3) { return INVALID_SYNTAX; } return getCurrentCoursePlaceholderValue(player, session, arguments); - case "checkpoint": + case CHECKPOINT: if (arguments.length < 3) { // deprecated return String.valueOf(session.getCurrentCheckpoint()); } @@ -368,7 +441,7 @@ private String getCurrentCoursePlaceholderValue(Player player, ParkourSession se case "displayname": return session.getCourse().getDisplayName(); - case "deaths": + case DEATHS: return String.valueOf(session.getDeaths()); case "timer": @@ -377,9 +450,9 @@ private String getCurrentCoursePlaceholderValue(Player player, ParkourSession se case "checkpoints": return String.valueOf(session.getCourse().getNumberOfCheckpoints()); - case "completed": - return getOrRetrieveCache(player.getName() + arguments[2] + session.getCourseName(), - () -> getCompletedMessage(player, session.getCourseName())); + case COMPLETED: + return getOrRetrieveCache(() -> getCompletedMessage(player, session.getCourseName()), + player.getName(), arguments[1], arguments[2], session.getCourseName()); case "record": if (arguments.length != 4) { @@ -387,14 +460,14 @@ private String getCurrentCoursePlaceholderValue(Player player, ParkourSession se } return getCourseRecord(session.getCourseName(), arguments[3]); - case "personal": - if (arguments.length != 5 || !arguments[3].equals("best")) { + case PERSONAL: + if (arguments.length != 5 || !arguments[3].equals(BEST)) { return INVALID_SYNTAX; } return getPersonalCourseRecord(player, session.getCourseName(), arguments[4]); case "remaining": - if (arguments.length != 4 || !arguments[3].equals("deaths")) { + if (arguments.length != 4 || !arguments[3].equals(DEATHS)) { return INVALID_SYNTAX; } return String.valueOf(session.getRemainingDeaths()); @@ -430,22 +503,22 @@ private TimeEntry getTopPlayerResultForCourse(Player player, String courseName) private String getCompletedMessage(OfflinePlayer player, String courseName) { boolean resultFound = parkour.getConfigManager().getCourseCompletionsConfig().hasCompletedCourse(player, courseName); - String key = resultFound ? "PlaceholderAPI.CurrentCourseCompleted" : "PlaceholderAPI.CurrentCourseNotCompleted"; + String key = resultFound ? PLACEHOLDER_API_CURRENT_COURSE_COMPLETED : PLACEHOLDER_API_CURRENT_COURSE_NOT_COMPLETED; return TranslationUtils.getTranslation(key, false); } - private String getCourseRecord(String courseName, String key) { - return getCourseRecord(courseName, key, 1); + private String getCourseRecord(String courseName, String timeDetail) { + return getCourseRecord(courseName, timeDetail, 1); } - private String getCourseRecord(String courseName, String key, Integer position) { - return getOrRetrieveCache(courseName + key + position, - () -> extractResultDetails(parkour.getDatabaseManager().getNthBestTime(courseName, position), key)); + private String getCourseRecord(String courseName, String timeDetail, Integer position) { + return getOrRetrieveCache(() -> extractResultDetails(parkour.getDatabaseManager().getNthBestTime(courseName, position), timeDetail), + courseName, timeDetail, String.valueOf(position)); } - private String getPersonalCourseRecord(Player player, String courseName, String key) { - return getOrRetrieveCache(player.getName() + courseName + key, - () -> extractResultDetails(getTopPlayerResultForCourse(player, courseName), key)); + private String getPersonalCourseRecord(Player player, String courseName, String timeDetail) { + return getOrRetrieveCache(() -> extractResultDetails(getTopPlayerResultForCourse(player, courseName), timeDetail), + player.getName(), courseName, timeDetail); } private String getLeaderboardPosition(OfflinePlayer player, String courseName) { @@ -453,22 +526,22 @@ private String getLeaderboardPosition(OfflinePlayer player, String courseName) { return result < 0 ? NO_TIME_RECORDED : String.valueOf(result + 1); } - private String extractResultDetails(TimeEntry result, String key) { + private String extractResultDetails(TimeEntry result, String timeDetail) { if (result == null) { return NO_TIME_RECORDED; } else { - switch (key) { - case "time": + switch (timeDetail) { + case TIME: return getTimeValue(result.getTime()); - case "milliseconds": + case MILLISECONDS: return String.valueOf(result.getTime()); - case "deaths": + case DEATHS: return String.valueOf(result.getDeaths()); - case "player": + case PLAYER: return result.getPlayerName(); default: @@ -480,14 +553,15 @@ private String extractResultDetails(TimeEntry result, String key) { private String getCheckpointHologramMessage(ParkourSession session, String course, int checkpoint) { if (session.getCurrentCheckpoint() + 1 == checkpoint && session.getCourseName().equals(course.toLowerCase())) { - return TranslationUtils.getValueTranslation("PlaceholderAPI.CheckpointHologram", + return TranslationUtils.getValueTranslation(PLACEHOLDER_API_CHECKPOINT_HOLOGRAM, Integer.toString(checkpoint), false); } else { return ""; } } - private String getOrRetrieveCache(String key, Supplier callback) { + private String getOrRetrieveCache(Supplier callback, String... keys) { + String key = generateCacheKey(keys); // check if the key exists or its 'get' is about to expire. if (!cache.containsKey(key) || cache.get(key).isEmpty()) { cache.put(key, callback.get()); @@ -496,11 +570,11 @@ private String getOrRetrieveCache(String key, Supplier callback) { return cache.get(key).orElse(NO_TIME_RECORDED); } - public void clearCache() { - this.cache.clear(); - } - private String getTimeValue(long milliseconds) { return StringUtils.colour(parkour.getParkourConfig().getPlaceholderTimeFormat().format(new Date(milliseconds))); } + + private String generateCacheKey(String... values) { + return String.join(DELIMITER, values); + } } diff --git a/src/main/java/io/github/a5h73y/parkour/commands/ParkourConsoleCommands.java b/src/main/java/io/github/a5h73y/parkour/commands/ParkourConsoleCommands.java index a1d0193a..b40f9f24 100644 --- a/src/main/java/io/github/a5h73y/parkour/commands/ParkourConsoleCommands.java +++ b/src/main/java/io/github/a5h73y/parkour/commands/ParkourConsoleCommands.java @@ -225,6 +225,10 @@ public boolean onCommand(@NotNull CommandSender commandSender, parkour.getPlaceholderApi().evaluatePlaceholder(findPlayer(commandSender, args[2]), args[1]); break; + case "printplaceholdercache": + parkour.getPlaceholderApi().printCacheSummary(commandSender); + break; + case "prize": case "setprize": if (!ValidationUtils.validateArgs(commandSender, args, 2)) { diff --git a/src/main/java/io/github/a5h73y/parkour/configuration/ConfigManager.java b/src/main/java/io/github/a5h73y/parkour/configuration/ConfigManager.java index 0f4f1642..31824113 100644 --- a/src/main/java/io/github/a5h73y/parkour/configuration/ConfigManager.java +++ b/src/main/java/io/github/a5h73y/parkour/configuration/ConfigManager.java @@ -1,5 +1,21 @@ package io.github.a5h73y.parkour.configuration; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import de.leonhard.storage.internal.FlatFile; +import de.leonhard.storage.internal.serialize.SimplixSerializer; import io.github.a5h73y.parkour.configuration.impl.DefaultConfig; import io.github.a5h73y.parkour.configuration.impl.StringsConfig; import io.github.a5h73y.parkour.configuration.serializable.CourseSerializable; @@ -16,21 +32,6 @@ import io.github.a5h73y.parkour.type.player.quiet.QuietModeConfig; import io.github.a5h73y.parkour.type.player.rank.ParkourRankConfig; import io.github.a5h73y.parkour.utility.PluginUtils; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import de.leonhard.storage.internal.FlatFile; -import de.leonhard.storage.internal.serialize.SimplixSerializer; import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/io/github/a5h73y/parkour/configuration/impl/DefaultConfig.java b/src/main/java/io/github/a5h73y/parkour/configuration/impl/DefaultConfig.java index c5946648..b0a75253 100644 --- a/src/main/java/io/github/a5h73y/parkour/configuration/impl/DefaultConfig.java +++ b/src/main/java/io/github/a5h73y/parkour/configuration/impl/DefaultConfig.java @@ -378,6 +378,10 @@ public List getDefaultJoinItems() { return this.getSerializableList(COURSE_DEFAULT_SETTINGS + JOIN_ITEMS, ItemStack.class); } + /** + * Add Material to default join items. + * @param itemStack item stack to add + */ public void addDefaultJoinItem(ItemStack itemStack) { List results = this.getStringList(COURSE_DEFAULT_SETTINGS + JOIN_ITEMS); results.add(Parkour.getInstance().getConfigManager().getItemStackSerializable().serialize(itemStack)); diff --git a/src/main/java/io/github/a5h73y/parkour/conversation/parkourkit/ParkourKitConversation.java b/src/main/java/io/github/a5h73y/parkour/conversation/parkourkit/ParkourKitConversation.java index f6e99673..f1d497ca 100644 --- a/src/main/java/io/github/a5h73y/parkour/conversation/parkourkit/ParkourKitConversation.java +++ b/src/main/java/io/github/a5h73y/parkour/conversation/parkourkit/ParkourKitConversation.java @@ -77,6 +77,7 @@ public String getPromptText(@NotNull ConversationContext context) { /** * Provide kit name to bypass name input prompts. + * * @param kitName kit name * @return conversation instance */ diff --git a/src/main/java/io/github/a5h73y/parkour/database/DatabaseManager.java b/src/main/java/io/github/a5h73y/parkour/database/DatabaseManager.java index e60f584f..ae3255eb 100644 --- a/src/main/java/io/github/a5h73y/parkour/database/DatabaseManager.java +++ b/src/main/java/io/github/a5h73y/parkour/database/DatabaseManager.java @@ -331,6 +331,77 @@ public boolean hasPlayerAchievedTime(OfflinePlayer player, String courseName) { return result; } + /** + * Get the sum of Player's accumulated deaths. + * @param player target offline player + * @return total deaths + */ + public int getAccumulatedDeaths(@NotNull OfflinePlayer player) { + int result = 0; + try (PreparedStatement statement = getDatabaseConnection().prepareStatement( + "SELECT SUM(deaths) AS total FROM time WHERE playerId = ?")) { + statement.setString(1, getPlayerId(player)); + ResultSet resultSet = statement.executeQuery(); + + if (resultSet.next()) { + result = resultSet.getInt("total"); + } + resultSet.getStatement().close(); + } catch (SQLException e) { + logSqlException(e); + } + + return result; + } + + /** + * Get the sum of Player's accumulated time in milliseconds. + * @param player target offline player + * @return total time in ms + */ + public long getAccumulatedTime(@NotNull OfflinePlayer player) { + long result = 0; + try (PreparedStatement statement = getDatabaseConnection().prepareStatement( + "SELECT SUM(time) AS total FROM time WHERE playerId = ?")) { + statement.setString(1, getPlayerId(player)); + ResultSet resultSet = statement.executeQuery(); + + if (resultSet.next()) { + result = resultSet.getLong("total"); + } + resultSet.getStatement().close(); + } catch (SQLException e) { + logSqlException(e); + } + + return result; + } + + /** + * Get the sum of Player's recorded times. + * @param player target offline player + * @return total times + */ + public int getAccumulatedTimes(@NotNull OfflinePlayer player) { + int result = 0; + try (PreparedStatement statement = getDatabaseConnection().prepareStatement( + "SELECT COUNT(*) AS total FROM time WHERE playerId = ?")) { + statement.setString(1, getPlayerId(player)); + ResultSet resultSet = statement.executeQuery(); + + if (resultSet.next()) { + result = resultSet.getInt("total"); + } + resultSet.getStatement().close(); + } catch (SQLException e) { + logSqlException(e); + } + + return result; + } + + + /** * Determine if this is the best time on the course. * diff --git a/src/main/java/io/github/a5h73y/parkour/listener/PlayerInteractListener.java b/src/main/java/io/github/a5h73y/parkour/listener/PlayerInteractListener.java index eb914751..e409e2d2 100644 --- a/src/main/java/io/github/a5h73y/parkour/listener/PlayerInteractListener.java +++ b/src/main/java/io/github/a5h73y/parkour/listener/PlayerInteractListener.java @@ -1,7 +1,5 @@ package io.github.a5h73y.parkour.listener; -import static io.github.a5h73y.parkour.type.player.PlayerConfig.SESSION; - import com.cryptomorin.xseries.XBlock; import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.configuration.impl.DefaultConfig; @@ -37,6 +35,11 @@ public class PlayerInteractListener extends AbstractPluginReceiver implements Li private final EnumMap parkourTools = new EnumMap<>(Material.class); + /** + * Initialise the PlayerInteractListener. + * Register each ParkourTool and the associated action. + * @param parkour parkour instance + */ public PlayerInteractListener(final Parkour parkour) { super(parkour); DefaultConfig config = parkour.getParkourConfig(); @@ -59,7 +62,7 @@ public PlayerInteractListener(final Parkour parkour) { (player, rightClick) -> handleRocketTool(player)); registerParkourTool(config.getFreedomTool(), "Freedom", false, false, ParkourMode.FREEDOM, - (player, rightClick) -> handleFreedomTool(player, rightClick)); + this::handleFreedomTool); } @EventHandler @@ -67,7 +70,7 @@ public void onProjectileThrownEvent(ProjectileLaunchEvent event) { if (!(event.getEntity().getShooter() instanceof Player)) { return; } - + Player player = (Player) event.getEntity().getShooter(); if (!parkour.getParkourSessionManager().isPlaying(player)) { @@ -131,8 +134,8 @@ public void onParkourToolInteract(PlayerInteractEvent event) { if (!parkour.getParkourConfig().getBoolean("ParkourTool.RemoveRightClickRestriction") && (toolAction.isRightClickOnly() - && !event.getAction().equals(Action.RIGHT_CLICK_BLOCK) - && !event.getAction().equals(Action.RIGHT_CLICK_AIR))) { + && !event.getAction().equals(Action.RIGHT_CLICK_BLOCK) + && !event.getAction().equals(Action.RIGHT_CLICK_AIR))) { return; } @@ -248,14 +251,14 @@ private void handleRocketTool(Player player) { if (maximumRockets != null) { PlayerConfig config = parkour.getConfigManager().getPlayerConfig(player); - int rocketsUsed = config.get(SESSION + "RocketsUsed", 0); + int rocketsUsed = config.getRocketsUsedInSession(); if (rocketsUsed >= maximumRockets) { TranslationUtils.sendMessage(player, "You have run out of Rockets!"); return; } - config.set(SESSION + "RocketsUsed", rocketsUsed + 1); + config.increaseRocketsUsedInSession(); } parkour.getPlayerManager().rocketLaunchPlayer(player); diff --git a/src/main/java/io/github/a5h73y/parkour/listener/move/ParkourBlockListener.java b/src/main/java/io/github/a5h73y/parkour/listener/move/ParkourBlockListener.java index 6919799f..ecb9e219 100644 --- a/src/main/java/io/github/a5h73y/parkour/listener/move/ParkourBlockListener.java +++ b/src/main/java/io/github/a5h73y/parkour/listener/move/ParkourBlockListener.java @@ -170,7 +170,8 @@ private Material calculateClosestBlock(Player player) { BlockFace result = BLOCK_FACES.stream() .filter(blockFace -> !XBlock.isAir(blockBelow.getRelative(blockFace).getType())) .min(Comparator.comparing(blockFace -> - blockBelow.getRelative(blockFace).getLocation().add(0.5, 0.5, 0.5).distance(player.getLocation()))) + blockBelow.getRelative(blockFace).getLocation() + .add(0.5, 0.5, 0.5).distance(player.getLocation()))) .orElse(BlockFace.NORTH); return blockBelow.getRelative(result).getType(); diff --git a/src/main/java/io/github/a5h73y/parkour/plugin/EconomyApi.java b/src/main/java/io/github/a5h73y/parkour/plugin/EconomyApi.java index 279b7327..10736111 100644 --- a/src/main/java/io/github/a5h73y/parkour/plugin/EconomyApi.java +++ b/src/main/java/io/github/a5h73y/parkour/plugin/EconomyApi.java @@ -48,7 +48,8 @@ protected void initialise() { if (isEnabled()) { if (parkour.getParkourConfig().getBoolean("Plugin.Vault.RegisterParkoins")) { - getServer().getServicesManager().register(Economy.class, new ParkoinsVault(parkour), parkour, ServicePriority.Normal); + getServer().getServicesManager().register( + Economy.class, new ParkoinsVault(parkour), parkour, ServicePriority.Normal); } RegisteredServiceProvider economyProvider = @@ -135,7 +136,7 @@ public void giveEconomyPrize(Player player, String courseName) { /** * Get formatted value of amount. - * @param amount + * @param amount amount to format * @return formatted amount */ public String getAmount(double amount) { @@ -355,7 +356,8 @@ private void processAmountCommand(CommandSender commandSender, String... args) { return; } - TranslationUtils.sendMessage(commandSender, args[2] + "'s balance: &b" + getAmount(economy.getBalance(targetPlayer))); + TranslationUtils.sendMessage(commandSender, args[2] + "'s balance: &b" + + getAmount(economy.getBalance(targetPlayer))); } @Override diff --git a/src/main/java/io/github/a5h73y/parkour/plugin/ParkoinsVault.java b/src/main/java/io/github/a5h73y/parkour/plugin/ParkoinsVault.java index 1984eede..2086a478 100644 --- a/src/main/java/io/github/a5h73y/parkour/plugin/ParkoinsVault.java +++ b/src/main/java/io/github/a5h73y/parkour/plugin/ParkoinsVault.java @@ -86,12 +86,15 @@ public boolean has(String playerName, String worldName, double amount) { @Override public EconomyResponse withdrawPlayer(String playerName, double amount) { if (!hasAccount(playerName)) { - return new EconomyResponse(amount, 0, EconomyResponse.ResponseType.FAILURE, "User doesn't have an account."); + return new EconomyResponse(amount, 0, + EconomyResponse.ResponseType.FAILURE, "User doesn't have an account."); } else if (!has(playerName, amount)) { - return new EconomyResponse(amount, getBalance(playerName), EconomyResponse.ResponseType.FAILURE, "User doesn't have enough Parkoins."); + return new EconomyResponse(amount, getBalance(playerName), + EconomyResponse.ResponseType.FAILURE, "User doesn't have enough Parkoins."); } else { getPlayerConfig(playerName).decreaseParkoins(amount); - return new EconomyResponse(amount, getBalance(playerName), EconomyResponse.ResponseType.SUCCESS, ""); + return new EconomyResponse(amount, getBalance(playerName), + EconomyResponse.ResponseType.SUCCESS, ""); } } diff --git a/src/main/java/io/github/a5h73y/parkour/plugin/PermissionVault.java b/src/main/java/io/github/a5h73y/parkour/plugin/PermissionVault.java index 1d550e32..cd9cbe89 100644 --- a/src/main/java/io/github/a5h73y/parkour/plugin/PermissionVault.java +++ b/src/main/java/io/github/a5h73y/parkour/plugin/PermissionVault.java @@ -2,11 +2,11 @@ import static org.bukkit.Bukkit.getServer; -import org.bukkit.entity.Player; -import org.bukkit.plugin.RegisteredServiceProvider; import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.utility.PluginUtils; import net.milkbowl.vault.permission.Permission; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; public class PermissionVault extends PluginWrapper { @@ -22,7 +22,8 @@ protected void initialise() { super.initialise(); if (isEnabled()) { - RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Permission.class); + RegisteredServiceProvider rsp = + getServer().getServicesManager().getRegistration(Permission.class); if (rsp == null) { PluginUtils.log("[Permission] Failed to connect to Vault's Permission service.", 2); diff --git a/src/main/java/io/github/a5h73y/parkour/plugin/PlaceholderApi.java b/src/main/java/io/github/a5h73y/parkour/plugin/PlaceholderApi.java index ae68b010..fd2e406b 100644 --- a/src/main/java/io/github/a5h73y/parkour/plugin/PlaceholderApi.java +++ b/src/main/java/io/github/a5h73y/parkour/plugin/PlaceholderApi.java @@ -3,6 +3,8 @@ import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.ParkourPlaceholders; import io.github.a5h73y.parkour.utility.TranslationUtils; +import io.github.a5h73y.parkour.utility.cache.CacheValue; +import java.util.concurrent.ConcurrentMap; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; @@ -95,4 +97,16 @@ public void clearCache() { this.getPlaceholders().clearCache(); } } + + public void printCacheSummary(CommandSender commandSender) { + if (isEnabled()) { + getPlaceholders().getCache().clean(); + ConcurrentMap> cacheMap = getPlaceholders().getCache().getCacheMap(); + TranslationUtils.sendHeading(cacheMap.size() + " placeholders:", commandSender); + cacheMap.forEach((key, value) -> + commandSender.sendMessage(key + ": " + value.getValue())); + } else { + TranslationUtils.sendMessage(commandSender, "Placeholders are disabled."); + } + } } diff --git a/src/main/java/io/github/a5h73y/parkour/type/course/CourseConfig.java b/src/main/java/io/github/a5h73y/parkour/type/course/CourseConfig.java index 3e814156..e2e356d1 100644 --- a/src/main/java/io/github/a5h73y/parkour/type/course/CourseConfig.java +++ b/src/main/java/io/github/a5h73y/parkour/type/course/CourseConfig.java @@ -839,10 +839,10 @@ public boolean isOneTimeFee() { /** * Set Economy one-time fee. - * @param is one-time joining fee + * @param oneTimeFee is one-time joining fee */ - public void setEconomyOneTimeFee(Boolean OneTimeFee) { - this.set(ECONOMY_ONETIME_FEE, OneTimeFee); + public void setEconomyOneTimeFee(Boolean oneTimeFee) { + this.set(ECONOMY_ONETIME_FEE, oneTimeFee); } /** @@ -1131,10 +1131,17 @@ public static void displayCourseInfo(@NotNull CommandSender commandSender, } } - public T getCourseSettingOrDefault(String key, T def) { - Object raw = this.get(key); + /** + * Get the Course's setting for the given key, or use default value. + * @param configKey key requested + * @param defaultValue default value + * @return value + * @param type of object expected + */ + public T getCourseSettingOrDefault(String configKey, T defaultValue) { + Object raw = this.get(configKey); return raw == null - ? Parkour.getDefaultConfig().get("CourseDefault.Settings." + key, def) - : ClassWrapper.getFromDef(raw, def); + ? Parkour.getDefaultConfig().get("CourseDefault.Settings." + configKey, defaultValue) + : ClassWrapper.getFromDef(raw, defaultValue); } } diff --git a/src/main/java/io/github/a5h73y/parkour/type/course/CourseManager.java b/src/main/java/io/github/a5h73y/parkour/type/course/CourseManager.java index 9073c453..7b972130 100644 --- a/src/main/java/io/github/a5h73y/parkour/type/course/CourseManager.java +++ b/src/main/java/io/github/a5h73y/parkour/type/course/CourseManager.java @@ -364,6 +364,16 @@ public void runEventCommands(final Player player, runEventCommands(player, session, eventType.getConfigEntry()); } + /** + * Execute the appropriate Event Commands for Course. + * When a Course has a matching Command for the {@link ParkourEventType}, execute each of them. + * The Commands will be dispatched either from the Console Sender or Player, based on prefix. + * The command will allow for a various internal placeholders to be replaced by ParkourSession values. + * + * @param player requesting player + * @param session parkour session + * @param eventTypeKey event type key + */ public void runEventCommands(final Player player, final ParkourSession session, final String eventTypeKey) { diff --git a/src/main/java/io/github/a5h73y/parkour/type/course/CourseSettingsManager.java b/src/main/java/io/github/a5h73y/parkour/type/course/CourseSettingsManager.java index d737e680..16331a93 100644 --- a/src/main/java/io/github/a5h73y/parkour/type/course/CourseSettingsManager.java +++ b/src/main/java/io/github/a5h73y/parkour/type/course/CourseSettingsManager.java @@ -26,7 +26,13 @@ import static io.github.a5h73y.parkour.type.course.CourseConfig.REWARD_ONCE; import static io.github.a5h73y.parkour.type.course.CourseConfig.REWARD_PARKOINS; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import com.google.common.io.Files; +import de.leonhard.storage.sections.FlatFileSection; import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.commands.CommandProcessor; import io.github.a5h73y.parkour.conversation.CoursePrizeConversation; @@ -43,11 +49,6 @@ import io.github.a5h73y.parkour.utility.permission.Permission; import io.github.a5h73y.parkour.utility.permission.PermissionUtils; import io.github.a5h73y.parkour.utility.time.DateTimeUtils; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import de.leonhard.storage.sections.FlatFileSection; import org.bukkit.command.CommandSender; import org.bukkit.conversations.Conversable; import org.bukkit.entity.Player; @@ -199,8 +200,8 @@ private void notifyActionChange(CommandSender commandSender, String property, St * @param courseName course name */ public void setChallengeOnlyStatus(@NotNull CommandSender commandSender, - @Nullable String courseName, - @Nullable Boolean value) { + @Nullable String courseName, + @Nullable Boolean value) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -245,8 +246,8 @@ public void setCreator(final CommandSender commandSender, final String courseNam * @param value flag value */ public void setDieInLiquid(@NotNull final CommandSender commandSender, - @Nullable final String courseName, - @Nullable Boolean value) { + @Nullable final String courseName, + @Nullable Boolean value) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -270,8 +271,8 @@ public void setDieInLiquid(@NotNull final CommandSender commandSender, * @param value flag value */ public void setDieInVoid(@NotNull final CommandSender commandSender, - @Nullable final String courseName, - @Nullable Boolean value) { + @Nullable final String courseName, + @Nullable Boolean value) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -332,7 +333,8 @@ public void setCourseToCourseLink(final CommandSender commandSender, final Strin CourseConfig courseConfig = parkour.getConfigManager().getCourseConfig(courseName); if (courseConfig.hasLinkedLobby()) { TranslationUtils.sendMessage(commandSender, "A linked lobby exists on this Course."); - TranslationUtils.sendMessage(commandSender, "The Player will be taken to the linked Course instead of the Lobby on finish."); + TranslationUtils.sendMessage(commandSender, + "The Player will be taken to the linked Course instead of the Lobby on finish."); } courseConfig.setLinkedCourse(targetCourse); @@ -362,7 +364,8 @@ public void setCourseToLobbyLink(final CommandSender commandSender, final String if (courseConfig.hasLinkedCourse()) { TranslationUtils.sendMessage(commandSender, "A linked Course exists on this Course."); - TranslationUtils.sendMessage(commandSender, "The Player will be taken to the linked Course instead of the Lobby on finish."); + TranslationUtils.sendMessage(commandSender, + "The Player will be taken to the linked Course instead of the Lobby on finish."); } courseConfig.setLinkedLobby(targetLobby); @@ -574,10 +577,10 @@ public void setParkourMode(final CommandSender commandSender, final String cours * @param joinMessage join message */ public void setPotionParkourMode(@NotNull final CommandSender commandSender, - @NotNull final String courseName, - @NotNull String potionEffectType, - @Nullable String durationAmplifier, - @Nullable String joinMessage) { + @NotNull final String courseName, + @NotNull String potionEffectType, + @Nullable String durationAmplifier, + @Nullable String joinMessage) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -623,8 +626,8 @@ public void setPlayerLimit(final CommandSender commandSender, final String cours * @param value value to set */ public void setReadyStatus(@NotNull final CommandSender commandSender, - @Nullable final String courseName, - @Nullable Boolean value) { + @Nullable final String courseName, + @Nullable Boolean value) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -777,7 +780,7 @@ public void setRewardParkourLevel(final CommandSender commandSender, final Strin * @param parkourLevelIncrease parkour level increase */ public void setRewardParkourLevelIncrease(final CommandSender commandSender, final String courseName, - final String parkourLevelIncrease) { + final String parkourLevelIncrease) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -800,8 +803,8 @@ public void setRewardParkourLevelIncrease(final CommandSender commandSender, fin * @param courseName course name */ public void setRewardOnceStatus(@NotNull final CommandSender commandSender, - @Nullable final String courseName, - @Nullable Boolean value) { + @Nullable final String courseName, + @Nullable Boolean value) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -868,9 +871,9 @@ public void setStartLocation(final CommandSender commandSender, final String cou * @param message message */ public void setCourseEventMessage(@NotNull CommandSender commandSender, - @Nullable String courseName, - @Nullable String eventTypeName, - @Nullable String message) { + @Nullable String courseName, + @Nullable String eventTypeName, + @Nullable String message) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -898,9 +901,9 @@ public void setCourseEventMessage(@NotNull CommandSender commandSender, * @param command message */ public void addCourseEventCommand(@NotNull CommandSender commandSender, - @Nullable String courseName, - @Nullable String eventTypeName, - @Nullable String command) { + @Nullable String courseName, + @Nullable String eventTypeName, + @Nullable String command) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; @@ -959,7 +962,8 @@ public void addJoinItem(final CommandSender commandSender, final String... args) String label = args.length >= 5 ? args[4] : StringUtils.standardizeText(data.getMaterial().name()); boolean unbreakable = args.length == 6 && Boolean.parseBoolean(args[5]); - itemStack = MaterialUtils.createItemStack(data.getMaterial(), amount, label, unbreakable, data.getCustomModelData()); + itemStack = MaterialUtils.createItemStack( + data.getMaterial(), amount, label, unbreakable, data.getCustomModelData()); } String name; @@ -983,7 +987,7 @@ public void addJoinItem(final CommandSender commandSender, final String... args) * @param courseName course name */ public void startCoursePrizeConversation(@NotNull CommandSender commandSender, - @Nullable String courseName) { + @Nullable String courseName) { if (!doesCourseExist(courseName)) { TranslationUtils.sendValueTranslation(ERROR_NO_EXIST, courseName, commandSender); return; diff --git a/src/main/java/io/github/a5h73y/parkour/type/player/PlayerConfig.java b/src/main/java/io/github/a5h73y/parkour/type/player/PlayerConfig.java index 36367d46..5bcfefad 100644 --- a/src/main/java/io/github/a5h73y/parkour/type/player/PlayerConfig.java +++ b/src/main/java/io/github/a5h73y/parkour/type/player/PlayerConfig.java @@ -1,10 +1,12 @@ package io.github.a5h73y.parkour.type.player; -import io.github.a5h73y.parkour.Parkour; -import io.github.a5h73y.parkour.utility.TranslationUtils; +import io.github.a5h73y.parkour.type.player.session.ParkourSession; import java.io.File; + import de.leonhard.storage.Json; import de.leonhard.storage.internal.FileType; +import io.github.a5h73y.parkour.Parkour; +import io.github.a5h73y.parkour.utility.TranslationUtils; import org.bukkit.Location; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; @@ -33,8 +35,13 @@ public class PlayerConfig extends Json { public static final String GAMEMODE = "GameMode"; public static final String PARKOINS = "Parkoins"; public static final String EXISTING_SESSION_COURSE_NAME = "ExistingSessionCourseName"; - public static final String SNAPSHOT = "Snapshot."; - public static final String SESSION = "Session."; + public static final String ROCKETS_USED = "RocketsUsed"; + public static final String TOTAL_DEATHS = "TotalDeaths"; + public static final String TOTAL_TIME = "TotalTime"; + + public static final String SNAPSHOT_PREFIX = "Snapshot."; + public static final String SESSION_PREFIX = "Session."; + public static final String STATS_PREFIX = "Stats."; public PlayerConfig(File playerFile) { @@ -185,12 +192,12 @@ public void setLastRewardedTime(String courseName, long rewardTime) { */ public void setPlayerDataSnapshot(Player player) { if (!hasPlayerDataSnapshot()) { - this.setSerializable(SNAPSHOT + INVENTORY, player.getInventory().getContents()); - this.setSerializable(SNAPSHOT + ARMOR, player.getInventory().getArmorContents()); - this.set(SNAPSHOT + HEALTH, player.getHealth()); - this.set(SNAPSHOT + HUNGER, player.getFoodLevel()); - this.set(SNAPSHOT + XP_LEVEL, player.getLevel()); - this.set(SNAPSHOT + GAMEMODE, player.getGameMode().name()); + this.setSerializable(SNAPSHOT_PREFIX + INVENTORY, player.getInventory().getContents()); + this.setSerializable(SNAPSHOT_PREFIX + ARMOR, player.getInventory().getArmorContents()); + this.set(SNAPSHOT_PREFIX + HEALTH, player.getHealth()); + this.set(SNAPSHOT_PREFIX + HUNGER, player.getFoodLevel()); + this.set(SNAPSHOT_PREFIX + XP_LEVEL, player.getLevel()); + this.set(SNAPSHOT_PREFIX + GAMEMODE, player.getGameMode().name()); if (!hasSnapshotJoinLocation()) { setPlayerJoinLocation(player); @@ -198,25 +205,30 @@ public void setPlayerDataSnapshot(Player player) { } } + /** + * Set the Snapshot data for the Player. + * This must not be overridden as the Player can join multiple courses without this data being touched. + * @param player player + */ public void setSnapshotStartParkourInventory(Player player) { - if (!this.contains(SNAPSHOT + START_PARKOUR_INVENTORY)) { - this.setSerializable(SNAPSHOT + START_PARKOUR_INVENTORY, player.getInventory().getContents()); + if (!this.contains(SNAPSHOT_PREFIX + START_PARKOUR_INVENTORY)) { + this.setSerializable(SNAPSHOT_PREFIX + START_PARKOUR_INVENTORY, player.getInventory().getContents()); } } public ItemStack[] getSnapshotStartParkourInventory() { - return this.getSerializable(SNAPSHOT + START_PARKOUR_INVENTORY, ItemStack[].class); + return this.getSerializable(SNAPSHOT_PREFIX + START_PARKOUR_INVENTORY, ItemStack[].class); } public boolean hasPlayerDataSnapshot() { - return this.contains(SNAPSHOT); + return this.contains(SNAPSHOT_PREFIX); } /** * Reset the Player's Snapshot data. */ public void resetPlayerDataSnapshot() { - this.remove(SNAPSHOT); + this.remove(SNAPSHOT_PREFIX); } /** @@ -224,7 +236,7 @@ public void resetPlayerDataSnapshot() { * @return ItemStacks representing inventory */ public ItemStack[] getSnapshotInventory() { - return this.getSerializable(SNAPSHOT + INVENTORY, ItemStack[].class); + return this.getSerializable(SNAPSHOT_PREFIX + INVENTORY, ItemStack[].class); } /** @@ -232,7 +244,7 @@ public ItemStack[] getSnapshotInventory() { * @return ItemStacks representing armor */ public ItemStack[] getSnapshotArmor() { - return this.getSerializable(SNAPSHOT + ARMOR, ItemStack[].class); + return this.getSerializable(SNAPSHOT_PREFIX + ARMOR, ItemStack[].class); } /** @@ -277,7 +289,7 @@ public void resetSessionJoinLocation() { * @return stored health */ public double getSnapshotHealth() { - return this.getDouble(SNAPSHOT + HEALTH); + return this.getDouble(SNAPSHOT_PREFIX + HEALTH); } /** @@ -285,7 +297,7 @@ public double getSnapshotHealth() { * @return stored hunger */ public int getSnapshotHunger() { - return this.getInt(SNAPSHOT + HUNGER); + return this.getInt(SNAPSHOT_PREFIX + HUNGER); } /** @@ -293,7 +305,7 @@ public int getSnapshotHunger() { * @return stored xp level */ public int getSnapshotXpLevel() { - return this.getInt(SNAPSHOT + XP_LEVEL); + return this.getInt(SNAPSHOT_PREFIX + XP_LEVEL); } /** @@ -301,7 +313,7 @@ public int getSnapshotXpLevel() { * @return saved gamemode name */ public String getSnapshotGameMode() { - return this.getString(SNAPSHOT + GAMEMODE).toUpperCase(); + return this.getString(SNAPSHOT_PREFIX + GAMEMODE).toUpperCase(); } /** @@ -370,7 +382,28 @@ public void removeExistingSessionCourseName() { * Session Data is any random data to associate with the Player's current Parkour session. */ public void resetSessionData() { - this.remove(SESSION); + this.remove(SESSION_PREFIX); + } + + public int getRocketsUsedInSession() { + return this.getInt(SESSION_PREFIX + ROCKETS_USED); + } + + public void increaseRocketsUsedInSession() { + this.set(SESSION_PREFIX + ROCKETS_USED, this.getInt(SESSION_PREFIX + ROCKETS_USED) + 1); + } + + public void recordStatistics(ParkourSession session) { + this.set(STATS_PREFIX + TOTAL_DEATHS, this.getTotalDeaths() + session.getDeaths()); + this.set(STATS_PREFIX + TOTAL_TIME, this.getTotalTime() + session.getAccumulatedTime()); + } + + public int getTotalDeaths() { + return this.getInt(STATS_PREFIX + TOTAL_DEATHS); + } + + public long getTotalTime() { + return this.getLong(STATS_PREFIX + TOTAL_TIME); } private static String getPlayerJsonPath(OfflinePlayer player) { diff --git a/src/main/java/io/github/a5h73y/parkour/type/player/PlayerManager.java b/src/main/java/io/github/a5h73y/parkour/type/player/PlayerManager.java index 97ffdd7f..f716aaee 100644 --- a/src/main/java/io/github/a5h73y/parkour/type/player/PlayerManager.java +++ b/src/main/java/io/github/a5h73y/parkour/type/player/PlayerManager.java @@ -21,17 +21,6 @@ import static io.github.a5h73y.parkour.utility.TranslationUtils.sendConditionalValue; import static io.github.a5h73y.parkour.utility.TranslationUtils.sendValue; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - import com.cryptomorin.xseries.XPotion; import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.commands.CommandProcessor; @@ -68,6 +57,16 @@ import io.github.a5h73y.parkour.utility.permission.Permission; import io.github.a5h73y.parkour.utility.permission.PermissionUtils; import io.github.a5h73y.parkour.utility.time.DateTimeUtils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Effect; @@ -285,6 +284,7 @@ public void leaveCourse(Player player, boolean silent) { } playerConfig.resetSessionData(); playerConfig.removeExistingSessionCourseName(); + playerConfig.recordStatistics(session); parkour.getParkourSessionManager().forceVisible(player); parkour.getScoreboardManager().removeScoreboard(player); @@ -529,6 +529,7 @@ public void finishCourse(final Player player) { parkour.getSoundsManager().playSound(player, SoundType.COURSE_FINISHED); PlayerConfig playerConfig = parkour.getConfigManager().getPlayerConfig(player); CourseConfig courseConfig = parkour.getConfigManager().getCourseConfig(courseName); + playerConfig.recordStatistics(session); Bukkit.getScheduler().scheduleSyncDelayedTask(parkour, () -> { parkour.getScoreboardManager().removeScoreboard(player); @@ -608,6 +609,11 @@ private void fireRestartEvent(Player player, ParkourSession session) { Bukkit.getServer().getPluginManager().callEvent(new ParkourRestartEvent(player, session.getCourseName())); } + /** + * Trigger a fast restart of the Course. + * The minimum amount of action required to safely restart the Player on the Course. + * @param player player + */ public void fastRestartCourse(Player player) { ParkourSession session = parkour.getParkourSessionManager().getParkourSession(player); diff --git a/src/main/java/io/github/a5h73y/parkour/upgrade/major/CourseDataUpgradeTask.java b/src/main/java/io/github/a5h73y/parkour/upgrade/major/CourseDataUpgradeTask.java index 89b51d66..6dfdae4e 100644 --- a/src/main/java/io/github/a5h73y/parkour/upgrade/major/CourseDataUpgradeTask.java +++ b/src/main/java/io/github/a5h73y/parkour/upgrade/major/CourseDataUpgradeTask.java @@ -94,7 +94,8 @@ private void updateJoinItems(CourseConfig newCourseConfig) { List results = itemStacks.stream() .map(itemStack -> - getParkourUpgrader().getNewConfigManager().getItemStackSerializable().serialize(itemStack)) + getParkourUpgrader().getNewConfigManager() + .getItemStackSerializable().serialize(itemStack)) .collect(Collectors.toList()); newCourseConfig.set(CourseConfig.JOIN_ITEMS, results); diff --git a/src/main/java/io/github/a5h73y/parkour/upgrade/minor/PlayerMinorUpgradeTask.java b/src/main/java/io/github/a5h73y/parkour/upgrade/minor/PlayerMinorUpgradeTask.java index 449311bf..fdc158aa 100644 --- a/src/main/java/io/github/a5h73y/parkour/upgrade/minor/PlayerMinorUpgradeTask.java +++ b/src/main/java/io/github/a5h73y/parkour/upgrade/minor/PlayerMinorUpgradeTask.java @@ -2,11 +2,13 @@ import static io.github.a5h73y.parkour.upgrade.minor.TimedConfigUpgradeTask.updateConfigEntry; +import java.util.List; +import java.util.Objects; + import io.github.a5h73y.parkour.type.player.PlayerConfig; import io.github.a5h73y.parkour.upgrade.ParkourUpgrader; import io.github.a5h73y.parkour.upgrade.TimedUpgradeTask; import io.github.a5h73y.parkour.utility.PlayerUtils; -import java.util.List; import org.bukkit.OfflinePlayer; public class PlayerMinorUpgradeTask extends TimedUpgradeTask { @@ -24,12 +26,14 @@ protected String getTitle() { protected boolean doWork() { List uuids = getParkourUpgrader().getNewConfigManager().getAllPlayerUuids(); - uuids.forEach(uuid -> { - OfflinePlayer player = PlayerUtils.findPlayer(uuid); - PlayerConfig config = getParkourUpgrader().getNewConfigManager().getPlayerConfig(player); + uuids.stream() + .filter(Objects::nonNull) + .forEach(uuid -> { + OfflinePlayer player = PlayerUtils.findPlayer(uuid); + PlayerConfig config = getParkourUpgrader().getNewConfigManager().getPlayerConfig(player); - updateConfigEntry(config, "Snapshot.JoinLocation", "JoinLocation"); - }); + updateConfigEntry(config, "Snapshot.JoinLocation", "JoinLocation"); + }); return true; } diff --git a/src/main/java/io/github/a5h73y/parkour/upgrade/minor/TimedConfigUpgradeTask.java b/src/main/java/io/github/a5h73y/parkour/upgrade/minor/TimedConfigUpgradeTask.java index 36e01470..416ae9d2 100644 --- a/src/main/java/io/github/a5h73y/parkour/upgrade/minor/TimedConfigUpgradeTask.java +++ b/src/main/java/io/github/a5h73y/parkour/upgrade/minor/TimedConfigUpgradeTask.java @@ -1,8 +1,8 @@ package io.github.a5h73y.parkour.upgrade.minor; +import de.leonhard.storage.internal.FlatFile; import io.github.a5h73y.parkour.upgrade.ParkourUpgrader; import io.github.a5h73y.parkour.upgrade.TimedUpgradeTask; -import de.leonhard.storage.internal.FlatFile; public abstract class TimedConfigUpgradeTask extends TimedUpgradeTask { @@ -17,10 +17,12 @@ public void updateConfigEntry(String fromKey, String toKey) { updateConfigEntry(config, fromKey, toKey); } - public boolean keyExists(String key) { - return keyExists(config, key); - } - + /** + * Copy value from one key to another in given config. + * @param config config file + * @param fromKey from config key + * @param toKey to config key + */ public static void updateConfigEntry(FlatFile config, String fromKey, String toKey) { if (keyExists(config, fromKey)) { config.set(toKey, config.get(fromKey)); @@ -28,6 +30,10 @@ public static void updateConfigEntry(FlatFile config, String fromKey, String toK } } + public boolean keyExists(String key) { + return keyExists(config, key); + } + public static boolean keyExists(FlatFile config, String key) { return key != null && config.contains(key); } diff --git a/src/main/java/io/github/a5h73y/parkour/utility/PlayerUtils.java b/src/main/java/io/github/a5h73y/parkour/utility/PlayerUtils.java index e2d6cf61..98024894 100644 --- a/src/main/java/io/github/a5h73y/parkour/utility/PlayerUtils.java +++ b/src/main/java/io/github/a5h73y/parkour/utility/PlayerUtils.java @@ -1,9 +1,10 @@ package io.github.a5h73y.parkour.utility; +import java.util.UUID; + import com.cryptomorin.xseries.XPotion; import io.github.a5h73y.parkour.Parkour; import io.github.a5h73y.parkour.type.player.session.ParkourSession; -import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; diff --git a/src/main/java/io/github/a5h73y/parkour/utility/cache/CacheValue.java b/src/main/java/io/github/a5h73y/parkour/utility/cache/CacheValue.java new file mode 100644 index 00000000..a3250f07 --- /dev/null +++ b/src/main/java/io/github/a5h73y/parkour/utility/cache/CacheValue.java @@ -0,0 +1,9 @@ +package io.github.a5h73y.parkour.utility.cache; + +import java.time.LocalDateTime; + +public interface CacheValue { + V getValue(); + + LocalDateTime getCreatedAt(); +} \ No newline at end of file diff --git a/src/main/java/io/github/a5h73y/parkour/utility/cache/GenericCache.java b/src/main/java/io/github/a5h73y/parkour/utility/cache/GenericCache.java index 335e2914..09c1c643 100644 --- a/src/main/java/io/github/a5h73y/parkour/utility/cache/GenericCache.java +++ b/src/main/java/io/github/a5h73y/parkour/utility/cache/GenericCache.java @@ -1,7 +1,7 @@ package io.github.a5h73y.parkour.utility.cache; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -46,15 +46,17 @@ public boolean containsKey(K key) { protected Set getExpiredKeys() { return this.cacheMap.keySet() .parallelStream() + .filter(Objects::nonNull) .filter(this::isExpired) .collect(Collectors.toSet()); } protected boolean isExpired(K key) { boolean result = true; - if (this.cacheMap.containsKey(key)) { - LocalDateTime expirationDateTime = this.cacheMap.get(key) - .getCreatedAt().plus(this.cacheTimeout, ChronoUnit.SECONDS); + + CacheValue cacheValue = this.cacheMap.get(key); + if (cacheValue != null) { + LocalDateTime expirationDateTime = cacheValue.getCreatedAt().plusSeconds(this.cacheTimeout); result = LocalDateTime.now().isAfter(expirationDateTime); } return result; @@ -96,10 +98,8 @@ public void remove(K key) { this.cacheMap.remove(key); } - protected interface CacheValue { - V getValue(); - - LocalDateTime getCreatedAt(); + public ConcurrentMap> getCacheMap() { + return cacheMap; } } diff --git a/src/main/resources/parkourCheckstyles.xml b/src/main/resources/parkourCheckstyles.xml index 22ef17c6..a6d58f47 100644 --- a/src/main/resources/parkourCheckstyles.xml +++ b/src/main/resources/parkourCheckstyles.xml @@ -4,6 +4,12 @@ "https://checkstyle.org/dtds/configuration_1_3.dtd"> - + + + - + - + @@ -37,7 +45,7 @@ - + @@ -74,7 +82,7 @@ INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, - OBJBLOCK, STATIC_INIT"/> + OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/> @@ -87,13 +95,21 @@ + INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF, LITERAL_SWITCH"/> + or preceding-sibling::*[last()][self::LCURLY]]"/> + + + @@ -101,16 +117,18 @@ + + LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, + NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR, + SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/> + value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks + may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/> @@ -124,7 +142,8 @@ + STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF, + COMPACT_CTOR_DEF"/> @@ -138,13 +157,13 @@ - + - + @@ -160,7 +179,8 @@ value="Package name ''{0}'' must match pattern ''{1}''."/> - + @@ -189,11 +209,26 @@ + + + + + + + + + + + + - + + PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF, + RECORD_COMPONENT_DEF"/> + @@ -233,11 +270,12 @@ + SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/> + value="COMMA, SEMI, POST_INC, POST_DEC, DOT, + LABELED_STAT, METHOD_REF"/> @@ -245,18 +283,21 @@ value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF, EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW, LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL, - METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA"/> + METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA, + RECORD_DEF"/> + LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF, + TYPE_EXTENSION_AND "/> + value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, + RECORD_DEF, COMPACT_CTOR_DEF"/> @@ -271,40 +312,63 @@ value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/> + - + - + - + + + + + + - + - - + + + - + + + + + + + + + + + + + + diff --git a/src/main/resources/parkourPlaceholders.json b/src/main/resources/parkourPlaceholders.json index 25bba2ac..c6e90b2d 100644 --- a/src/main/resources/parkourPlaceholders.json +++ b/src/main/resources/parkourPlaceholders.json @@ -90,6 +90,31 @@ "placeholder": "%parkour_player_personal_best_(course)_deaths%", "output": "3", "description": "The number of deaths accumulated for the Player's personal best time on the specified Course." + }, + { + "placeholder": "%parkour_player_total_leaderboard_deaths%", + "output": "53", + "description": "The number of recorded deaths accumulated by the Player across all Courses in the Leaderboards." + }, + { + "placeholder": "%parkour_player_total_leaderboard_time%", + "output": "01:02:34", + "description": "The accumulated recorded time by the Player across all Courses in the Leaderboards." + }, + { + "placeholder": "%parkour_player_total_leaderboard_times%", + "output": "9", + "description": "The accumulated number of leaderboard times recorded by the Player across all Courses." + }, + { + "placeholder": "%parkour_player_total_playing_deaths%", + "output": "153", + "description": "The total number of recorded deaths accumulated by the Player across all Courses." + }, + { + "placeholder": "%parkour_player_total_playing_time%", + "output": "02:03:45", + "description": "The total recorded time by the Player across all Courses." } ] },