diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java index 4d9dc96..6505542 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java @@ -3,13 +3,12 @@ package com.gmail.artemis.the.gr8.playerstats; import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter; import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread; -import com.gmail.artemis.the.gr8.playerstats.statistic.ShareQueue; -import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest; +import com.gmail.artemis.the.gr8.playerstats.statistic.ShareManager; +import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -22,23 +21,24 @@ public class ThreadManager { private final BukkitAudiences adventure; private static ConfigHandler config; private static MessageWriter messageWriter; - private final ShareQueue shareQueue; + private final ShareManager shareManager; private ReloadThread lastActiveReloadThread; private StatThread lastActiveStatThread; private final HashMap statThreads; private static long lastRecordedCalcTime; - public ThreadManager(BukkitAudiences a, ConfigHandler c, MessageWriter m, @Nullable ShareQueue s) { + public ThreadManager(BukkitAudiences a, ConfigHandler c, MessageWriter m) { adventure = a; config = c; messageWriter = m; - shareQueue = s; + shareManager = new ShareManager(config); statThreads = new HashMap<>(); statThreadID = 0; reloadThreadID = 0; lastRecordedCalcTime = 0; + startReloadThread(null); } @@ -83,7 +83,7 @@ public class ThreadManager { } private void startNewStatThread(StatRequest request) { - lastActiveStatThread = new StatThread(adventure, config, messageWriter, statThreadID, threshold, request, lastActiveReloadThread); + lastActiveStatThread = new StatThread(adventure, config, messageWriter, statThreadID, threshold, request, lastActiveReloadThread, shareManager); statThreads.put(request.getCommandSender().getName(), lastActiveStatThread); lastActiveStatThread.start(); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ShareCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ShareCommand.java index 9d97980..53305e6 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ShareCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ShareCommand.java @@ -9,6 +9,8 @@ public class ShareCommand implements CommandExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, String label, String[] args) { + //TODO use UUID code as arg to share the appropriate statResult + return false; } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index fa0a9c2..ebce446 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -3,7 +3,7 @@ package com.gmail.artemis.the.gr8.playerstats.commands; import com.gmail.artemis.the.gr8.playerstats.ThreadManager; import com.gmail.artemis.the.gr8.playerstats.enums.Target; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; -import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest; +import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter; import net.kyori.adventure.platform.bukkit.BukkitAudiences; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/config/ConfigHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/config/ConfigHandler.java index 5bf33ff..c2a61d7 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/config/ConfigHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/config/ConfigHandler.java @@ -85,11 +85,11 @@ public class ConfigHandler { return config.getBoolean("enable-stat-sharing", true); } - /** Returns the number of minutes a command-sender has to wait before being able to + /** Returns the number of minutes a player has to wait before being able to share another stat-result.

Default: 0

*/ - public int getStatShareLimit() { - return config.getInt("sharing-time-limit", 0); + public int getStatShareWaitingTime() { + return config.getInt("waiting-time-before-sharing-again", 0); } /** Returns the config setting for include-whitelist-only. @@ -176,13 +176,13 @@ public class ConfigHandler { /** Whether to use festive formatting, such as pride colors.

Default: true

*/ - public boolean useFestiveFormatting() { + public boolean enableFestiveFormatting() { return config.getBoolean("enable-festive-formatting", true); } /** Whether to use rainbow colors for the [PlayerStats] prefix rather than the default gold/purple.

Default: false

*/ - public boolean useRainbowMode() { + public boolean enableRainbowMode() { return config.getBoolean("rainbow-mode", false); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatRequest.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatRequest.java similarity index 98% rename from src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatRequest.java rename to src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatRequest.java index 919c813..a854c99 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatRequest.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatRequest.java @@ -1,4 +1,4 @@ -package com.gmail.artemis.the.gr8.playerstats.statistic; +package com.gmail.artemis.the.gr8.playerstats.models; import com.gmail.artemis.the.gr8.playerstats.enums.Target; import org.bukkit.Bukkit; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatResult.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatResult.java new file mode 100644 index 0000000..b6c1add --- /dev/null +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/models/StatResult.java @@ -0,0 +1,9 @@ +package com.gmail.artemis.the.gr8.playerstats.models; + +import net.kyori.adventure.text.TextComponent; + +import java.util.UUID; + + +public record StatResult(String playerName, TextComponent statResult, int ID, UUID uuid) { +} diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java index 4d74aa7..cdaf8e4 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java @@ -7,7 +7,7 @@ import com.gmail.artemis.the.gr8.playerstats.enums.Unit; import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.ExampleMessage; import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.HelpMessage; import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler; -import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest; +import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; @@ -42,7 +42,7 @@ public class MessageWriter { } private static void getComponentFactory() { - if (config.useFestiveFormatting() || config.useRainbowMode()) { + if (config.enableFestiveFormatting() || config.enableRainbowMode()) { componentFactory = new PrideComponentFactory(config); } else { diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideComponentFactory.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideComponentFactory.java index 9782f22..8f9ad2a 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideComponentFactory.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideComponentFactory.java @@ -61,7 +61,7 @@ public class PrideComponentFactory extends ComponentFactory { if festive formatting is disabled or it is not pride month, or the commandsender is a Bukkit or Spigot console.*/ private boolean cancelRainbow(boolean isBukkitConsole) { - return !(config.useRainbowMode() || (config.useFestiveFormatting() && LocalDate.now().getMonth().equals(Month.JUNE))) || + return !(config.enableRainbowMode() || (config.enableFestiveFormatting() && LocalDate.now().getMonth().equals(Month.JUNE))) || (isBukkitConsole); } } \ No newline at end of file diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java index 4eefd05..6d1ecaf 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java @@ -55,7 +55,7 @@ public class ReloadThread extends Thread { MyLogger.waitingForOtherThread(this.getName(), statThread.getName()); statThread.join(); } catch (InterruptedException e) { - MyLogger.logException(e, "ReloadThread", "run(), trying to join" + statThread.getName()); + MyLogger.logException(e, "ReloadThread", "run(), trying to join " + statThread.getName()); throw new RuntimeException(e); } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareManager.java new file mode 100644 index 0000000..b2f41b2 --- /dev/null +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareManager.java @@ -0,0 +1,95 @@ +package com.gmail.artemis.the.gr8.playerstats.statistic; + +import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; +import com.gmail.artemis.the.gr8.playerstats.models.StatResult; +import net.kyori.adventure.text.TextComponent; + +import javax.annotation.Nullable; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +public class ShareManager { + + private boolean isEnabled; + private int waitingTime; + + private volatile AtomicInteger resultID; //always starts with value 0 + private ConcurrentHashMap statResults = null; + private ConcurrentHashMap shareTimeStamp = null; + + public ShareManager(ConfigHandler config) { + isEnabled = config.enableStatSharing(); + waitingTime = config.getStatShareWaitingTime(); + if (isEnabled) { + statResults = new ConcurrentHashMap<>(); + shareTimeStamp = new ConcurrentHashMap<>(); + } + } + + public boolean isEnabled() { + return this.isEnabled; + } + + public void updateSettings(ConfigHandler config) { + isEnabled = config.enableStatSharing(); + waitingTime = config.getStatShareWaitingTime(); + if (isEnabled && statResults == null) { + statResults = new ConcurrentHashMap<>(); + shareTimeStamp = new ConcurrentHashMap<>(); + } + } + + public UUID saveStatResult(String playerName, TextComponent statResult) { + removeExcessResults(playerName); + + int ID = getNextIDNumber(); + UUID identifier = UUID.randomUUID(); + + statResults.put(identifier, new StatResult(playerName, statResult, ID, identifier)); + return identifier; + } + + public @Nullable TextComponent getStatResult(String playerName, UUID identifier) { + if (statResults.containsKey(identifier) && playerCanShare(playerName)) { + shareTimeStamp.put(playerName, Instant.now()); + return statResults.remove(identifier).statResult(); + } else { + return null; + } + } + + private boolean playerCanShare(String playerName) { + if (waitingTime == 0 || !shareTimeStamp.containsKey(playerName)) { + return true; + } else { + long seconds = shareTimeStamp.get(playerName).until(Instant.now(), ChronoUnit.SECONDS); + return seconds >= waitingTime; + } + } + + /** If the given player already has more than x (in this case 25) StatResults saved, + remove the oldest one.*/ + private void removeExcessResults(String playerName) { + List alreadySavedResults = statResults.values() + .parallelStream() + .filter(result -> result.playerName().equalsIgnoreCase(playerName)) + .toList(); + + if (alreadySavedResults.size() > 25) { + UUID uuid = alreadySavedResults + .parallelStream() + .min(Comparator.comparing(StatResult::ID)) + .orElseThrow().uuid(); + statResults.remove(uuid); + } + } + + private int getNextIDNumber() { + return resultID.incrementAndGet(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareQueue.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareQueue.java deleted file mode 100644 index 52f3624..0000000 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/ShareQueue.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gmail.artemis.the.gr8.playerstats.statistic; - -import net.kyori.adventure.text.TextComponent; -import org.jetbrains.annotations.Nullable; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.concurrent.ConcurrentHashMap; - -public class ShareQueue { - - private volatile long timeThreshold; - private final ConcurrentHashMap statResults; - private final ConcurrentHashMap shareTimestamp; - - public ShareQueue(long timeThreshold) { - this.timeThreshold = timeThreshold; - statResults = new ConcurrentHashMap<>(); - shareTimestamp = new ConcurrentHashMap<>(); - } - - public void saveStatResults(String senderName, TextComponent statResult) { - statResults.put(senderName, statResult); - } - - public boolean senderCanShare(String senderName) { - return senderCanShare(senderName, 0); - } - - /** Returns true if the given sender has a statResult that can be shared, - if they have not shared a statResult yet, or if they have passed the timeLimit. - @param senderName name of the commandSender to evaluate - @param timeLimit the waiting time in seconds during which sharing is not allowed*/ - public boolean senderCanShare(String senderName, long timeLimit) { - if (timeLimit == 0 || !shareTimestamp.containsKey(senderName)) { - return statResults.containsKey(senderName); - } else { - long seconds = shareTimestamp.get(senderName).until(Instant.now(), ChronoUnit.SECONDS); - return seconds >= timeLimit; - } - } - - /** Removes and returns the last statResults for this sender, - and stores the timestamp the results were retrieved on.*/ - public @Nullable TextComponent getLastStatResult(String senderName) { - if (statResults.containsKey(senderName)) { - shareTimestamp.put(senderName, Instant.now()); - return statResults.remove(senderName); - } else { - return null; - } - } -} diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java index fa7ddc3..460410c 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java @@ -1,6 +1,7 @@ package com.gmail.artemis.the.gr8.playerstats.statistic; import com.gmail.artemis.the.gr8.playerstats.enums.Target; +import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter; import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread; import com.gmail.artemis.the.gr8.playerstats.ThreadManager; @@ -23,21 +24,18 @@ public class StatThread extends Thread { private final int threshold; private final ReloadThread reloadThread; - private final ShareQueue shareQueue; + private final ShareManager shareManager; private final BukkitAudiences adventure; private static ConfigHandler config; private static MessageWriter messageWriter; private final StatRequest request; - public StatThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, int ID, int threshold, StatRequest s, @Nullable ReloadThread r) { - this(a, c, m, ID, threshold, s, r, null); - } - public StatThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, int ID, int threshold, StatRequest s, @Nullable ReloadThread r, @Nullable ShareQueue q) { + public StatThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, int ID, int threshold, StatRequest s, @Nullable ReloadThread r, @Nullable ShareManager sm) { this.threshold = threshold; reloadThread = r; - shareQueue = q; + shareManager = sm; adventure = a; config = c; @@ -65,7 +63,7 @@ public class StatThread extends Thread { reloadThread.join(); } catch (InterruptedException e) { - MyLogger.logException(e, "StatThread", "Trying to join" + reloadThread.getName()); + MyLogger.logException(e, "StatThread", "Trying to join " + reloadThread.getName()); throw new RuntimeException(e); } } @@ -85,7 +83,9 @@ public class StatThread extends Thread { } else { statResult = messageWriter.formatServerStat(getServerTotal(), request); } - + if (shareManager.isEnabled()) { + UUID shareCode = shareManager.saveStatResult(request.getCommandSender().getName(), statResult); + } adventure.sender(request.getCommandSender()).sendMessage(statResult); } catch (ConcurrentModificationException e) { @@ -103,7 +103,7 @@ public class StatThread extends Thread { } private long getServerTotal() { - List numbers = getAllStats().values().stream().toList(); + List numbers = getAllStats().values().parallelStream().toList(); return numbers.parallelStream().mapToLong(Integer::longValue).sum(); } @@ -111,7 +111,7 @@ public class StatThread extends Thread { private @NotNull ConcurrentHashMap getAllStats() throws ConcurrentModificationException { long time = System.currentTimeMillis(); - int size = OfflinePlayerHandler.getOfflinePlayerCount() != 0 ? (int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05) : 16; + int size = OfflinePlayerHandler.getOfflinePlayerCount() != 0 ? OfflinePlayerHandler.getOfflinePlayerCount() : 16; ConcurrentHashMap playerStats = new ConcurrentHashMap<>(size); ImmutableList playerNames = ImmutableList.copyOf(OfflinePlayerHandler.getOfflinePlayerNames()); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/TopStatAction.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/TopStatAction.java index 0067b27..0df89e1 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/TopStatAction.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/TopStatAction.java @@ -1,5 +1,6 @@ package com.gmail.artemis.the.gr8.playerstats.statistic; +import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.google.common.collect.ImmutableList; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index bae4634..b501218 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -21,9 +21,9 @@ only-allow-one-lookup-at-a-time-per-player: true # Whether statistics can be shared with everyone in chat enable-stat-sharing: true -# How often players can share statistics in chat. You can specify a waiting time (in minutes) below, -# or leave this setting on 0 to not use any time-limit -sharing-time-limit: 0 +# How often players can share statistics in chat (use this if you want to limit chat spam) +# Leave this on 0 to disable the cool-down, or specify the number of minutes you want players to wait +waiting-time-before-sharing-again: 0 # Filtering options to control which players should be included in statistic calculations include-whitelist-only: false