From 7712ea1c039e27423d672b10ea791d047615dd5e Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 25 Aug 2026 18:02:20 -0600 Subject: [PATCH 1/3] Split top voter loading and ranking --- .../votingplugin/VotingPluginMain.java | 26 +- .../votingplugin/signs/SignHandler.java | 636 +++--- .../topvoter/TopVoterHandler.java | 1944 +++++++---------- .../votingplugin/topvoter/TopVoterLoader.java | 208 ++ .../topvoter/TopVoterRanking.java | 55 + .../topvoter/TopVoterLoaderTest.java | 25 + 6 files changed, 1455 insertions(+), 1439 deletions(-) create mode 100644 VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterLoader.java create mode 100644 VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterRanking.java create mode 100644 VotingPlugin/src/test/java/com/bencodez/votingplugin/topvoter/TopVoterLoaderTest.java diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/VotingPluginMain.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/VotingPluginMain.java index e283d45ed9..7e58920864 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/VotingPluginMain.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/VotingPluginMain.java @@ -268,7 +268,6 @@ public class VotingPluginMain extends AdvancedCorePlugin { @Getter private long lastBackgroundTaskTimeTaken = -1; - private boolean firstTimeLoaded = false; @Getter @Setter @@ -291,25 +290,7 @@ public void basicBungeeUpdate() { } } - public void checkFirstTimeLoaded() { - if (!firstTimeLoaded) { - - if (getGui().isChestVoteTopUseSkull()) { - int maxToLoad = 200; - for (TopVoter top : getTopVoter().keySet()) { - int num = 1; - Set players = getTopVoter().get(top).keySet(); - for (TopVoterPlayer p : players) { - if (num <= maxToLoad) { - getSkullCacheHandler().addToCache(p.getUuid(), p.getPlayerName()); - } - num++; - } - } - } - } - firstTimeLoaded = true; - } + /** * Check votifier. @@ -351,9 +332,7 @@ public void loadVoteShopManager() { voteShopManager = new VoteShopManager(this); } - public ArrayList convertSet(Set set) { - return new ArrayList<>(set); - } + @Override public FileConfiguration getConfig() { @@ -1212,7 +1191,6 @@ public synchronized void update() { serverData.updateValues(); getSigns().updateSigns(); - checkFirstTimeLoaded(); if (plugin.getConfigFile().isDiscordSRVEnabled() && getDiscordHandler() != null) { for (TopVoter top : TopVoter.values()) { diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/signs/SignHandler.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/signs/SignHandler.java index 0b62bc7ce4..688eda03a1 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/signs/SignHandler.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/signs/SignHandler.java @@ -1,318 +1,318 @@ -package com.bencodez.votingplugin.signs; - -import java.util.ArrayList; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.Skull; -import org.bukkit.block.data.Directional; -import org.bukkit.inventory.meta.SkullMeta; - -import com.bencodez.advancedcore.api.misc.MiscUtils; -import com.bencodez.simpleapi.array.ArrayUtils; -import com.bencodez.votingplugin.VotingPluginMain; -import com.bencodez.votingplugin.topvoter.TopVoter; -import com.bencodez.votingplugin.topvoter.TopVoterPlayer; - -import lombok.Getter; -import lombok.Setter; - -// TODO: Auto-generated Javadoc -/** - * The Class SignHandler. - */ -public class SignHandler { - - @Getter - @Setter - private String data; - - /** The lines. */ - private ArrayList lines; - - @Getter - @Setter - private Location location; - - private String playerName; - - private UUID uuid; - - private VotingPluginMain plugin; - - private int position; - - @Getter - @Setter - private String sign; - - @Getter - @Setter - private Location skullLocation; - - @Getter - @Setter - private boolean valid; - - private int votes; - - /** - * Constructs a new SignHandler. - * - * @param plugin the main plugin instance - * @param sign the sign identifier - * @param location the sign location - * @param skullLocation the skull location - * @param data the sign data - * @param position the sign position - */ - public SignHandler(VotingPluginMain plugin, String sign, Location location, Location skullLocation, String data, - int position) { - this.plugin = plugin; - setSign(sign); - setLocation(location); - setSkullLocation(skullLocation); - setData(data); - this.position = position; - setValid(true); - lines = new ArrayList<>(); - checkValidSign(); - playerName = ""; - } - - @SuppressWarnings("deprecation") - private boolean checkSkull(Block block) { - if (block.getState() instanceof Skull) { - if (!playerName.equals("No Player")) { - Skull skull = (Skull) block.getState(); - try { - SkullMeta meta = (SkullMeta) plugin.getSkullCacheHandler().getSkull(uuid, playerName).getItemMeta(); - skull.setOwnerProfile(meta.getOwnerProfile()); - skull.update(true, false); - } catch (Exception e) { - plugin.debug("Failed to set skull for " + playerName); - plugin.debug(e); - skull.setOwner(playerName); - skull.update(true, false); - } - - } - return true; - } - return false; - } - - /** - * Check skulls. - */ - public void checkSkulls() { - if (noNameAvailable()) { - return; - } - if (skullLocation != null) { - checkSkull(skullLocation.getBlock()); - return; - } - - Location loc = location; - Location loc1 = new Location(loc.getWorld(), loc.getBlockX() - 1, loc.getBlockY() - 1, loc.getBlockZ() - 1); - Location loc2 = new Location(loc.getWorld(), loc.getBlockX() + 1, loc.getBlockY() + 1, loc.getBlockZ() + 1); - updateSkulls(loc1, loc2); - } - - /** - * Check valid sign. - */ - public void checkValidSign() { - plugin.getBukkitScheduler().runTask(plugin, new Runnable() { - - @Override - public void run() { - try { - setValid(getLocation().getBlock().getState() instanceof Sign); - } catch (Exception ex) { - setValid(false); - } - } - }, getLocation()); - - } - - /** - * Gets the right click message. - * - * @return the right click message - */ - public String getRightClickMessage() { - String msg = plugin.getConfigFile().getFormatSignTopVoterSignRightClickMessage(); - msg = msg.replace("%player%", playerName); - msg = msg.replace("%position%", "" + position); - msg = msg.replace("%votes%", "" + votes); - msg = msg.replace("%SiteName%", data); - return msg; - } - - /** - * Checks if is location same. - * - * @param loc the loc - * @return true, if is location same - */ - public boolean isLocationSame(Location loc) { - return loc.equals(getLocation()); - } - - /** - * Checks if skull location is set. - * - * @return true if skull is set - */ - public boolean isSkullSet() { - return skullLocation != null; - } - - /** - * Checks if no player name is available. - * - * @return true if no name available - */ - public boolean noNameAvailable() { - return playerName.equalsIgnoreCase("No Player") || playerName.equals(""); - } - - /** - * Removes the sign. - */ - public void removeSign() { - plugin.getServerData().removeSign(sign); - } - - /** - * Store sign. - */ - public void storeSign() { - plugin.getServerData().setSign(sign, location, skullLocation, data, position); - } - - /** - * Update lines. - */ - public void updateLines() { - lines = new ArrayList<>(); - checkValidSign(); - if (position != 0) { - String line1 = plugin.getConfigFile().getFormatSignTopVoterSignLine1(); - String line2 = plugin.getConfigFile().getFormatSignTopVoterSignLine2(); - String line3 = plugin.getConfigFile().getFormatSignTopVoterSignLine3(); - String line4 = plugin.getConfigFile().getFormatSignTopVoterSignLine4(); - lines.add(line1); - lines.add(line2); - lines.add(line3); - lines.add(line4); - - ArrayList users = null; - if (data.equalsIgnoreCase("all")) { - users = plugin.convertSet(plugin.getTopVoter(TopVoter.AllTime).keySet()); - } else if (data.equalsIgnoreCase("monthly")) { - users = plugin.convertSet(plugin.getTopVoter(TopVoter.Monthly).keySet()); - } else if (data.equalsIgnoreCase("weekly")) { - users = plugin.convertSet(plugin.getTopVoter(TopVoter.Weekly).keySet()); - } else if (data.equalsIgnoreCase("daily")) { - users = plugin.convertSet(plugin.getTopVoter(TopVoter.Daily).keySet()); - } - - if (users != null && users.size() >= position) { - TopVoterPlayer user = users.get(position - 1); - playerName = user.getPlayerName(); - uuid = user.getUuid(); - - votes = 0; - if (data.equalsIgnoreCase("all")) { - votes = plugin.getTopVoter(TopVoter.AllTime).get(user); - } else if (data.equalsIgnoreCase("monthly")) { - votes = plugin.getTopVoter(TopVoter.Monthly).get(user); - } else if (data.equalsIgnoreCase("weekly")) { - votes = plugin.getTopVoter(TopVoter.Weekly).get(user); - } else if (data.equalsIgnoreCase("daily")) { - votes = plugin.getTopVoter(TopVoter.Daily).get(user); - } - - for (int j = 0; j < lines.size(); j++) { - lines.set(j, lines.get(j).replace("%votes%", "" + votes).replace("%player%", playerName)); - } - } else { - playerName = "No Player"; - votes = 0; - for (int j = 0; j < lines.size(); j++) { - lines.set(j, lines.get(j).replace("%votes%", "" + votes).replace("%player%", playerName)); - } - } - - for (int j = 0; j < lines.size(); j++) { - lines.set(j, lines.get(j).replace("%SiteName%", data).replace("%position%", "" + position)); - } - - lines = ArrayUtils.colorize(lines); - - } - - } - - /** - * Update sign. - * - * @param delay the delay - */ - public void updateSign(int delay) { - plugin.getBukkitScheduler().runTaskLater(plugin, new Runnable() { - - @SuppressWarnings("deprecation") - @Override - public void run() { - try { - BlockState state = getLocation().getBlock().getState(); - if (state instanceof Sign) { - Sign s = (Sign) state; - - for (int j = 0; j < lines.size() && j < 4; j++) { - s.setLine(j, lines.get(j)); - } - s.update(); - checkSkulls(); - } - - } catch (Exception ex) { - plugin.debug("Failed to update sign at " + getLocation().toString()); - VotingPluginMain.plugin.debug(ex); - } - } - }, delay, getLocation()); - } - - /** - * Updates skulls in the specified region. - * - * @param loc1 the first location - * @param loc2 the second location - */ - public void updateSkulls(Location loc1, Location loc2) { - BlockState state = getLocation().getBlock().getState(); - if (state instanceof Sign && state.getBlockData() instanceof Directional) { - Directional s = (Directional) state.getBlockData(); - Block b = location.getBlock().getRelative(s.getFacing()); - Block above = b.getRelative(BlockFace.UP); - if (checkSkull(above)) { - return; - } - - } - - for (Block block : MiscUtils.getInstance().getRegionBlocks(location.getWorld(), loc1, loc2)) { - checkSkull(block); - } - } -} +package com.bencodez.votingplugin.signs; + +import java.util.ArrayList; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.Skull; +import org.bukkit.block.data.Directional; +import org.bukkit.inventory.meta.SkullMeta; + +import com.bencodez.advancedcore.api.misc.MiscUtils; +import com.bencodez.simpleapi.array.ArrayUtils; +import com.bencodez.votingplugin.VotingPluginMain; +import com.bencodez.votingplugin.topvoter.TopVoter; +import com.bencodez.votingplugin.topvoter.TopVoterPlayer; + +import lombok.Getter; +import lombok.Setter; + +// TODO: Auto-generated Javadoc +/** + * The Class SignHandler. + */ +public class SignHandler { + + @Getter + @Setter + private String data; + + /** The lines. */ + private ArrayList lines; + + @Getter + @Setter + private Location location; + + private String playerName; + + private UUID uuid; + + private VotingPluginMain plugin; + + private int position; + + @Getter + @Setter + private String sign; + + @Getter + @Setter + private Location skullLocation; + + @Getter + @Setter + private boolean valid; + + private int votes; + + /** + * Constructs a new SignHandler. + * + * @param plugin the main plugin instance + * @param sign the sign identifier + * @param location the sign location + * @param skullLocation the skull location + * @param data the sign data + * @param position the sign position + */ + public SignHandler(VotingPluginMain plugin, String sign, Location location, Location skullLocation, String data, + int position) { + this.plugin = plugin; + setSign(sign); + setLocation(location); + setSkullLocation(skullLocation); + setData(data); + this.position = position; + setValid(true); + lines = new ArrayList<>(); + checkValidSign(); + playerName = ""; + } + + @SuppressWarnings("deprecation") + private boolean checkSkull(Block block) { + if (block.getState() instanceof Skull) { + if (!playerName.equals("No Player")) { + Skull skull = (Skull) block.getState(); + try { + SkullMeta meta = (SkullMeta) plugin.getSkullCacheHandler().getSkull(uuid, playerName).getItemMeta(); + skull.setOwnerProfile(meta.getOwnerProfile()); + skull.update(true, false); + } catch (Exception e) { + plugin.debug("Failed to set skull for " + playerName); + plugin.debug(e); + skull.setOwner(playerName); + skull.update(true, false); + } + + } + return true; + } + return false; + } + + /** + * Check skulls. + */ + public void checkSkulls() { + if (noNameAvailable()) { + return; + } + if (skullLocation != null) { + checkSkull(skullLocation.getBlock()); + return; + } + + Location loc = location; + Location loc1 = new Location(loc.getWorld(), loc.getBlockX() - 1, loc.getBlockY() - 1, loc.getBlockZ() - 1); + Location loc2 = new Location(loc.getWorld(), loc.getBlockX() + 1, loc.getBlockY() + 1, loc.getBlockZ() + 1); + updateSkulls(loc1, loc2); + } + + /** + * Check valid sign. + */ + public void checkValidSign() { + plugin.getBukkitScheduler().runTask(plugin, new Runnable() { + + @Override + public void run() { + try { + setValid(getLocation().getBlock().getState() instanceof Sign); + } catch (Exception ex) { + setValid(false); + } + } + }, getLocation()); + + } + + /** + * Gets the right click message. + * + * @return the right click message + */ + public String getRightClickMessage() { + String msg = plugin.getConfigFile().getFormatSignTopVoterSignRightClickMessage(); + msg = msg.replace("%player%", playerName); + msg = msg.replace("%position%", "" + position); + msg = msg.replace("%votes%", "" + votes); + msg = msg.replace("%SiteName%", data); + return msg; + } + + /** + * Checks if is location same. + * + * @param loc the loc + * @return true, if is location same + */ + public boolean isLocationSame(Location loc) { + return loc.equals(getLocation()); + } + + /** + * Checks if skull location is set. + * + * @return true if skull is set + */ + public boolean isSkullSet() { + return skullLocation != null; + } + + /** + * Checks if no player name is available. + * + * @return true if no name available + */ + public boolean noNameAvailable() { + return playerName.equalsIgnoreCase("No Player") || playerName.equals(""); + } + + /** + * Removes the sign. + */ + public void removeSign() { + plugin.getServerData().removeSign(sign); + } + + /** + * Store sign. + */ + public void storeSign() { + plugin.getServerData().setSign(sign, location, skullLocation, data, position); + } + + /** + * Update lines. + */ + public void updateLines() { + lines = new ArrayList<>(); + checkValidSign(); + if (position != 0) { + String line1 = plugin.getConfigFile().getFormatSignTopVoterSignLine1(); + String line2 = plugin.getConfigFile().getFormatSignTopVoterSignLine2(); + String line3 = plugin.getConfigFile().getFormatSignTopVoterSignLine3(); + String line4 = plugin.getConfigFile().getFormatSignTopVoterSignLine4(); + lines.add(line1); + lines.add(line2); + lines.add(line3); + lines.add(line4); + + ArrayList users = null; + if (data.equalsIgnoreCase("all")) { + users = new ArrayList<>(plugin.getTopVoter(TopVoter.AllTime).keySet()); + } else if (data.equalsIgnoreCase("monthly")) { + users = new ArrayList<>(plugin.getTopVoter(TopVoter.Monthly).keySet()); + } else if (data.equalsIgnoreCase("weekly")) { + users = new ArrayList<>(plugin.getTopVoter(TopVoter.Weekly).keySet()); + } else if (data.equalsIgnoreCase("daily")) { + users = new ArrayList<>(plugin.getTopVoter(TopVoter.Daily).keySet()); + } + + if (users != null && users.size() >= position) { + TopVoterPlayer user = users.get(position - 1); + playerName = user.getPlayerName(); + uuid = user.getUuid(); + + votes = 0; + if (data.equalsIgnoreCase("all")) { + votes = plugin.getTopVoter(TopVoter.AllTime).get(user); + } else if (data.equalsIgnoreCase("monthly")) { + votes = plugin.getTopVoter(TopVoter.Monthly).get(user); + } else if (data.equalsIgnoreCase("weekly")) { + votes = plugin.getTopVoter(TopVoter.Weekly).get(user); + } else if (data.equalsIgnoreCase("daily")) { + votes = plugin.getTopVoter(TopVoter.Daily).get(user); + } + + for (int j = 0; j < lines.size(); j++) { + lines.set(j, lines.get(j).replace("%votes%", "" + votes).replace("%player%", playerName)); + } + } else { + playerName = "No Player"; + votes = 0; + for (int j = 0; j < lines.size(); j++) { + lines.set(j, lines.get(j).replace("%votes%", "" + votes).replace("%player%", playerName)); + } + } + + for (int j = 0; j < lines.size(); j++) { + lines.set(j, lines.get(j).replace("%SiteName%", data).replace("%position%", "" + position)); + } + + lines = ArrayUtils.colorize(lines); + + } + + } + + /** + * Update sign. + * + * @param delay the delay + */ + public void updateSign(int delay) { + plugin.getBukkitScheduler().runTaskLater(plugin, new Runnable() { + + @SuppressWarnings("deprecation") + @Override + public void run() { + try { + BlockState state = getLocation().getBlock().getState(); + if (state instanceof Sign) { + Sign s = (Sign) state; + + for (int j = 0; j < lines.size() && j < 4; j++) { + s.setLine(j, lines.get(j)); + } + s.update(); + checkSkulls(); + } + + } catch (Exception ex) { + plugin.debug("Failed to update sign at " + getLocation().toString()); + VotingPluginMain.plugin.debug(ex); + } + } + }, delay, getLocation()); + } + + /** + * Updates skulls in the specified region. + * + * @param loc1 the first location + * @param loc2 the second location + */ + public void updateSkulls(Location loc1, Location loc2) { + BlockState state = getLocation().getBlock().getState(); + if (state instanceof Sign && state.getBlockData() instanceof Directional) { + Directional s = (Directional) state.getBlockData(); + Block b = location.getBlock().getRelative(s.getFacing()); + Block above = b.getRelative(BlockFace.UP); + if (checkSkull(above)) { + return; + } + + } + + for (Block block : MiscUtils.getInstance().getRegionBlocks(location.getWorld(), loc1, loc2)) { + checkSkull(block); + } + } +} diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterHandler.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterHandler.java index 3d02d832a6..7a5eee0eac 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterHandler.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterHandler.java @@ -1,1097 +1,847 @@ -package com.bencodez.votingplugin.topvoter; - -import java.io.File; -import java.time.LocalDateTime; -import java.time.Month; -import java.time.YearMonth; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; - -import com.bencodez.advancedcore.api.time.TimeType; -import com.bencodez.advancedcore.api.time.events.DateChangedEvent; -import com.bencodez.advancedcore.api.time.events.DayChangeEvent; -import com.bencodez.advancedcore.api.time.events.MonthChangeEvent; -import com.bencodez.advancedcore.api.time.events.PreDateChangedEvent; -import com.bencodez.advancedcore.api.time.events.WeekChangeEvent; -import com.bencodez.advancedcore.api.user.UserDataFetchMode; -import com.bencodez.advancedcore.api.user.UserStorage; -import com.bencodez.simpleapi.array.ArrayUtils; -import com.bencodez.simpleapi.file.YMLFileHandler; -import com.bencodez.simpleapi.messages.MessageAPI; -import com.bencodez.simpleapi.sql.Column; -import com.bencodez.simpleapi.sql.DataType; -import com.bencodez.votingplugin.VotingPluginMain; -import com.bencodez.votingplugin.user.VotingPluginUser; - -/** - * Handles top voter rankings and statistics. - */ -public class TopVoterHandler implements Listener { - - private VotingPluginMain plugin; - - /** - * Constructs a new top voter handler. - * @param plugin the plugin instance - */ - public TopVoterHandler(VotingPluginMain plugin) { - this.plugin = plugin; - } - - /** - * Checks if bungee should handle resets. - * @return true if bungee handles resets - */ - public boolean bungeeHandleResets() { - if (plugin.getBungeeSettings().isUseBungeecoord()) { - if (plugin.getBungeeSettings().isGloblalDataEnabled()) { - return true; - } - } - - return false; - } - - /** - * Gets monthly top voters at a specific time. - * @param atTime the time to check - * @return map of top voters and their vote counts - */ - public LinkedHashMap getMonthlyTopVotersAtTime(LocalDateTime atTime) { - // int limitSize = plugin.getConfigFile().getMaxiumNumberOfTopVotersToLoad(); - - LinkedHashMap topVoter = new LinkedHashMap<>(); - - // forEachUserKeys is callback-based; wait for completion because this method - // returns a value. - // NOTE: do not call this from the main thread if forEachUserKeys is async. - CountDownLatch latch = new CountDownLatch(1); - - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - if (plugin != null && plugin.isEnabled() && uuid != null) { - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - int total = user.getTotal(TopVoter.Monthly, atTime); - if (total > 0) { - topVoter.put(user.getTopVoterPlayer(), total); - } - - user.clearTempCache(); - } - }, (count) -> { - latch.countDown(); - }); - - try { - latch.await(10, TimeUnit.MINUTES); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - return sortByValues(topVoter, false); - - } - - /** - * Gets the top voter blacklist. - * @return list of blacklisted players - */ - public ArrayList getTopVoterBlackList() { - return plugin.getConfigFile().getBlackList(); - } - - /** - * Gets top voters for a specific month. - * @param month the year-month to check - * @param cols player data columns - * @return map of top voters and their vote counts - */ - public LinkedHashMap getTopVotersOfMonth(YearMonth month, - HashMap> cols) { - - LinkedHashMap totals = new LinkedHashMap<>(); - LocalDateTime atTime = month.atDay(15).atTime(0, 0); - for (Entry> playerData : cols.entrySet()) { - - String uuid = playerData.getKey().toString(); - if (uuid != null && !uuid.isEmpty()) { - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(UUID.fromString(uuid), - false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(playerData.getValue()); - int total = 0; - total = user.getTotal(TopVoter.Monthly, atTime); - - if (total > 0) { - totals.put(user.getTopVoterPlayer(), total); - } - user.clearTempCache(); - } - } - - return sortByValues(totals, false); - } - - /** - * Top voters weekly. - * - * @return the string[] - */ - - /** - * Gets weekly top voters as formatted strings. - * @return array of formatted top voter lines - */ - public String[] getTopVotersWeekly() { - ArrayList msg = new ArrayList<>(); - ArrayList users = plugin.convertSet(plugin.getTopVoter(TopVoter.Weekly).keySet()); - for (int i = 0; i < users.size(); i++) { - String line = plugin.getConfigFile().getFormatCommandsVoteTopLine().replace("%num%", "" + (i + 1)) - .replace("%player%", users.get(i).getPlayerName()) - .replace("%votes%", "" + plugin.getTopVoter(TopVoter.Weekly).get(users.get(i))); - msg.add(line); - } - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - private HashMap handlePlaces(Set places) { - HashMap place = new HashMap<>(); - for (String p : places) { - String[] data = p.split("-"); - try { - if (data.length > 1) { - for (int i = Integer.parseInt(data[0]); i < Integer.parseInt(data[1]); i++) { - place.put(i, p); - } - } else { - place.put(Integer.parseInt(data[0]), p); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - return place; - } - - /** - * Loads last month's top voter data. - */ - public void loadLastMonth() { - if (!plugin.getGui().isLastMonthGUI()) { - return; - } - - plugin.getLastMonthTopVoter().clear(); - - // If you don’t need insertion order here, HashMap is faster. - final HashMap totals = new HashMap<>(); - - final LocalDateTime lastMonthTime = plugin.getTimeChecker().getTime().minusMonths(1); - final boolean useDateTotalsPrimary = plugin.getConfigFile().isUseMonthDateTotalsAsPrimaryTotal(); - - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - if (uuid == null || plugin == null || !plugin.isEnabled()) { - return; - } - - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - try { - final int total = useDateTotalsPrimary ? user.getTotal(TopVoter.Monthly, lastMonthTime) - : user.getLastMonthTotal(); - - if (total > 0) { - // Only build tvp if we actually need it - totals.put(user.getTopVoterPlayer(), total); - } - } finally { - user.clearTempCache(); - } - - }, (count) -> { - plugin.getLastMonthTopVoter().putAll(sortByValues(new LinkedHashMap<>(totals), false)); - plugin.debug("Loaded last month top voters"); - }); - } - - /** - * Loads previous month top voters for all configured months. - */ - public void loadPreviousMonthTopVoters() { - if (!plugin.getConfigFile().isStoreMonthTotalsWithDate()) { - return; - } - - final LocalDateTime now = plugin.getTimeChecker().getTime(); - final YearMonth currentYm = YearMonth.of(now.getYear(), now.getMonth()); - - // Discover month columns -> YearMonth list + matching column names - final ArrayList months = new ArrayList<>(); - final ArrayList monthColumns = new ArrayList<>(); - - for (String column : plugin.getUserManager().getAllColumns()) { - if (!column.startsWith("MonthTotal-")) { - continue; - } - - // Expected: MonthTotal-MONTH-YEAR (ex: MonthTotal-JANUARY-2025) - // Faster than split("-") (regex) - final int firstDash = column.indexOf('-'); // after MonthTotal - final int secondDash = column.indexOf('-', firstDash + 1); // after MONTH - if (firstDash < 0 || secondDash < 0) { - continue; - } - - final String monthStr = column.substring(firstDash + 1, secondDash); - final String yearStr = column.substring(secondDash + 1); - - if (!MessageAPI.isInt(yearStr)) { - continue; - } - - try { - final int year = Integer.parseInt(yearStr); - final Month month = Month.valueOf(monthStr); - final YearMonth ym = YearMonth.of(year, month); - - if (ym.isBefore(currentYm)) { - months.add(ym); - monthColumns.add(column); - } - } catch (Exception ignored) { - // bad month string etc. - } - } - - if (months.isEmpty()) { - return; - } - - // Sort months AND keep monthColumns aligned - { - final Integer[] idx = new Integer[months.size()]; - for (int i = 0; i < idx.length; i++) - idx[i] = i; - - java.util.Arrays.sort(idx, (a, b) -> months.get(a).compareTo(months.get(b))); - - final ArrayList monthsSorted = new ArrayList<>(months.size()); - final ArrayList colsSorted = new ArrayList<>(months.size()); - for (int i = 0; i < idx.length; i++) { - monthsSorted.add(months.get(idx[i])); - colsSorted.add(monthColumns.get(idx[i])); - } - - months.clear(); - months.addAll(monthsSorted); - - monthColumns.clear(); - monthColumns.addAll(colsSorted); - } - - // Prepare destination maps once - final LinkedHashMap> prev = plugin - .getPreviousMonthsTopVoters(); - prev.clear(); - - for (YearMonth ym : months) { - plugin.debug("Loading previous month top voters of " + ym); - prev.put(ym, new LinkedHashMap<>()); - } - - @SuppressWarnings("unchecked") - final LinkedHashMap[] monthMaps = new LinkedHashMap[months.size()]; - for (int i = 0; i < months.size(); i++) { - monthMaps[i] = prev.get(months.get(i)); - } - - // Snapshot arrays for faster access inside the loop - final String[] monthColsArr = monthColumns.toArray(new String[0]); - - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - if (uuid == null) { - return; - } - - final VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - try { - TopVoterPlayer tvp = null; - - // Read MonthTotal-* columns directly (FAST) - for (int i = 0; i < monthColsArr.length; i++) { - final int total = user.getData().getInt(monthColsArr[i], 0, UserDataFetchMode.TEMP_ONLY); - if (total > 0) { - if (tvp == null) { - tvp = user.getTopVoterPlayer(); - } - monthMaps[i].put(tvp, total); - } - } - } finally { - user.clearTempCache(); - } - - }, (count) -> { - // Sort each month map once - for (int i = 0; i < months.size(); i++) { - final YearMonth ym = months.get(i); - prev.put(ym, sortByValues(prev.get(ym), false)); - } - - plugin.extraDebug("Previous Months: " + prev.keySet()); - }); - } - - /** - * Handles date change events. - * - * @param event date changed event - */ - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onDateChanged(DateChangedEvent event) { - plugin.setUpdate(true); - plugin.update(); - if (event.getTimeType().equals(TimeType.MONTH)) { - loadLastMonth(); - } - if (plugin.getStorageType().equals(UserStorage.MYSQL)) { - plugin.getMysql().clearCacheBasic(); - } - } - - /** - * Handles day change events. - * @param event the day change event - */ - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onDayChange(DayChangeEvent event) { - synchronized (VotingPluginMain.plugin) { - long startTime = System.currentTimeMillis(); - if (plugin.getConfigFile().isStoreTopVotersDaily()) { - plugin.getLogger().info("Saving TopVoters Daily"); - storeTopVoters(TopVoter.Daily); - } - - plugin.getUserManager().copyColumnData(TopVoter.Daily.getColumnName(), TopVoter.Daily.getLastColumnName()); - if (plugin.getConfigFile().isUseVoteStreaks() || plugin.getConfigFile().isUseHighestTotals()) { - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - if (plugin.getConfigFile().isUseVoteStreaks()) { - if (!user.voteStreakUpdatedToday(LocalDateTime.now().minusDays(1))) { - if (user.getDayVoteStreak() != 0) { - user.setDayVoteStreak(0); - } - } - } - - if (plugin.getConfigFile().isUseHighestTotals()) { - if (user.getHighestDailyTotal() < user.getTotal(TopVoter.Daily)) { - user.setHighestDailyTotal(user.getTotal(TopVoter.Daily)); - } - } - user.clearTempCache(); - }, (count) -> { - // finished - }); - } - - try { - if (plugin.getSpecialRewardsConfig().isEnableDailyRewards()) { - HashMap places = handlePlaces( - plugin.getSpecialRewardsConfig().getDailyPossibleRewardPlaces()); - int i = 0; - int lastTotal = -1; - @SuppressWarnings("unchecked") - LinkedHashMap clone = (LinkedHashMap) plugin - .getTopVoter(TopVoter.Daily).clone(); - for (Entry entry : clone.entrySet()) { - if (plugin.getConfigFile().isTopVoterAwardsTies()) { - if (entry.getValue().intValue() != lastTotal) { - i++; - } - } else { - i++; - } - if (places.containsKey(i)) { - VotingPluginUser user = entry.getKey().getUser(); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { - user.giveDailyTopVoterAward(i, places.get(i)); - plugin.getLogger().info( - "Giving daily top voter reward " + i + " to " + entry.getKey().getPlayerName()); - } - } - lastTotal = entry.getValue().intValue(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { - if (plugin.getShopFile().getVoteShopResetDaily(shopIdent)) { - resetVoteShopLimit(shopIdent); - } - } - - // give time for other servers to catch up - if (!plugin.getTopVoterHandler().bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { - plugin.debug("Delaying time change 10 seconds for other servers to catchup"); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - if (!bungeeHandleResets()) { - resetTotals(TopVoter.Daily); - } - - if (plugin.getStorageType().equals(UserStorage.MYSQL)) { - plugin.getMysql().clearCacheBasic(); - } - - long now = ((System.currentTimeMillis() - startTime) / 1000); - plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); - } - } - - /** - * Handles month change events. - * @param event the month change event - */ - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onMonthChange(MonthChangeEvent event) { - long startTime = System.currentTimeMillis(); - synchronized (VotingPluginMain.plugin) { - plugin.getLogger().info("Saving TopVoters Monthly"); - storeTopVoters(TopVoter.Monthly); - if (!bungeeHandleResets()) { - plugin.getUserManager().copyColumnData(TopVoter.Monthly.getColumnName(), - TopVoter.Monthly.getLastColumnName()); - } - LocalDateTime lastMonthTime = plugin.getTimeChecker().getTime().minusMonths(1); - if (plugin.getConfigFile().isUseHighestTotals() || plugin.getConfigFile().isUseVoteStreaks()) { - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - if (plugin.getConfigFile().isUseVoteStreaks()) { - if (user.getTotal(TopVoter.Monthly, lastMonthTime) == 0 && user.getMonthVoteStreak() != 0) { - user.setMonthVoteStreak(0); - } else { - if (!plugin.getSpecialRewardsConfig().isVoteStreakRequirementUsePercentage() - || user.hasPercentageTotal(TopVoter.Monthly, - plugin.getSpecialRewardsConfig().getVoteStreakRequirementMonth(), - lastMonthTime)) { - user.addMonthVoteStreak(); - plugin.getSpecialRewards().checkVoteStreak(null, user, "Month", - plugin.getBungeeSettings().isUseBungeecoord()); - } - } - } - - // using new system - // user.setLastMonthTotal(user.getTotal(TopVoter.Monthly)); - - if (plugin.getConfigFile().isUseHighestTotals()) { - if (user.getHighestMonthlyTotal() < user.getTotal(TopVoter.Monthly, lastMonthTime)) { - user.setHighestMonthlyTotal(user.getTotal(TopVoter.Monthly, lastMonthTime)); - } - } - user.clearTempCache(); - }, (count) -> { - // finished - }); - - } - - try { - if (plugin.getSpecialRewardsConfig().isEnableMonthlyAwards()) { - HashMap places = handlePlaces( - plugin.getSpecialRewardsConfig().getMonthlyPossibleRewardPlaces()); - int i = 0; - int lastTotal = -1; - - LinkedHashMap topVoters = null; - if (plugin.getConfigFile().isUseMonthDateTotalsAsPrimaryTotal()) { - topVoters = getMonthlyTopVotersAtTime(lastMonthTime); - } else { - @SuppressWarnings("unchecked") - LinkedHashMap clone = (LinkedHashMap) plugin - .getTopVoter(TopVoter.Monthly).clone(); - topVoters = clone; - } - - for (Entry entry : topVoters.entrySet()) { - if (plugin.getConfigFile().isTopVoterAwardsTies()) { - if (entry.getValue().intValue() != lastTotal) { - i++; - } - } else { - i++; - } - - if (places.containsKey(i)) { - VotingPluginUser user = entry.getKey().getUser(); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { - user.giveMonthlyTopVoterAward(i, places.get(i)); - plugin.getLogger().info("Giving Monthly top voter reward " + i + " to " - + entry.getKey().getPlayerName()); - } - } - lastTotal = entry.getValue().intValue(); - } - - } - } catch (Exception e) { - e.printStackTrace(); - } - - for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { - if (plugin.getShopFile().getVoteShopResetMonthly(shopIdent)) { - resetVoteShopLimit(shopIdent); - } - } - - // give time for other servers to catch up - if (!bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { - plugin.debug("Delaying time change 10 seconds for other servers to catchup"); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - if (!bungeeHandleResets()) { - resetTotals(TopVoter.Monthly); - } - - if (plugin.getStorageType().equals(UserStorage.MYSQL)) { - plugin.getMysql().clearCacheBasic(); - } - long now = ((System.currentTimeMillis() - startTime) / 1000); - plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); - } - } - - /** - * Handles pre-date change events. - * @param event the pre-date changed event - */ - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onPreDateChanged(PreDateChangedEvent event) { - if (event.getTimeType().equals(TimeType.DAY)) { - plugin.getBannedPlayers().clear(); - for (OfflinePlayer p : Bukkit.getBannedPlayers()) { - plugin.getBannedPlayers().add(p.getUniqueId().toString()); - } - } - plugin.setUpdate(true); - plugin.update(); - } - - /** - * Handles week change events. - * - * @param event week change event - */ - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onWeekChange(WeekChangeEvent event) { - long startTime = System.currentTimeMillis(); - synchronized (VotingPluginMain.plugin) { - if (plugin.getConfigFile().isStoreTopVotersWeekly()) { - plugin.getLogger().info("Saving TopVoters Weekly"); - storeTopVoters(TopVoter.Weekly); - } - - plugin.getUserManager().copyColumnData(TopVoter.Weekly.getColumnName(), - TopVoter.Weekly.getLastColumnName()); - if (plugin.getConfigFile().isUseVoteStreaks() || plugin.getConfigFile().isUseHighestTotals()) { - plugin.getUserManager().forEachUserKeys((uuid, columns) -> { - VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - user.updateTempCacheWithColumns(columns); - - if (plugin.getConfigFile().isUseVoteStreaks()) { - if (user.getTotal(TopVoter.Weekly) == 0 && user.getWeekVoteStreak() != 0) { - user.setWeekVoteStreak(0); - } else { - if (!plugin.getSpecialRewardsConfig().isVoteStreakRequirementUsePercentage() - || user.hasPercentageTotal(TopVoter.Weekly, - plugin.getSpecialRewardsConfig().getVoteStreakRequirementWeek(), null)) { - user.addWeekVoteStreak(); - plugin.getSpecialRewards().checkVoteStreak(null, user, "Week", - plugin.getBungeeSettings().isUseBungeecoord()); - } - } - } - - if (plugin.getConfigFile().isUseHighestTotals()) { - if (user.getHighestWeeklyTotal() < user.getTotal(TopVoter.Weekly)) { - user.setHighestWeeklyTotal(user.getTotal(TopVoter.Weekly)); - } - } - user.clearTempCache(); - }, (count) -> { - // finished - }); - } - - try { - if (plugin.getSpecialRewardsConfig().isEnableWeeklyAwards()) { - HashMap places = handlePlaces( - plugin.getSpecialRewardsConfig().getWeeklyPossibleRewardPlaces()); - int i = 0; - int lastTotal = -1; - @SuppressWarnings("unchecked") - LinkedHashMap clone = (LinkedHashMap) plugin - .getTopVoter(TopVoter.Weekly).clone(); - for (Entry entry : clone.entrySet()) { - if (plugin.getConfigFile().isTopVoterAwardsTies()) { - if (entry.getValue().intValue() != lastTotal) { - i++; - } - } else { - i++; - } - if (places.containsKey(i)) { - VotingPluginUser user = entry.getKey().getUser(); - user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); - if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { - user.giveWeeklyTopVoterAward(i, places.get(i)); - plugin.getLogger().info("Giving weekly top voter reward " + i + " to " - + entry.getKey().getPlayerName()); - } - } - lastTotal = entry.getValue().intValue(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { - if (plugin.getShopFile().getVoteShopResetWeekly(shopIdent)) { - resetVoteShopLimit(shopIdent); - } - } - - // give time for other servers to catch up - if (!plugin.getTopVoterHandler().bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { - plugin.debug("Delaying time change 10 seconds for other servers to catchup"); - try { - Thread.sleep(10000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - if (!bungeeHandleResets()) { - resetTotals(TopVoter.Weekly); - } - - if (plugin.getStorageType().equals(UserStorage.MYSQL)) { - plugin.getMysql().clearCacheBasic(); - } - - long now = ((System.currentTimeMillis() - startTime) / 1000); - plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); - } - } - - /** - * Registers this handler as a listener. - */ - public void register() { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - /** - * Resets vote totals for a specific period. - * @param topVoter the top voter period to reset - */ - public void resetTotals(TopVoter topVoter) { - plugin.getUserManager().removeAllKeyValues(topVoter.getColumnName(), DataType.INTEGER); - } - - /** - * Resets vote shop limits for a specific shop. - * @param shopIdent the shop identifier - */ - public void resetVoteShopLimit(String shopIdent) { - plugin.getUserManager().removeAllKeyValues("VoteShopLimit" + shopIdent, DataType.INTEGER); - } - - /** - * Sorts top voters by their vote counts. - * @param map the map to sort - * @param order true for ascending order, false for descending - * @return sorted map of top voters - */ - public LinkedHashMap sortByValues(LinkedHashMap map, - final boolean order) { - - List> list = new LinkedList<>(map.entrySet()); - - // Sorting the list based on values - Collections.sort(list, new Comparator>() { - @Override - public int compare(Entry o1, Entry o2) { - if (order) { - int result = (o1.getValue()).compareTo(o2.getValue()); - if (result != 0) { - return result; - } - return o2.getKey().getLastVoteTime().compareTo(o1.getKey().getLastVoteTime()); - } - int result = (o2.getValue()).compareTo(o1.getValue()); - if (result != 0) { - return result; - } - return o1.getKey().getLastVoteTime().compareTo(o2.getKey().getLastVoteTime()); - } - }); - - // Maintaining insertion order with the help of LinkedList - LinkedHashMap sortedMap = new LinkedHashMap<>(); - for (Entry entry : list) { - sortedMap.put(entry.getKey(), entry.getValue()); - } - - return sortedMap; - } - - /** - * Stores top voter data to file. - * @param top the top voter period to store - */ - public void storeTopVoters(TopVoter top) { - LocalDateTime time = LocalDateTime.now().minusDays(1); - String month = time.getMonth().toString(); - int year = time.getYear(); - int week = time.getDayOfYear(); - int day = time.getDayOfMonth(); - String fileName = "TopVoter" + File.separator + top.toString() + File.separator + year + "_" + month; - - if (top.equals(TopVoter.Daily)) { - fileName += "_" + day; - } else if (top.equals(TopVoter.Weekly)) { - fileName += "_" + week; - } - fileName += "_" + System.currentTimeMillis() + ".yml"; - - YMLFileHandler file = new YMLFileHandler(plugin, new File(plugin.getDataFolder(), fileName)); - file.setup(); - file.header("Saving top voters for " + top.toString() + ", file also contains other top voter info as backup"); - for (TopVoter cTop : TopVoter.values()) { - ArrayList topVoters = new ArrayList<>(); - int cTotal = 0; - try { - ArrayList nums = plugin.getUserManager().getNumbersInColumn(cTop.getColumnName()); - for (Integer num : nums) { - cTotal += num.intValue(); - } - topVoters.add("Combined total: " + cTotal); - } catch (Exception e) { - e.printStackTrace(); - } - if (plugin.getTopVoter().containsKey(cTop)) { - - int count = 1; - for (Entry entry : plugin.getTopVoter(cTop).entrySet()) { - topVoters.add(count + ": " + entry.getKey().getPlayerName() + ": " + entry.getValue()); - count++; - } - file.getData().set(cTop.toString(), topVoters); - } - } - file.saveData(); - } - - /** - * Top voter all time - * - * @param page the page - * @return the string[] - */ - public String[] topVoterAllTime(int page) { - int pagesize = plugin.getConfigFile().getFormatPageSize(); - ArrayList msg = new ArrayList<>(); - ArrayList topVoters = new ArrayList<>(); - int count = 1; - for (Entry entry : plugin.getTopVoter(TopVoter.AllTime).entrySet()) { - String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); - line = line.replace("%num%", "" + count); - line = line.replace("%player%", entry.getKey().getPlayerName()); - line = line.replace("%votes%", "" + entry.getValue()); - topVoters.add(line); - count++; - } - - int pageSize = (topVoters.size() / pagesize); - if ((topVoters.size() % pagesize) != 0) { - pageSize++; - } - - String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); - title = title.replace("%page%", "" + page); - title = title.replace("%maxpages%", "" + pageSize); - title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterAllTime()); - msg.add(MessageAPI.colorize(title)); - - for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { - msg.add(topVoters.get(i)); - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets daily top voters with pagination. - * @param page the page number - * @return array of formatted top voter lines - */ - public String[] topVoterDaily(int page) { - int pagesize = plugin.getConfigFile().getFormatPageSize(); - ArrayList msg = new ArrayList<>(); - ArrayList topVoters = new ArrayList<>(); - int count = 1; - for (Entry entry : plugin.getTopVoter(TopVoter.Daily).entrySet()) { - String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); - line = line.replace("%num%", "" + count); - line = line.replace("%player%", entry.getKey().getPlayerName()); - line = line.replace("%votes%", "" + entry.getValue()); - topVoters.add(line); - count++; - } - - int pageSize = (topVoters.size() / pagesize); - if ((topVoters.size() % pagesize) != 0) { - pageSize++; - } - - String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); - title = title.replace("%page%", "" + page); - title = title.replace("%maxpages%", "" + pageSize); - title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterDaily()); - msg.add(MessageAPI.colorize(title)); - - for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { - msg.add(topVoters.get(i)); - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets monthly top voters with pagination. - * @param page the page number - * @return array of formatted top voter lines - */ - public String[] topVoterMonthly(int page) { - int pagesize = plugin.getConfigFile().getFormatPageSize(); - ArrayList msg = new ArrayList<>(); - ArrayList topVoters = new ArrayList<>(); - int count = 1; - for (Entry entry : plugin.getTopVoter(TopVoter.Monthly).entrySet()) { - String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); - line = line.replace("%num%", "" + count); - line = line.replace("%player%", entry.getKey().getPlayerName()); - line = line.replace("%votes%", "" + entry.getValue()); - topVoters.add(line); - count++; - } - - int pageSize = (topVoters.size() / pagesize); - if ((topVoters.size() % pagesize) != 0) { - pageSize++; - } - - String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); - title = title.replace("%page%", "" + page); - title = title.replace("%maxpages%", "" + pageSize); - title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterMonthly()); - msg.add(MessageAPI.colorize(title)); - - for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { - msg.add(topVoters.get(i)); - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets all-time top voters as formatted strings. - * @return array of formatted top voter lines - */ - public String[] topVotersAllTime() { - ArrayList msg = new ArrayList<>(); - List> list = new LinkedList<>(plugin.getTopVoter(TopVoter.AllTime).entrySet()); - int i = 0; - for (Entry entry : list) { - String line = "%num%: %player%, %votes%"; - line = line.replace("%num%", "" + (i + 1)); - try { - line = line.replace("%player%", entry.getKey().getPlayerName()); - } catch (Exception ex) { - VotingPluginMain.plugin.debug(ex); - } - line = line.replace("%votes%", "" + entry.getValue()); - - msg.add(line); - i++; - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets daily top voters as formatted strings. - * @return array of formatted top voter lines - */ - public String[] topVotersDaily() { - ArrayList msg = new ArrayList<>(); - ArrayList users = plugin.convertSet(plugin.getTopVoter(TopVoter.Daily).keySet()); - for (int i = 0; i < users.size(); i++) { - String line = "%num%: %player%, %votes%"; - line = line.replace("%num%", "" + (i + 1)); - try { - line = line.replace("%player%", users.get(i).getPlayerName()); - } catch (Exception ex) { - VotingPluginMain.plugin.debug(ex); - } - line = line.replace("%votes%", "" + plugin.getTopVoter(TopVoter.Monthly).get(users.get(i))); - msg.add(line); - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets monthly top voters as formatted strings. - * @return array of formatted top voter lines - */ - public String[] topVotersMonthly() { - ArrayList msg = new ArrayList<>(); - List> list = new LinkedList<>(plugin.getTopVoter(TopVoter.Monthly).entrySet()); - int i = 0; - for (Entry entry : list) { - String line = "%num%: %player%, %votes%"; - line = line.replace("%num%", "" + (i + 1)); - try { - line = line.replace("%player%", entry.getKey().getPlayerName()); - } catch (Exception ex) { - VotingPluginMain.plugin.debug(ex); - } - line = line.replace("%votes%", "" + entry.getValue()); - - msg.add(line); - i++; - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Gets weekly top voters with pagination. - * @param page the page number - * @return array of formatted top voter lines - */ - public String[] topVoterWeekly(int page) { - int pagesize = plugin.getConfigFile().getFormatPageSize(); - ArrayList msg = new ArrayList<>(); - ArrayList topVoters = new ArrayList<>(); - int count = 1; - for (Entry entry : plugin.getTopVoter(TopVoter.Weekly).entrySet()) { - String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); - line = line.replace("%num%", "" + count); - line = line.replace("%player%", entry.getKey().getPlayerName()); - line = line.replace("%votes%", "" + entry.getValue()); - topVoters.add(line); - count++; - } - - int pageSize = (topVoters.size() / pagesize); - if ((topVoters.size() % pagesize) != 0) { - pageSize++; - } - - String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); - title = title.replace("%page%", "" + page); - title = title.replace("%maxpages%", "" + pageSize); - title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterWeekly()); - msg.add(MessageAPI.colorize(title)); - - for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { - msg.add(topVoters.get(i)); - } - - msg = ArrayUtils.colorize(msg); - return ArrayUtils.convert(msg); - } - - /** - * Updates top voter rankings for a specific period. - * @param tempTopVoter map of top voter periods to player rankings - */ - public synchronized void updateTopVoters( - LinkedHashMap> tempTopVoter) { - - int limitSize = plugin.getConfigFile().getMaxiumNumberOfTopVotersToLoad(); - for (Entry> entry : tempTopVoter.entrySet()) { - LinkedHashMap map = entry.getValue(); - map = sortByValues(map, false); - if (limitSize > 0) { - ArrayList listKeys = new ArrayList<>(map.keySet()); - if (listKeys.size() > limitSize) { - for (int i = listKeys.size() - 1; i >= 0 && i >= limitSize; i--) { - map.remove(listKeys.get(i)); - } - } - - } - tempTopVoter.put(entry.getKey(), map); - plugin.setTopVoter(tempTopVoter); - plugin.debug(entry.getKey().getName() + " TopVoter loaded, number of players " + map.size()); - - } - - plugin.debug("Updated TopVoter"); - } -} +package com.bencodez.votingplugin.topvoter; + +import java.io.File; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import com.bencodez.advancedcore.api.time.TimeType; +import com.bencodez.advancedcore.api.time.events.DateChangedEvent; +import com.bencodez.advancedcore.api.time.events.DayChangeEvent; +import com.bencodez.advancedcore.api.time.events.MonthChangeEvent; +import com.bencodez.advancedcore.api.time.events.PreDateChangedEvent; +import com.bencodez.advancedcore.api.time.events.WeekChangeEvent; +import com.bencodez.advancedcore.api.user.UserDataFetchMode; +import com.bencodez.advancedcore.api.user.UserStorage; +import com.bencodez.simpleapi.array.ArrayUtils; +import com.bencodez.simpleapi.file.YMLFileHandler; +import com.bencodez.simpleapi.messages.MessageAPI; +import com.bencodez.simpleapi.sql.Column; +import com.bencodez.simpleapi.sql.DataType; +import com.bencodez.votingplugin.VotingPluginMain; +import com.bencodez.votingplugin.user.VotingPluginUser; + +/** + * Handles top voter rankings and statistics. + */ +public class TopVoterHandler implements Listener { + + private VotingPluginMain plugin; + private final TopVoterLoader loader; + + /** + * Constructs a new top voter handler. + * @param plugin the plugin instance + */ + public TopVoterHandler(VotingPluginMain plugin) { + this.plugin = plugin; + this.loader = new TopVoterLoader(plugin); + } + + /** + * Checks if bungee should handle resets. + * @return true if bungee handles resets + */ + public boolean bungeeHandleResets() { + if (plugin.getBungeeSettings().isUseBungeecoord()) { + if (plugin.getBungeeSettings().isGloblalDataEnabled()) { + return true; + } + } + + return false; + } + + /** + * Gets monthly top voters at a specific time. + * @param atTime the time to check + * @return map of top voters and their vote counts + */ + public LinkedHashMap getMonthlyTopVotersAtTime(LocalDateTime atTime) { + return loader.getMonthlyTopVotersAtTime(atTime); + } + + + /** + * Gets the top voter blacklist. + * @return list of blacklisted players + */ + public ArrayList getTopVoterBlackList() { + return plugin.getConfigFile().getBlackList(); + } + + /** + * Gets top voters for a specific month. + * @param month the year-month to check + * @param cols player data columns + * @return map of top voters and their vote counts + */ + public LinkedHashMap getTopVotersOfMonth(YearMonth month, + HashMap> cols) { + return loader.getTopVotersOfMonth(month, cols); + } + + + /** + * Top voters weekly. + * + * @return the string[] + */ + + /** + * Gets weekly top voters as formatted strings. + * @return array of formatted top voter lines + */ + public String[] getTopVotersWeekly() { + ArrayList msg = new ArrayList<>(); + ArrayList users = new ArrayList<>(plugin.getTopVoter(TopVoter.Weekly).keySet()); + for (int i = 0; i < users.size(); i++) { + String line = plugin.getConfigFile().getFormatCommandsVoteTopLine().replace("%num%", "" + (i + 1)) + .replace("%player%", users.get(i).getPlayerName()) + .replace("%votes%", "" + plugin.getTopVoter(TopVoter.Weekly).get(users.get(i))); + msg.add(line); + } + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + private HashMap handlePlaces(Set places) { + return TopVoterRanking.mapAwardPlaces(places); + } + + + /** + * Loads last month's top voter data. + */ + public void loadLastMonth() { + loader.loadLastMonth(); + } + + + /** + * Loads previous month top voters for all configured months. + */ + public void loadPreviousMonthTopVoters() { + loader.loadPreviousMonthTopVoters(); + } + + + /** + * Handles date change events. + * + * @param event date changed event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onDateChanged(DateChangedEvent event) { + plugin.setUpdate(true); + plugin.update(); + if (event.getTimeType().equals(TimeType.MONTH)) { + loadLastMonth(); + } + if (plugin.getStorageType().equals(UserStorage.MYSQL)) { + plugin.getMysql().clearCacheBasic(); + } + } + + /** + * Handles day change events. + * @param event the day change event + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onDayChange(DayChangeEvent event) { + synchronized (VotingPluginMain.plugin) { + long startTime = System.currentTimeMillis(); + if (plugin.getConfigFile().isStoreTopVotersDaily()) { + plugin.getLogger().info("Saving TopVoters Daily"); + storeTopVoters(TopVoter.Daily); + } + + plugin.getUserManager().copyColumnData(TopVoter.Daily.getColumnName(), TopVoter.Daily.getLastColumnName()); + if (plugin.getConfigFile().isUseVoteStreaks() || plugin.getConfigFile().isUseHighestTotals()) { + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + + if (plugin.getConfigFile().isUseVoteStreaks()) { + if (!user.voteStreakUpdatedToday(LocalDateTime.now().minusDays(1))) { + if (user.getDayVoteStreak() != 0) { + user.setDayVoteStreak(0); + } + } + } + + if (plugin.getConfigFile().isUseHighestTotals()) { + if (user.getHighestDailyTotal() < user.getTotal(TopVoter.Daily)) { + user.setHighestDailyTotal(user.getTotal(TopVoter.Daily)); + } + } + user.clearTempCache(); + }, (count) -> { + // finished + }); + } + + try { + if (plugin.getSpecialRewardsConfig().isEnableDailyRewards()) { + HashMap places = handlePlaces( + plugin.getSpecialRewardsConfig().getDailyPossibleRewardPlaces()); + int i = 0; + int lastTotal = -1; + @SuppressWarnings("unchecked") + LinkedHashMap clone = (LinkedHashMap) plugin + .getTopVoter(TopVoter.Daily).clone(); + for (Entry entry : clone.entrySet()) { + if (plugin.getConfigFile().isTopVoterAwardsTies()) { + if (entry.getValue().intValue() != lastTotal) { + i++; + } + } else { + i++; + } + if (places.containsKey(i)) { + VotingPluginUser user = entry.getKey().getUser(); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { + user.giveDailyTopVoterAward(i, places.get(i)); + plugin.getLogger().info( + "Giving daily top voter reward " + i + " to " + entry.getKey().getPlayerName()); + } + } + lastTotal = entry.getValue().intValue(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { + if (plugin.getShopFile().getVoteShopResetDaily(shopIdent)) { + resetVoteShopLimit(shopIdent); + } + } + + // give time for other servers to catch up + if (!plugin.getTopVoterHandler().bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { + plugin.debug("Delaying time change 10 seconds for other servers to catchup"); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (!bungeeHandleResets()) { + resetTotals(TopVoter.Daily); + } + + if (plugin.getStorageType().equals(UserStorage.MYSQL)) { + plugin.getMysql().clearCacheBasic(); + } + + long now = ((System.currentTimeMillis() - startTime) / 1000); + plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); + } + } + + /** + * Handles month change events. + * @param event the month change event + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onMonthChange(MonthChangeEvent event) { + long startTime = System.currentTimeMillis(); + synchronized (VotingPluginMain.plugin) { + plugin.getLogger().info("Saving TopVoters Monthly"); + storeTopVoters(TopVoter.Monthly); + if (!bungeeHandleResets()) { + plugin.getUserManager().copyColumnData(TopVoter.Monthly.getColumnName(), + TopVoter.Monthly.getLastColumnName()); + } + LocalDateTime lastMonthTime = plugin.getTimeChecker().getTime().minusMonths(1); + if (plugin.getConfigFile().isUseHighestTotals() || plugin.getConfigFile().isUseVoteStreaks()) { + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + + if (plugin.getConfigFile().isUseVoteStreaks()) { + if (user.getTotal(TopVoter.Monthly, lastMonthTime) == 0 && user.getMonthVoteStreak() != 0) { + user.setMonthVoteStreak(0); + } else { + if (!plugin.getSpecialRewardsConfig().isVoteStreakRequirementUsePercentage() + || user.hasPercentageTotal(TopVoter.Monthly, + plugin.getSpecialRewardsConfig().getVoteStreakRequirementMonth(), + lastMonthTime)) { + user.addMonthVoteStreak(); + plugin.getSpecialRewards().checkVoteStreak(null, user, "Month", + plugin.getBungeeSettings().isUseBungeecoord()); + } + } + } + + // using new system + // user.setLastMonthTotal(user.getTotal(TopVoter.Monthly)); + + if (plugin.getConfigFile().isUseHighestTotals()) { + if (user.getHighestMonthlyTotal() < user.getTotal(TopVoter.Monthly, lastMonthTime)) { + user.setHighestMonthlyTotal(user.getTotal(TopVoter.Monthly, lastMonthTime)); + } + } + user.clearTempCache(); + }, (count) -> { + // finished + }); + + } + + try { + if (plugin.getSpecialRewardsConfig().isEnableMonthlyAwards()) { + HashMap places = handlePlaces( + plugin.getSpecialRewardsConfig().getMonthlyPossibleRewardPlaces()); + int i = 0; + int lastTotal = -1; + + LinkedHashMap topVoters = null; + if (plugin.getConfigFile().isUseMonthDateTotalsAsPrimaryTotal()) { + topVoters = getMonthlyTopVotersAtTime(lastMonthTime); + } else { + @SuppressWarnings("unchecked") + LinkedHashMap clone = (LinkedHashMap) plugin + .getTopVoter(TopVoter.Monthly).clone(); + topVoters = clone; + } + + for (Entry entry : topVoters.entrySet()) { + if (plugin.getConfigFile().isTopVoterAwardsTies()) { + if (entry.getValue().intValue() != lastTotal) { + i++; + } + } else { + i++; + } + + if (places.containsKey(i)) { + VotingPluginUser user = entry.getKey().getUser(); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { + user.giveMonthlyTopVoterAward(i, places.get(i)); + plugin.getLogger().info("Giving Monthly top voter reward " + i + " to " + + entry.getKey().getPlayerName()); + } + } + lastTotal = entry.getValue().intValue(); + } + + } + } catch (Exception e) { + e.printStackTrace(); + } + + for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { + if (plugin.getShopFile().getVoteShopResetMonthly(shopIdent)) { + resetVoteShopLimit(shopIdent); + } + } + + // give time for other servers to catch up + if (!bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { + plugin.debug("Delaying time change 10 seconds for other servers to catchup"); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (!bungeeHandleResets()) { + resetTotals(TopVoter.Monthly); + } + + if (plugin.getStorageType().equals(UserStorage.MYSQL)) { + plugin.getMysql().clearCacheBasic(); + } + long now = ((System.currentTimeMillis() - startTime) / 1000); + plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); + } + } + + /** + * Handles pre-date change events. + * @param event the pre-date changed event + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPreDateChanged(PreDateChangedEvent event) { + if (event.getTimeType().equals(TimeType.DAY)) { + plugin.getBannedPlayers().clear(); + for (OfflinePlayer p : Bukkit.getBannedPlayers()) { + plugin.getBannedPlayers().add(p.getUniqueId().toString()); + } + } + plugin.setUpdate(true); + plugin.update(); + } + + /** + * Handles week change events. + * + * @param event week change event + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onWeekChange(WeekChangeEvent event) { + long startTime = System.currentTimeMillis(); + synchronized (VotingPluginMain.plugin) { + if (plugin.getConfigFile().isStoreTopVotersWeekly()) { + plugin.getLogger().info("Saving TopVoters Weekly"); + storeTopVoters(TopVoter.Weekly); + } + + plugin.getUserManager().copyColumnData(TopVoter.Weekly.getColumnName(), + TopVoter.Weekly.getLastColumnName()); + if (plugin.getConfigFile().isUseVoteStreaks() || plugin.getConfigFile().isUseHighestTotals()) { + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + + if (plugin.getConfigFile().isUseVoteStreaks()) { + if (user.getTotal(TopVoter.Weekly) == 0 && user.getWeekVoteStreak() != 0) { + user.setWeekVoteStreak(0); + } else { + if (!plugin.getSpecialRewardsConfig().isVoteStreakRequirementUsePercentage() + || user.hasPercentageTotal(TopVoter.Weekly, + plugin.getSpecialRewardsConfig().getVoteStreakRequirementWeek(), null)) { + user.addWeekVoteStreak(); + plugin.getSpecialRewards().checkVoteStreak(null, user, "Week", + plugin.getBungeeSettings().isUseBungeecoord()); + } + } + } + + if (plugin.getConfigFile().isUseHighestTotals()) { + if (user.getHighestWeeklyTotal() < user.getTotal(TopVoter.Weekly)) { + user.setHighestWeeklyTotal(user.getTotal(TopVoter.Weekly)); + } + } + user.clearTempCache(); + }, (count) -> { + // finished + }); + } + + try { + if (plugin.getSpecialRewardsConfig().isEnableWeeklyAwards()) { + HashMap places = handlePlaces( + plugin.getSpecialRewardsConfig().getWeeklyPossibleRewardPlaces()); + int i = 0; + int lastTotal = -1; + @SuppressWarnings("unchecked") + LinkedHashMap clone = (LinkedHashMap) plugin + .getTopVoter(TopVoter.Weekly).clone(); + for (Entry entry : clone.entrySet()) { + if (plugin.getConfigFile().isTopVoterAwardsTies()) { + if (entry.getValue().intValue() != lastTotal) { + i++; + } + } else { + i++; + } + if (places.containsKey(i)) { + VotingPluginUser user = entry.getKey().getUser(); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + if (!plugin.getConfigFile().isTopVoterIgnorePermission() || !user.isTopVoterIgnore()) { + user.giveWeeklyTopVoterAward(i, places.get(i)); + plugin.getLogger().info("Giving weekly top voter reward " + i + " to " + + entry.getKey().getPlayerName()); + } + } + lastTotal = entry.getValue().intValue(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + for (String shopIdent : plugin.getShopFile().getShopIdentifiers()) { + if (plugin.getShopFile().getVoteShopResetWeekly(shopIdent)) { + resetVoteShopLimit(shopIdent); + } + } + + // give time for other servers to catch up + if (!plugin.getTopVoterHandler().bungeeHandleResets() && plugin.getBungeeSettings().isUseBungeecoord()) { + plugin.debug("Delaying time change 10 seconds for other servers to catchup"); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (!bungeeHandleResets()) { + resetTotals(TopVoter.Weekly); + } + + if (plugin.getStorageType().equals(UserStorage.MYSQL)) { + plugin.getMysql().clearCacheBasic(); + } + + long now = ((System.currentTimeMillis() - startTime) / 1000); + plugin.getLogger().info("Finished processing day change, took " + now + " seconds"); + } + } + + /** + * Registers this handler as a listener. + */ + public void register() { + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + /** + * Resets vote totals for a specific period. + * @param topVoter the top voter period to reset + */ + public void resetTotals(TopVoter topVoter) { + plugin.getUserManager().removeAllKeyValues(topVoter.getColumnName(), DataType.INTEGER); + } + + /** + * Resets vote shop limits for a specific shop. + * @param shopIdent the shop identifier + */ + public void resetVoteShopLimit(String shopIdent) { + plugin.getUserManager().removeAllKeyValues("VoteShopLimit" + shopIdent, DataType.INTEGER); + } + + /** + * Sorts top voters by their vote counts. + * @param map the map to sort + * @param order true for ascending order, false for descending + * @return sorted map of top voters + */ + public LinkedHashMap sortByValues(LinkedHashMap map, + final boolean order) { + return TopVoterRanking.sortByValues(map, order); + } + + + /** + * Stores top voter data to file. + * @param top the top voter period to store + */ + public void storeTopVoters(TopVoter top) { + LocalDateTime time = LocalDateTime.now().minusDays(1); + String month = time.getMonth().toString(); + int year = time.getYear(); + int week = time.getDayOfYear(); + int day = time.getDayOfMonth(); + String fileName = "TopVoter" + File.separator + top.toString() + File.separator + year + "_" + month; + + if (top.equals(TopVoter.Daily)) { + fileName += "_" + day; + } else if (top.equals(TopVoter.Weekly)) { + fileName += "_" + week; + } + fileName += "_" + System.currentTimeMillis() + ".yml"; + + YMLFileHandler file = new YMLFileHandler(plugin, new File(plugin.getDataFolder(), fileName)); + file.setup(); + file.header("Saving top voters for " + top.toString() + ", file also contains other top voter info as backup"); + for (TopVoter cTop : TopVoter.values()) { + ArrayList topVoters = new ArrayList<>(); + int cTotal = 0; + try { + ArrayList nums = plugin.getUserManager().getNumbersInColumn(cTop.getColumnName()); + for (Integer num : nums) { + cTotal += num.intValue(); + } + topVoters.add("Combined total: " + cTotal); + } catch (Exception e) { + e.printStackTrace(); + } + if (plugin.getTopVoter().containsKey(cTop)) { + + int count = 1; + for (Entry entry : plugin.getTopVoter(cTop).entrySet()) { + topVoters.add(count + ": " + entry.getKey().getPlayerName() + ": " + entry.getValue()); + count++; + } + file.getData().set(cTop.toString(), topVoters); + } + } + file.saveData(); + } + + /** + * Top voter all time + * + * @param page the page + * @return the string[] + */ + public String[] topVoterAllTime(int page) { + int pagesize = plugin.getConfigFile().getFormatPageSize(); + ArrayList msg = new ArrayList<>(); + ArrayList topVoters = new ArrayList<>(); + int count = 1; + for (Entry entry : plugin.getTopVoter(TopVoter.AllTime).entrySet()) { + String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); + line = line.replace("%num%", "" + count); + line = line.replace("%player%", entry.getKey().getPlayerName()); + line = line.replace("%votes%", "" + entry.getValue()); + topVoters.add(line); + count++; + } + + int pageSize = (topVoters.size() / pagesize); + if ((topVoters.size() % pagesize) != 0) { + pageSize++; + } + + String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); + title = title.replace("%page%", "" + page); + title = title.replace("%maxpages%", "" + pageSize); + title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterAllTime()); + msg.add(MessageAPI.colorize(title)); + + for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { + msg.add(topVoters.get(i)); + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets daily top voters with pagination. + * @param page the page number + * @return array of formatted top voter lines + */ + public String[] topVoterDaily(int page) { + int pagesize = plugin.getConfigFile().getFormatPageSize(); + ArrayList msg = new ArrayList<>(); + ArrayList topVoters = new ArrayList<>(); + int count = 1; + for (Entry entry : plugin.getTopVoter(TopVoter.Daily).entrySet()) { + String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); + line = line.replace("%num%", "" + count); + line = line.replace("%player%", entry.getKey().getPlayerName()); + line = line.replace("%votes%", "" + entry.getValue()); + topVoters.add(line); + count++; + } + + int pageSize = (topVoters.size() / pagesize); + if ((topVoters.size() % pagesize) != 0) { + pageSize++; + } + + String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); + title = title.replace("%page%", "" + page); + title = title.replace("%maxpages%", "" + pageSize); + title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterDaily()); + msg.add(MessageAPI.colorize(title)); + + for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { + msg.add(topVoters.get(i)); + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets monthly top voters with pagination. + * @param page the page number + * @return array of formatted top voter lines + */ + public String[] topVoterMonthly(int page) { + int pagesize = plugin.getConfigFile().getFormatPageSize(); + ArrayList msg = new ArrayList<>(); + ArrayList topVoters = new ArrayList<>(); + int count = 1; + for (Entry entry : plugin.getTopVoter(TopVoter.Monthly).entrySet()) { + String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); + line = line.replace("%num%", "" + count); + line = line.replace("%player%", entry.getKey().getPlayerName()); + line = line.replace("%votes%", "" + entry.getValue()); + topVoters.add(line); + count++; + } + + int pageSize = (topVoters.size() / pagesize); + if ((topVoters.size() % pagesize) != 0) { + pageSize++; + } + + String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); + title = title.replace("%page%", "" + page); + title = title.replace("%maxpages%", "" + pageSize); + title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterMonthly()); + msg.add(MessageAPI.colorize(title)); + + for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { + msg.add(topVoters.get(i)); + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets all-time top voters as formatted strings. + * @return array of formatted top voter lines + */ + public String[] topVotersAllTime() { + ArrayList msg = new ArrayList<>(); + List> list = new LinkedList<>(plugin.getTopVoter(TopVoter.AllTime).entrySet()); + int i = 0; + for (Entry entry : list) { + String line = "%num%: %player%, %votes%"; + line = line.replace("%num%", "" + (i + 1)); + try { + line = line.replace("%player%", entry.getKey().getPlayerName()); + } catch (Exception ex) { + VotingPluginMain.plugin.debug(ex); + } + line = line.replace("%votes%", "" + entry.getValue()); + + msg.add(line); + i++; + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets daily top voters as formatted strings. + * @return array of formatted top voter lines + */ + public String[] topVotersDaily() { + ArrayList msg = new ArrayList<>(); + ArrayList users = new ArrayList<>(plugin.getTopVoter(TopVoter.Daily).keySet()); + for (int i = 0; i < users.size(); i++) { + String line = "%num%: %player%, %votes%"; + line = line.replace("%num%", "" + (i + 1)); + try { + line = line.replace("%player%", users.get(i).getPlayerName()); + } catch (Exception ex) { + VotingPluginMain.plugin.debug(ex); + } + line = line.replace("%votes%", "" + plugin.getTopVoter(TopVoter.Monthly).get(users.get(i))); + msg.add(line); + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets monthly top voters as formatted strings. + * @return array of formatted top voter lines + */ + public String[] topVotersMonthly() { + ArrayList msg = new ArrayList<>(); + List> list = new LinkedList<>(plugin.getTopVoter(TopVoter.Monthly).entrySet()); + int i = 0; + for (Entry entry : list) { + String line = "%num%: %player%, %votes%"; + line = line.replace("%num%", "" + (i + 1)); + try { + line = line.replace("%player%", entry.getKey().getPlayerName()); + } catch (Exception ex) { + VotingPluginMain.plugin.debug(ex); + } + line = line.replace("%votes%", "" + entry.getValue()); + + msg.add(line); + i++; + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Gets weekly top voters with pagination. + * @param page the page number + * @return array of formatted top voter lines + */ + public String[] topVoterWeekly(int page) { + int pagesize = plugin.getConfigFile().getFormatPageSize(); + ArrayList msg = new ArrayList<>(); + ArrayList topVoters = new ArrayList<>(); + int count = 1; + for (Entry entry : plugin.getTopVoter(TopVoter.Weekly).entrySet()) { + String line = plugin.getConfigFile().getFormatCommandsVoteTopLine(); + line = line.replace("%num%", "" + count); + line = line.replace("%player%", entry.getKey().getPlayerName()); + line = line.replace("%votes%", "" + entry.getValue()); + topVoters.add(line); + count++; + } + + int pageSize = (topVoters.size() / pagesize); + if ((topVoters.size() % pagesize) != 0) { + pageSize++; + } + + String title = plugin.getConfigFile().getFormatCommandVoteTopTitle(); + title = title.replace("%page%", "" + page); + title = title.replace("%maxpages%", "" + pageSize); + title = title.replace("%Top%", plugin.getConfigFile().getFormatTopVoterWeekly()); + msg.add(MessageAPI.colorize(title)); + + for (int i = (page - 1) * pagesize; (i < topVoters.size()) && (i < (((page - 1) * pagesize) + 10)); i++) { + msg.add(topVoters.get(i)); + } + + msg = ArrayUtils.colorize(msg); + return ArrayUtils.convert(msg); + } + + /** + * Updates top voter rankings for a specific period. + * @param tempTopVoter map of top voter periods to player rankings + */ + public synchronized void updateTopVoters( + LinkedHashMap> tempTopVoter) { + + int limitSize = plugin.getConfigFile().getMaxiumNumberOfTopVotersToLoad(); + for (Entry> entry : tempTopVoter.entrySet()) { + LinkedHashMap map = entry.getValue(); + map = sortByValues(map, false); + if (limitSize > 0) { + ArrayList listKeys = new ArrayList<>(map.keySet()); + if (listKeys.size() > limitSize) { + for (int i = listKeys.size() - 1; i >= 0 && i >= limitSize; i--) { + map.remove(listKeys.get(i)); + } + } + + } + tempTopVoter.put(entry.getKey(), map); + plugin.setTopVoter(tempTopVoter); + plugin.debug(entry.getKey().getName() + " TopVoter loaded, number of players " + map.size()); + + } + + plugin.debug("Updated TopVoter"); + } +} diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterLoader.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterLoader.java new file mode 100644 index 0000000000..c6c6485d0b --- /dev/null +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterLoader.java @@ -0,0 +1,208 @@ +package com.bencodez.votingplugin.topvoter; + +import java.time.LocalDateTime; +import java.time.Month; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.bencodez.advancedcore.api.user.UserDataFetchMode; +import com.bencodez.simpleapi.messages.MessageAPI; +import com.bencodez.simpleapi.sql.Column; +import com.bencodez.votingplugin.VotingPluginMain; +import com.bencodez.votingplugin.user.VotingPluginUser; + +/** Loads top-voter rankings from user storage. */ +public class TopVoterLoader { + + private final VotingPluginMain plugin; + + public TopVoterLoader(VotingPluginMain plugin) { + this.plugin = plugin; + } + + public LinkedHashMap getMonthlyTopVotersAtTime(LocalDateTime atTime) { + LinkedHashMap topVoters = new LinkedHashMap<>(); + CountDownLatch latch = new CountDownLatch(1); + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + if (plugin != null && plugin.isEnabled() && uuid != null) { + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + int total = user.getTotal(TopVoter.Monthly, atTime); + if (total > 0) { + topVoters.put(user.getTopVoterPlayer(), total); + } + user.clearTempCache(); + } + }, count -> latch.countDown()); + + try { + latch.await(10, TimeUnit.MINUTES); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return TopVoterRanking.sortByValues(topVoters, false); + } + + public LinkedHashMap getTopVotersOfMonth(YearMonth month, + HashMap> columnsByPlayer) { + LinkedHashMap totals = new LinkedHashMap<>(); + LocalDateTime atTime = month.atDay(15).atTime(0, 0); + for (Entry> playerData : columnsByPlayer.entrySet()) { + UUID uuid = playerData.getKey(); + if (uuid == null) { + continue; + } + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(playerData.getValue()); + int total = user.getTotal(TopVoter.Monthly, atTime); + if (total > 0) { + totals.put(user.getTopVoterPlayer(), total); + } + user.clearTempCache(); + } + return TopVoterRanking.sortByValues(totals, false); + } + + public void loadLastMonth() { + if (!plugin.getGui().isLastMonthGUI()) { + return; + } + plugin.getTopVoterState().getLastMonthTopVoters().clear(); + HashMap totals = new HashMap<>(); + LocalDateTime lastMonthTime = plugin.getTimeChecker().getTime().minusMonths(1); + boolean useDateTotalsPrimary = plugin.getConfigFile().isUseMonthDateTotalsAsPrimaryTotal(); + + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + if (uuid == null || plugin == null || !plugin.isEnabled()) { + return; + } + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + try { + int total = useDateTotalsPrimary ? user.getTotal(TopVoter.Monthly, lastMonthTime) + : user.getLastMonthTotal(); + if (total > 0) { + totals.put(user.getTopVoterPlayer(), total); + } + } finally { + user.clearTempCache(); + } + }, count -> { + plugin.getTopVoterState().getLastMonthTopVoters() + .putAll(TopVoterRanking.sortByValues(new LinkedHashMap<>(totals), false)); + plugin.debug("Loaded last month top voters"); + }); + } + + public void loadPreviousMonthTopVoters() { + if (!plugin.getConfigFile().isStoreMonthTotalsWithDate()) { + return; + } + + LocalDateTime now = plugin.getTimeChecker().getTime(); + YearMonth currentMonth = YearMonth.of(now.getYear(), now.getMonth()); + ArrayList months = new ArrayList<>(); + ArrayList monthColumns = new ArrayList<>(); + + for (String column : plugin.getUserManager().getAllColumns()) { + YearMonth month = parseMonthColumn(column); + if (month != null && month.isBefore(currentMonth)) { + months.add(month); + monthColumns.add(column); + } + } + if (months.isEmpty()) { + return; + } + + Integer[] order = new Integer[months.size()]; + for (int index = 0; index < order.length; index++) { + order[index] = index; + } + java.util.Arrays.sort(order, (left, right) -> months.get(left).compareTo(months.get(right))); + ArrayList sortedMonths = new ArrayList<>(months.size()); + ArrayList sortedColumns = new ArrayList<>(months.size()); + for (Integer index : order) { + sortedMonths.add(months.get(index)); + sortedColumns.add(monthColumns.get(index)); + } + months.clear(); + months.addAll(sortedMonths); + monthColumns.clear(); + monthColumns.addAll(sortedColumns); + + LinkedHashMap> previous = plugin.getTopVoterState() + .getPreviousMonthsTopVoters(); + previous.clear(); + for (YearMonth month : months) { + plugin.debug("Loading previous month top voters of " + month); + previous.put(month, new LinkedHashMap<>()); + } + + @SuppressWarnings("unchecked") + LinkedHashMap[] monthMaps = new LinkedHashMap[months.size()]; + for (int index = 0; index < months.size(); index++) { + monthMaps[index] = previous.get(months.get(index)); + } + String[] monthColumnNames = monthColumns.toArray(new String[0]); + + plugin.getUserManager().forEachUserKeys((uuid, columns) -> { + if (uuid == null) { + return; + } + VotingPluginUser user = plugin.getVotingPluginUserManager().getVotingPluginUser(uuid, false); + user.userDataFetechMode(UserDataFetchMode.TEMP_ONLY); + user.updateTempCacheWithColumns(columns); + try { + TopVoterPlayer player = null; + for (int index = 0; index < monthColumnNames.length; index++) { + int total = user.getData().getInt(monthColumnNames[index], 0, UserDataFetchMode.TEMP_ONLY); + if (total > 0) { + if (player == null) { + player = user.getTopVoterPlayer(); + } + monthMaps[index].put(player, total); + } + } + } finally { + user.clearTempCache(); + } + }, count -> { + for (int index = 0; index < months.size(); index++) { + YearMonth month = months.get(index); + previous.put(month, TopVoterRanking.sortByValues(previous.get(month), false)); + } + plugin.extraDebug("Previous Months: " + previous.keySet()); + }); + } + + static YearMonth parseMonthColumn(String column) { + if (column == null || !column.startsWith("MonthTotal-")) { + return null; + } + int firstDash = column.indexOf('-'); + int secondDash = column.indexOf('-', firstDash + 1); + if (firstDash < 0 || secondDash < 0) { + return null; + } + String month = column.substring(firstDash + 1, secondDash); + String year = column.substring(secondDash + 1); + if (!MessageAPI.isInt(year)) { + return null; + } + try { + return YearMonth.of(Integer.parseInt(year), Month.valueOf(month)); + } catch (RuntimeException ignored) { + return null; + } + } +} diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterRanking.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterRanking.java new file mode 100644 index 0000000000..49b0cdc5a7 --- /dev/null +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterRanking.java @@ -0,0 +1,55 @@ +package com.bencodez.votingplugin.topvoter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +/** Ranking utilities shared by top-voter loading and reward handling. */ +public final class TopVoterRanking { + + private TopVoterRanking() { + } + + public static LinkedHashMap sortByValues( + LinkedHashMap map, boolean ascending) { + List> entries = new ArrayList<>(map.entrySet()); + entries.sort((first, second) -> { + int result = ascending ? first.getValue().compareTo(second.getValue()) + : second.getValue().compareTo(first.getValue()); + if (result != 0) { + return result; + } + return ascending + ? second.getKey().getLastVoteTime().compareTo(first.getKey().getLastVoteTime()) + : first.getKey().getLastVoteTime().compareTo(second.getKey().getLastVoteTime()); + }); + + LinkedHashMap sorted = new LinkedHashMap<>(); + for (Entry entry : entries) { + sorted.put(entry.getKey(), entry.getValue()); + } + return sorted; + } + + public static HashMap mapAwardPlaces(Set places) { + HashMap mapped = new HashMap<>(); + for (String place : places) { + String[] data = place.split("-"); + try { + if (data.length > 1) { + for (int index = Integer.parseInt(data[0]); index < Integer.parseInt(data[1]); index++) { + mapped.put(index, place); + } + } else { + mapped.put(Integer.parseInt(data[0]), place); + } + } catch (RuntimeException e) { + e.printStackTrace(); + } + } + return mapped; + } +} diff --git a/VotingPlugin/src/test/java/com/bencodez/votingplugin/topvoter/TopVoterLoaderTest.java b/VotingPlugin/src/test/java/com/bencodez/votingplugin/topvoter/TopVoterLoaderTest.java new file mode 100644 index 0000000000..2fd872f13f --- /dev/null +++ b/VotingPlugin/src/test/java/com/bencodez/votingplugin/topvoter/TopVoterLoaderTest.java @@ -0,0 +1,25 @@ +package com.bencodez.votingplugin.topvoter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.time.YearMonth; + +import org.junit.jupiter.api.Test; + +public class TopVoterLoaderTest { + + @Test + public void parsesStoredMonthColumns() { + assertEquals(YearMonth.of(2025, 1), TopVoterLoader.parseMonthColumn("MonthTotal-JANUARY-2025")); + assertEquals(YearMonth.of(2026, 12), TopVoterLoader.parseMonthColumn("MonthTotal-DECEMBER-2026")); + } + + @Test + public void rejectsMalformedMonthColumns() { + assertNull(TopVoterLoader.parseMonthColumn("MonthTotal-BAD-2025")); + assertNull(TopVoterLoader.parseMonthColumn("MonthTotal-JANUARY-nope")); + assertNull(TopVoterLoader.parseMonthColumn("Other-JANUARY-2025")); + assertNull(TopVoterLoader.parseMonthColumn(null)); + } +} From 53998ea0fb01846814dc8281f27b6b2ac4dee6c4 Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 25 Aug 2026 18:06:24 -0600 Subject: [PATCH 2/3] Restore top voter skull cache warmup --- .../votingplugin/topvoter/TopVoterState.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java index ec5817514b..84f53badd6 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import com.bencodez.votingplugin.VotingPluginMain; import com.bencodez.votingplugin.votesites.VoteSite; /** @@ -16,6 +17,7 @@ public class TopVoterState { private LinkedHashMap lastMonthTopVoters; private LinkedHashMap> previousMonthsTopVoters; private LinkedHashMap> voteToday; + private boolean skullCacheWarmed; public TopVoterState() { reset(); @@ -29,6 +31,7 @@ public void reset() { lastMonthTopVoters = new LinkedHashMap<>(); previousMonthsTopVoters = new LinkedHashMap<>(); voteToday = new LinkedHashMap<>(); + skullCacheWarmed = false; } public LinkedHashMap> getTopVoters() { @@ -37,6 +40,37 @@ public LinkedHashMap> getTopVot public void setTopVoters(LinkedHashMap> topVoters) { this.topVoters = topVoters != null ? topVoters : new LinkedHashMap<>(); + warmSkullCacheOnce(); + } + + private void warmSkullCacheOnce() { + if (skullCacheWarmed) { + return; + } + skullCacheWarmed = true; + + VotingPluginMain plugin = VotingPluginMain.plugin; + if (plugin == null) { + return; + } + + new Thread(() -> { + if (!plugin.getGui().isChestVoteTopUseSkull()) { + return; + } + + int maxToLoad = 200; + for (TopVoter topVoter : topVoters.keySet()) { + int loaded = 0; + for (TopVoterPlayer player : getTopVoters(topVoter).keySet()) { + plugin.getSkullCacheHandler().addToCache(player.getUuid(), player.getPlayerName()); + loaded++; + if (loaded >= maxToLoad) { + break; + } + } + } + }).start(); } public LinkedHashMap getTopVoters(TopVoter topVoter) { From 5391868232d79aee4af7cc3f5e8417b8a0e9289b Mon Sep 17 00:00:00 2001 From: Ben Date: Tue, 25 Aug 2026 18:11:27 -0600 Subject: [PATCH 3/3] Warm skull cache after all rankings finalize --- .../bencodez/votingplugin/topvoter/TopVoterState.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java index 84f53badd6..390f0a8bfb 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/topvoter/TopVoterState.java @@ -18,6 +18,7 @@ public class TopVoterState { private LinkedHashMap> previousMonthsTopVoters; private LinkedHashMap> voteToday; private boolean skullCacheWarmed; + private int rankingUpdatesSeen; public TopVoterState() { reset(); @@ -32,6 +33,7 @@ public void reset() { previousMonthsTopVoters = new LinkedHashMap<>(); voteToday = new LinkedHashMap<>(); skullCacheWarmed = false; + rankingUpdatesSeen = 0; } public LinkedHashMap> getTopVoters() { @@ -40,7 +42,12 @@ public LinkedHashMap> getTopVot public void setTopVoters(LinkedHashMap> topVoters) { this.topVoters = topVoters != null ? topVoters : new LinkedHashMap<>(); - warmSkullCacheOnce(); + if (!skullCacheWarmed && !this.topVoters.isEmpty()) { + rankingUpdatesSeen++; + if (rankingUpdatesSeen >= this.topVoters.size()) { + warmSkullCacheOnce(); + } + } } private void warmSkullCacheOnce() {