diff --git a/src/main/java/us/ajg0702/leaderboards/boards/TopManager.java b/src/main/java/us/ajg0702/leaderboards/boards/TopManager.java index a7b0fae2..adbb0810 100644 --- a/src/main/java/us/ajg0702/leaderboards/boards/TopManager.java +++ b/src/main/java/us/ajg0702/leaderboards/boards/TopManager.java @@ -270,6 +270,59 @@ public int getBoardSize(String board) { } + Map totalLastRefresh = new HashMap<>(); + LoadingCache totalCache = CacheBuilder.newBuilder() + .expireAfterAccess(24, TimeUnit.HOURS) + .refreshAfterWrite(1, TimeUnit.SECONDS) + .maximumSize(10000) + .removalListener(notification -> { + if(!notification.getCause().equals(RemovalCause.REPLACED)) totalLastRefresh.remove((BoardType) notification.getKey()); + }) + .build(new CacheLoader() { + @Override + public @NotNull Double load(@NotNull BoardType key) { + return plugin.getCache().getTotal(key.getBoard(), key.getType()); + } + + @Override + public @NotNull ListenableFuture reload(@NotNull BoardType key, @NotNull Double oldValue) { + if(plugin.isShuttingDown() || System.currentTimeMillis() - totalLastRefresh.getOrDefault(key, 0L) < Math.max(cacheTime()*2, 5000)) { + return Futures.immediateFuture(oldValue); + } + ListenableFutureTask task = ListenableFutureTask.create(() -> { + totalLastRefresh.put(key, System.currentTimeMillis()); + return plugin.getCache().getTotal(key.getBoard(), key.getType()); + }); + if(plugin.isShuttingDown()) return Futures.immediateFuture(oldValue); + fetchService.execute(task); + return task; + } + }); + + + /** + * Gets the sum of all players on the leaderboard + * @param board the board + * @param type the timed type + * @return the sum of all players in the specified board for the specified timed type + */ + public double getTotal(String board, TimedType type) { + BoardType boardType = new BoardType(board, type); + Double cached = totalCache.getIfPresent(boardType); + + if(cached == null) { + if(BlockingFetch.shouldBlock(plugin)) { + cached = totalCache.getUnchecked(boardType); + } else { + fetchService.submit(() -> totalCache.getUnchecked(boardType)); + return -2; + } + } + + return cached; + + } + List boardCache; long lastGetBoard = 0; diff --git a/src/main/java/us/ajg0702/leaderboards/cache/Cache.java b/src/main/java/us/ajg0702/leaderboards/cache/Cache.java index ccc0fe64..d7effe05 100644 --- a/src/main/java/us/ajg0702/leaderboards/cache/Cache.java +++ b/src/main/java/us/ajg0702/leaderboards/cache/Cache.java @@ -277,6 +277,58 @@ public int getBoardSize(String board) { return size; } + public double getTotal(String board, TimedType type) { + if(!plugin.getTopManager().boardExists(board)) { + if(!nonExistantBoards.contains(board)) { + nonExistantBoards.add(board); + } + return -3; + } + + Connection connection = null; + ResultSet rs = null; + + int size; + + try { + connection = method.getConnection(); + + PreparedStatement ps = connection.prepareStatement(String.format( + method.formatStatement("select SUM(%s) from '%s'"), + type == TimedType.ALLTIME ? "value" : type.lowerName() + "_delta", + tablePrefix+board + )); + + rs = ps.executeQuery(); + + rs.next(); + + size = rs.getInt(1); + + } catch (SQLException e) { + if( + !e.getMessage().contains("ResultSet closed") && + !e.getMessage().contains("empty result set") && + !e.getMessage().contains("[2000-") + ) { + plugin.getLogger().log(Level.WARNING, "Unable to get size of board:", e); + return -1; + } else { + return 0; + } + } finally { + try { + if(connection != null) method.close(connection); + if(rs != null) rs.close(); + } catch (SQLException e) { + plugin.getLogger().log(Level.WARNING, "Error while closing resources from board size fetch:", e); + } + + } + + return size; + } + public boolean createBoard(String name) { try { Connection conn = method.getConnection(); diff --git a/src/main/java/us/ajg0702/leaderboards/placeholders/PlaceholderExpansion.java b/src/main/java/us/ajg0702/leaderboards/placeholders/PlaceholderExpansion.java index 680a5a07..2ab6998f 100644 --- a/src/main/java/us/ajg0702/leaderboards/placeholders/PlaceholderExpansion.java +++ b/src/main/java/us/ajg0702/leaderboards/placeholders/PlaceholderExpansion.java @@ -3,8 +3,7 @@ import org.bukkit.OfflinePlayer; import org.jetbrains.annotations.NotNull; import us.ajg0702.leaderboards.LeaderboardPlugin; -import us.ajg0702.leaderboards.placeholders.placeholders.Reset; -import us.ajg0702.leaderboards.placeholders.placeholders.Size; +import us.ajg0702.leaderboards.placeholders.placeholders.*; import us.ajg0702.leaderboards.placeholders.placeholders.debug.Fetching; import us.ajg0702.leaderboards.placeholders.placeholders.debug.Rolling; import us.ajg0702.leaderboards.placeholders.placeholders.lb.*; @@ -66,6 +65,9 @@ public PlaceholderExpansion(LeaderboardPlugin plugin) { placeholders.add(new Reset(plugin)); placeholders.add(new Size(plugin)); + placeholders.add(new TotalFormatted(plugin)); + placeholders.add(new TotalRaw(plugin)); + placeholders.add(new Total(plugin)); placeholders.add(new Fetching(plugin)); placeholders.add(new Rolling(plugin)); diff --git a/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/Total.java b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/Total.java new file mode 100644 index 00000000..32a11e1e --- /dev/null +++ b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/Total.java @@ -0,0 +1,32 @@ +package us.ajg0702.leaderboards.placeholders.placeholders; + +import org.bukkit.OfflinePlayer; +import us.ajg0702.leaderboards.LeaderboardPlugin; +import us.ajg0702.leaderboards.boards.TimedType; +import us.ajg0702.leaderboards.placeholders.Placeholder; + +import java.util.Locale; +import java.util.regex.Matcher; + +public class Total extends Placeholder { + public Total(LeaderboardPlugin plugin) { + super(plugin); + } + + @Override + public String getRegex() { + return "total_(.*)_(.*)"; + } + + @Override + public String parse(Matcher matcher, OfflinePlayer p) { + String board = matcher.group(1); + String typeRaw = matcher.group(2).toUpperCase(Locale.ROOT); + TimedType type = TimedType.of(typeRaw); + if(type == null) { + return "Invalid TimedType '" + typeRaw + "'"; + } + double total = plugin.getTopManager().getTotal(board, type); + return plugin.getPlaceholderFormatter().toFormat(total, board); + } +} diff --git a/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalFormatted.java b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalFormatted.java new file mode 100644 index 00000000..0ea3cc03 --- /dev/null +++ b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalFormatted.java @@ -0,0 +1,34 @@ +package us.ajg0702.leaderboards.placeholders.placeholders; + +import org.bukkit.OfflinePlayer; +import us.ajg0702.leaderboards.LeaderboardPlugin; +import us.ajg0702.leaderboards.boards.TimedType; +import us.ajg0702.leaderboards.placeholders.Placeholder; + +import java.util.Locale; +import java.util.regex.Matcher; + +import static us.ajg0702.leaderboards.boards.StatEntry.formatDouble; + +public class TotalFormatted extends Placeholder { + public TotalFormatted(LeaderboardPlugin plugin) { + super(plugin); + } + + @Override + public String getRegex() { + return "total_(.*)_(.*)_formatted"; + } + + @Override + public String parse(Matcher matcher, OfflinePlayer p) { + String board = matcher.group(1); + String typeRaw = matcher.group(2).toUpperCase(Locale.ROOT); + TimedType type = TimedType.of(typeRaw); + if(type == null) { + return "Invalid TimedType '" + typeRaw + "'"; + } + double total = plugin.getTopManager().getTotal(board, type); + return formatDouble(total); + } +} diff --git a/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalRaw.java b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalRaw.java new file mode 100644 index 00000000..34831b33 --- /dev/null +++ b/src/main/java/us/ajg0702/leaderboards/placeholders/placeholders/TotalRaw.java @@ -0,0 +1,34 @@ +package us.ajg0702.leaderboards.placeholders.placeholders; + +import org.bukkit.OfflinePlayer; +import us.ajg0702.leaderboards.LeaderboardPlugin; +import us.ajg0702.leaderboards.boards.TimedType; +import us.ajg0702.leaderboards.placeholders.Placeholder; + +import java.text.DecimalFormat; +import java.util.Locale; +import java.util.regex.Matcher; + +public class TotalRaw extends Placeholder { + public TotalRaw(LeaderboardPlugin plugin) { + super(plugin); + } + + @Override + public String getRegex() { + return "total_(.*)_(.*)_raw"; + } + + @Override + public String parse(Matcher matcher, OfflinePlayer p) { + String board = matcher.group(1); + String typeRaw = matcher.group(2).toUpperCase(Locale.ROOT); + TimedType type = TimedType.of(typeRaw); + if(type == null) { + return "Invalid TimedType '" + typeRaw + "'"; + } + double total = plugin.getTopManager().getTotal(board, type); + DecimalFormat df = new DecimalFormat("#.##"); + return df.format(total); + } +}