mirror of
https://github.com/itHotL/PlayerStats.git
synced 2025-02-02 23:31:21 +01:00
commit
113a47772e
@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.gmail.artemis-the-gr8</groupId>
|
||||
<groupId>com.gmail.artemis.the.gr8</groupId>
|
||||
<artifactId>PlayerStats</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>1.6</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<source>16</source>
|
||||
<target>16</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@ -36,6 +36,16 @@
|
||||
<exclude>org.jetbrains:annotations</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.kyori</pattern>
|
||||
<shadedPattern>com.gmail.artemis.the.gr8.kyori</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.tchristofferson</pattern>
|
||||
<shadedPattern>com.gmail.artemis.the.gr8.tchristofferson</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
@ -45,6 +55,18 @@
|
||||
<exclude>images/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>net.kyori:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.tchristofferson:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@ -79,8 +101,8 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
||||
|
36
pom.xml
36
pom.xml
@ -4,14 +4,14 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.gmail.artemis-the-gr8</groupId>
|
||||
<groupId>com.gmail.artemis.the.gr8</groupId>
|
||||
<artifactId>PlayerStats</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>1.6</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
|
||||
@ -78,14 +78,14 @@
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<source>16</source>
|
||||
<target>16</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@ -106,6 +106,16 @@
|
||||
<exclude>org.jetbrains:annotations</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>net.kyori</pattern>
|
||||
<shadedPattern>com.gmail.artemis.the.gr8.kyori</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.tchristofferson</pattern>
|
||||
<shadedPattern>com.gmail.artemis.the.gr8.tchristofferson</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
@ -115,6 +125,18 @@
|
||||
<exclude>images/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>net.kyori:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>com.tchristofferson:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
@ -1,26 +1,24 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.commands.ReloadCommand;
|
||||
import com.gmail.artemis.the.gr8.playerstats.commands.ShareCommand;
|
||||
import com.gmail.artemis.the.gr8.playerstats.commands.StatCommand;
|
||||
import com.gmail.artemis.the.gr8.playerstats.commands.TabCompleter;
|
||||
import com.gmail.artemis.the.gr8.playerstats.commands.cmdutils.TabCompleteHelper;
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
|
||||
import com.gmail.artemis.the.gr8.playerstats.listeners.JoinListener;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class Main extends JavaPlugin {
|
||||
|
||||
private BukkitAudiences adventure;
|
||||
private static BukkitAudiences adventure;
|
||||
|
||||
public @NotNull BukkitAudiences adventure() {
|
||||
public static @NotNull BukkitAudiences adventure() {
|
||||
if (adventure == null) {
|
||||
throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!");
|
||||
}
|
||||
@ -32,25 +30,24 @@ public class Main extends JavaPlugin {
|
||||
//initialize the Adventure library
|
||||
adventure = BukkitAudiences.create(this);
|
||||
|
||||
//first get an instance of the ConfigHandler
|
||||
//first get an instance of all the classes that need to be initialized or passed along to different classes
|
||||
ConfigHandler config = new ConfigHandler(this);
|
||||
OutputManager sender = new OutputManager(config);
|
||||
OfflinePlayerHandler offlinePlayerHandler = new OfflinePlayerHandler();
|
||||
|
||||
//for now always use the PrideComponentFactory (it'll use the regular formatting when needed)
|
||||
MessageWriter messageWriter = new MessageWriter(config);
|
||||
|
||||
//initialize the threadManager
|
||||
ThreadManager threadManager = new ThreadManager(adventure(), config, messageWriter, this);
|
||||
TabCompleteHelper tab = new TabCompleteHelper();
|
||||
Bukkit.getLogger().info(tab.getEntityKilledSuggestions().toString());
|
||||
ThreadManager threadManager = ThreadManager.getInstance(config, sender, offlinePlayerHandler);
|
||||
ShareManager shareManager = ShareManager.getInstance(config);
|
||||
|
||||
//register all commands and the tabCompleter
|
||||
PluginCommand statcmd = this.getCommand("statistic");
|
||||
if (statcmd != null) {
|
||||
statcmd.setExecutor(new StatCommand(adventure(), messageWriter, threadManager));
|
||||
statcmd.setTabCompleter(new TabCompleter());
|
||||
statcmd.setExecutor(new StatCommand(sender, threadManager, offlinePlayerHandler));
|
||||
statcmd.setTabCompleter(new TabCompleter(offlinePlayerHandler));
|
||||
}
|
||||
PluginCommand reloadcmd = this.getCommand("statisticreload");
|
||||
if (reloadcmd != null) reloadcmd.setExecutor(new ReloadCommand(threadManager));
|
||||
PluginCommand sharecmd = this.getCommand("statisticshare");
|
||||
if (sharecmd != null) sharecmd.setExecutor(new ShareCommand(shareManager, sender));
|
||||
|
||||
//register the listener
|
||||
Bukkit.getPluginManager().registerEvents(new JoinListener(threadManager), this);
|
||||
|
@ -0,0 +1,160 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
|
||||
import com.gmail.artemis.the.gr8.playerstats.models.StatResult;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.time.temporal.ChronoUnit.SECONDS;
|
||||
|
||||
public final class ShareManager {
|
||||
|
||||
private static volatile ShareManager instance;
|
||||
|
||||
private static boolean isEnabled;
|
||||
private static int waitingTime;
|
||||
|
||||
private volatile AtomicInteger resultID;
|
||||
private ConcurrentHashMap<UUID, StatResult> statResultQueue;
|
||||
private ConcurrentHashMap<String, Instant> shareTimeStamp;
|
||||
private ArrayBlockingQueue<UUID> sharedResults;
|
||||
|
||||
private ShareManager(ConfigHandler config) {
|
||||
updateSettings(config);
|
||||
}
|
||||
|
||||
public static ShareManager getInstance(ConfigHandler config) {
|
||||
ShareManager shareManager = instance;
|
||||
if (shareManager != null) {
|
||||
return shareManager;
|
||||
}
|
||||
synchronized (ShareManager.class) {
|
||||
if (instance == null) {
|
||||
instance = new ShareManager(config);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void updateSettings(ConfigHandler config) {
|
||||
isEnabled = config.allowStatSharing() && config.useHoverText();
|
||||
waitingTime = config.getStatShareWaitingTime();
|
||||
|
||||
if (isEnabled) {
|
||||
sharedResults = new ArrayBlockingQueue<>(500); //reset the sharedResultsQueue
|
||||
if (resultID == null) { //if we went from disabled to enabled, initialize
|
||||
resultID = new AtomicInteger(); //always starts with value 0
|
||||
statResultQueue = new ConcurrentHashMap<>();
|
||||
shareTimeStamp = new ConcurrentHashMap<>();
|
||||
}
|
||||
} else {
|
||||
//if we went from enabled to disabled, purge the existing data
|
||||
if (statResultQueue != null) {
|
||||
statResultQueue = null;
|
||||
shareTimeStamp = null;
|
||||
sharedResults = null;
|
||||
}
|
||||
if (config.allowStatSharing() && !config.useHoverText()) {
|
||||
MyLogger.logMsg("Stat-sharing does not work without hover-text enabled! " +
|
||||
"Enable hover-text, or disable stat-sharing to stop seeing this message.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
public boolean senderHasPermission(CommandSender sender) {
|
||||
return !(sender instanceof ConsoleCommandSender) && sender.hasPermission("playerstats.share");
|
||||
}
|
||||
|
||||
public UUID saveStatResult(String playerName, TextComponent statResult) {
|
||||
removeExcessResults(playerName);
|
||||
|
||||
int ID = getNextIDNumber();
|
||||
UUID shareCode = UUID.randomUUID();
|
||||
|
||||
statResultQueue.put(shareCode, new StatResult(playerName, statResult, ID, shareCode));
|
||||
MyLogger.logMsg("Saving statResults with no. " + ID, DebugLevel.MEDIUM);
|
||||
return shareCode;
|
||||
}
|
||||
|
||||
public boolean isOnCoolDown(String playerName) {
|
||||
if (waitingTime == 0 || !shareTimeStamp.containsKey(playerName)) {
|
||||
return false;
|
||||
} else {
|
||||
long seconds = SECONDS.between(shareTimeStamp.get(playerName), Instant.now());
|
||||
return seconds <= (long) waitingTime * 60;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean requestAlreadyShared(UUID shareCode) {
|
||||
return sharedResults.contains(shareCode);
|
||||
}
|
||||
|
||||
/** Takes a statResult from the internal ConcurrentHashmap,
|
||||
puts the current time in the shareTimeStamp (ConcurrentHashMap),
|
||||
puts the shareCode (UUID) in the sharedResults (ArrayBlockingQueue),
|
||||
and returns the statResult. If no statResult was found, returns null.*/
|
||||
public @Nullable StatResult getStatResult(String playerName, UUID shareCode) {
|
||||
if (statResultQueue.containsKey(shareCode)) {
|
||||
shareTimeStamp.put(playerName, Instant.now());
|
||||
|
||||
if (!sharedResults.offer(shareCode)) { //create a new ArrayBlockingQueue if our queue is full
|
||||
MyLogger.logMsg("500 stat-results have been shared, " +
|
||||
"creating a new internal queue with the most recent 50 share-code-values and discarding the rest...", DebugLevel.MEDIUM);
|
||||
ArrayBlockingQueue<UUID> newQueue = new ArrayBlockingQueue<>(500);
|
||||
|
||||
synchronized (this) { //put the last 50 values in the new Queue
|
||||
UUID[] lastValues = sharedResults.toArray(new UUID[0]);
|
||||
Arrays.stream(Arrays.copyOfRange(lastValues, 450, 500))
|
||||
.parallel().iterator()
|
||||
.forEachRemaining(newQueue::offer);
|
||||
|
||||
sharedResults = newQueue;
|
||||
}
|
||||
sharedResults.offer(shareCode);
|
||||
}
|
||||
return statResultQueue.remove(shareCode);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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<StatResult> alreadySavedResults = statResultQueue.values()
|
||||
.parallelStream()
|
||||
.filter(result -> result.playerName().equalsIgnoreCase(playerName))
|
||||
.toList();
|
||||
|
||||
if (alreadySavedResults.size() > 25) {
|
||||
UUID uuid = alreadySavedResults
|
||||
.parallelStream()
|
||||
.min(Comparator.comparing(StatResult::ID))
|
||||
.orElseThrow().uuid();
|
||||
MyLogger.logMsg("Removing old stat no. " + statResultQueue.get(uuid).ID() + " for player " + playerName, DebugLevel.MEDIUM);
|
||||
statResultQueue.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
private int getNextIDNumber() {
|
||||
return resultID.incrementAndGet();
|
||||
}
|
||||
}
|
@ -1,51 +1,69 @@
|
||||
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.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread;
|
||||
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.statistic.StatThread;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public final class ThreadManager {
|
||||
|
||||
public class ThreadManager {
|
||||
private static volatile ThreadManager instance;
|
||||
|
||||
private final int threshold = 10;
|
||||
private final static int threshold = 10;
|
||||
private int statThreadID;
|
||||
private int reloadThreadID;
|
||||
|
||||
private final Main plugin;
|
||||
private final BukkitAudiences adventure;
|
||||
private static ConfigHandler config;
|
||||
private static MessageWriter messageWriter;
|
||||
private static OutputManager messageSender;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
private ReloadThread lastActiveReloadThread;
|
||||
private StatThread lastActiveStatThread;
|
||||
private final HashMap<String, Thread> statThreads;
|
||||
private static long lastRecordedCalcTime;
|
||||
|
||||
public ThreadManager(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p) {
|
||||
adventure = a;
|
||||
private ThreadManager(ConfigHandler c, OutputManager m, OfflinePlayerHandler o) {
|
||||
config = c;
|
||||
messageWriter = m;
|
||||
plugin = p;
|
||||
messageSender = m;
|
||||
offlinePlayerHandler = o;
|
||||
|
||||
statThreads = new HashMap<>();
|
||||
statThreadID = 0;
|
||||
reloadThreadID = 0;
|
||||
lastRecordedCalcTime = 0;
|
||||
|
||||
startReloadThread(null);
|
||||
}
|
||||
|
||||
public static ThreadManager getInstance(ConfigHandler config, OutputManager messageSender, OfflinePlayerHandler offlinePlayerHandler) {
|
||||
ThreadManager threadManager = instance;
|
||||
if (threadManager != null) {
|
||||
return threadManager;
|
||||
}
|
||||
synchronized (ThreadManager.class) {
|
||||
if (instance == null) {
|
||||
instance = new ThreadManager(config, messageSender, offlinePlayerHandler);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTaskThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
public void startReloadThread(CommandSender sender) {
|
||||
if (lastActiveReloadThread == null || !lastActiveReloadThread.isAlive()) {
|
||||
reloadThreadID += 1;
|
||||
|
||||
lastActiveReloadThread = new ReloadThread(adventure, config, messageWriter, threshold, reloadThreadID, lastActiveStatThread, sender);
|
||||
lastActiveReloadThread = new ReloadThread(config, messageSender, offlinePlayerHandler, reloadThreadID, lastActiveStatThread, sender);
|
||||
lastActiveReloadThread.start();
|
||||
}
|
||||
else {
|
||||
@ -60,7 +78,7 @@ public class ThreadManager {
|
||||
if (config.limitStatRequests() && statThreads.containsKey(cmdSender)) {
|
||||
Thread runningThread = statThreads.get(cmdSender);
|
||||
if (runningThread.isAlive()) {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(messageWriter.requestAlreadyRunning(request.isBukkitConsoleSender()));
|
||||
messageSender.sendFeedbackMsg(request.getCommandSender(), StandardMessage.REQUEST_ALREADY_RUNNING);
|
||||
} else {
|
||||
startNewStatThread(request);
|
||||
}
|
||||
@ -82,7 +100,7 @@ public class ThreadManager {
|
||||
}
|
||||
|
||||
private void startNewStatThread(StatRequest request) {
|
||||
lastActiveStatThread = new StatThread(adventure, config, messageWriter, plugin, statThreadID, threshold, request, lastActiveReloadThread);
|
||||
lastActiveStatThread = new StatThread(config, messageSender, offlinePlayerHandler, statThreadID, request, lastActiveReloadThread);
|
||||
statThreads.put(request.getCommandSender().getName(), lastActiveStatThread);
|
||||
lastActiveStatThread.start();
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReloadCommand implements CommandExecutor {
|
||||
|
||||
private final ThreadManager threadManager;
|
||||
private static ThreadManager threadManager;
|
||||
|
||||
public ReloadCommand(ThreadManager t) {
|
||||
threadManager = t;
|
||||
|
@ -0,0 +1,52 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.commands;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.ShareManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.models.StatResult;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ShareCommand implements CommandExecutor {
|
||||
|
||||
private static ShareManager shareManager;
|
||||
private static OutputManager outputManager;
|
||||
|
||||
public ShareCommand(ShareManager s, OutputManager m) {
|
||||
shareManager = s;
|
||||
outputManager = m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) {
|
||||
if (args.length == 1 && shareManager.isEnabled()) {
|
||||
UUID shareCode;
|
||||
try {
|
||||
shareCode = UUID.fromString(args[0]);
|
||||
} catch (IllegalArgumentException e) {
|
||||
MyLogger.logException(e, "ShareCommand", "/statshare is being called without a valid UUID argument");
|
||||
return false;
|
||||
}
|
||||
if (shareManager.requestAlreadyShared(shareCode)) {
|
||||
outputManager.sendFeedbackMsg(sender, StandardMessage.RESULTS_ALREADY_SHARED);
|
||||
}
|
||||
else if (shareManager.isOnCoolDown(sender.getName())) {
|
||||
outputManager.sendFeedbackMsg(sender, StandardMessage.STILL_ON_SHARE_COOLDOWN);
|
||||
}
|
||||
else {
|
||||
StatResult result = shareManager.getStatResult(sender.getName(), shareCode);
|
||||
if (result == null) { //at this point the only possible cause of statResult being null is the request being older than 25 player-requests ago
|
||||
outputManager.sendFeedbackMsg(sender, StandardMessage.STAT_RESULTS_TOO_OLD);
|
||||
} else {
|
||||
outputManager.shareStatResults(result.statResult());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.commands;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
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;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.Command;
|
||||
@ -18,39 +16,34 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
public class StatCommand implements CommandExecutor {
|
||||
|
||||
private final BukkitAudiences adventure;
|
||||
private final MessageWriter messageWriter;
|
||||
private final ThreadManager threadManager;
|
||||
private static ThreadManager threadManager;
|
||||
private static OutputManager outputManager;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
public StatCommand(BukkitAudiences a, MessageWriter m, ThreadManager t) {
|
||||
adventure = a;
|
||||
messageWriter = m;
|
||||
public StatCommand(OutputManager m, ThreadManager t, OfflinePlayerHandler o) {
|
||||
threadManager = t;
|
||||
outputManager = m;
|
||||
offlinePlayerHandler = o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
boolean isBukkitConsole = sender instanceof ConsoleCommandSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit");
|
||||
if (args.length == 0 || args[0].equalsIgnoreCase("help")) { //in case of less than 1 argument or "help", display the help message
|
||||
adventure.sender(sender).sendMessage(messageWriter.helpMsg(sender instanceof ConsoleCommandSender));
|
||||
outputManager.sendHelp(sender);
|
||||
}
|
||||
else if (args[0].equalsIgnoreCase("examples") ||
|
||||
args[0].equalsIgnoreCase("example")) { //in case of "statistic examples", show examples
|
||||
adventure.sender(sender).sendMessage(messageWriter.usageExamples(isBukkitConsole));
|
||||
outputManager.sendExamples(sender);
|
||||
}
|
||||
else {
|
||||
StatRequest request = generateRequest(sender, args);
|
||||
TextComponent issues = checkRequest(request, isBukkitConsole);
|
||||
if (issues == null) {
|
||||
if (requestIsValid(request)) {
|
||||
threadManager.startStatThread(request);
|
||||
}
|
||||
else {
|
||||
adventure.sender(sender).sendMessage(issues);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -90,7 +83,7 @@ public class StatCommand implements CommandExecutor {
|
||||
request.setSelection(Target.SERVER);
|
||||
}
|
||||
}
|
||||
else if (OfflinePlayerHandler.isRelevantPlayer(arg) && request.getPlayerName() == null) {
|
||||
else if (offlinePlayerHandler.isRelevantPlayer(arg) && request.getPlayerName() == null) {
|
||||
request.setPlayerName(arg);
|
||||
request.setSelection(Target.PLAYER);
|
||||
}
|
||||
@ -136,28 +129,32 @@ public class StatCommand implements CommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/** This method validates the StatRequest and returns feedback in the form of a TextComponent.
|
||||
/** This method validates the StatRequest and returns feedback to the player if it returns false.
|
||||
It checks the following:
|
||||
<p>1. Is a Statistic set?</p>
|
||||
<p>2. Is a subStat needed, and is a subStat Enum Constant present? (block/entity/item)</p>
|
||||
<p>3. If the target is PLAYER, is a valid PlayerName provided? </p>
|
||||
@return null if the Request is valid, and an explanation message otherwise. */
|
||||
private @Nullable TextComponent checkRequest(StatRequest request, boolean isBukkitConsole) {
|
||||
@return true if the Request is valid, and false + an explanation message otherwise. */
|
||||
private boolean requestIsValid(StatRequest request) {
|
||||
if (request.getStatistic() == null) {
|
||||
return messageWriter.missingStatName(isBukkitConsole);
|
||||
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.MISSING_STAT_NAME);
|
||||
return false;
|
||||
}
|
||||
Statistic.Type type = request.getStatistic().getType();
|
||||
if (request.getSubStatEntry() == null && type != Statistic.Type.UNTYPED) {
|
||||
return messageWriter.missingSubStatName(type, isBukkitConsole);
|
||||
outputManager.sendFeedbackMsgMissingSubStat(request.getCommandSender(), type);
|
||||
return false;
|
||||
}
|
||||
else if (!matchingSubStat(request)) {
|
||||
return messageWriter.wrongSubStatType(type, request.getSubStatEntry(), isBukkitConsole);
|
||||
outputManager.sendFeedbackMsgWrongSubStat(request.getCommandSender(), type, request.getSubStatEntry());
|
||||
return false;
|
||||
}
|
||||
else if (request.getSelection() == Target.PLAYER && request.getPlayerName() == null) {
|
||||
return messageWriter.missingPlayerName(isBukkitConsole);
|
||||
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.MISSING_PLAYER_NAME);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,18 +14,21 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
|
||||
private final List<String> commandOptions;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
private final TabCompleteHelper tabCompleteHelper;
|
||||
|
||||
//TODO add "example" to the list
|
||||
public TabCompleter() {
|
||||
private final List<String> commandOptions;
|
||||
|
||||
public TabCompleter(OfflinePlayerHandler o) {
|
||||
offlinePlayerHandler = o;
|
||||
tabCompleteHelper = new TabCompleteHelper();
|
||||
|
||||
commandOptions = new ArrayList<>();
|
||||
commandOptions.add("top");
|
||||
commandOptions.add("player");
|
||||
commandOptions.add("server");
|
||||
commandOptions.add("me");
|
||||
|
||||
tabCompleteHelper = new TabCompleteHelper();
|
||||
}
|
||||
|
||||
//args[0] = statistic (length = 1)
|
||||
@ -37,34 +40,31 @@ public class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
List<String> tabSuggestions = new ArrayList<>();
|
||||
|
||||
//after typing "stat", suggest a list of viable statistics
|
||||
if (args.length >= 1) {
|
||||
String currentArg = args[args.length -1];
|
||||
|
||||
if (args.length == 1) {
|
||||
tabSuggestions = getTabSuggestions(EnumHandler.getStatNames(), args[0]);
|
||||
if (args.length == 1) { //after typing "stat", suggest a list of viable statistics
|
||||
tabSuggestions = getFirstArgSuggestions(args[0]);
|
||||
}
|
||||
|
||||
//after checking if args[0] is a viable statistic, suggest substatistic OR commandOptions
|
||||
else {
|
||||
else { //after checking if args[0] is a viable statistic, suggest substatistic OR commandOptions
|
||||
String previousArg = args[args.length -2];
|
||||
|
||||
if (EnumHandler.isStatistic(previousArg)) {
|
||||
Statistic stat = EnumHandler.getStatEnum(previousArg);
|
||||
|
||||
if (stat != null) {
|
||||
tabSuggestions = getTabSuggestions(getRelevantList(stat), currentArg);
|
||||
}
|
||||
}
|
||||
|
||||
//if previous arg = "player", suggest playerNames
|
||||
//if previous arg = "player"
|
||||
else if (previousArg.equalsIgnoreCase("player")) {
|
||||
//if args.length-3 is kill_entity or entity_killed_by
|
||||
|
||||
if (args.length >= 3 && EnumHandler.isEntityStatistic(args[args.length-3])) {
|
||||
tabSuggestions = commandOptions;
|
||||
tabSuggestions = commandOptions; //if arg before "player" was entity-stat, suggest commandOptions
|
||||
}
|
||||
else {
|
||||
tabSuggestions = getTabSuggestions(OfflinePlayerHandler.getOfflinePlayerNames(), currentArg);
|
||||
else { //otherwise "player" is target-flag: suggest playerNames
|
||||
tabSuggestions = getTabSuggestions(offlinePlayerHandler.getOfflinePlayerNames(), currentArg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +77,13 @@ public class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
return tabSuggestions;
|
||||
}
|
||||
|
||||
private List<String> getFirstArgSuggestions(String currentArg) {
|
||||
List<String> suggestions = EnumHandler.getStatNames();
|
||||
suggestions.add("examples");
|
||||
suggestions.add("help");
|
||||
return getTabSuggestions(suggestions, currentArg);
|
||||
}
|
||||
|
||||
private List<String> getTabSuggestions(List<String> completeList, String currentArg) {
|
||||
return completeList.stream()
|
||||
.filter(item -> item.toLowerCase().contains(currentArg.toLowerCase()))
|
||||
@ -91,14 +98,12 @@ public class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
case ITEM -> {
|
||||
if (stat == Statistic.BREAK_ITEM) {
|
||||
return tabCompleteHelper.getItemBrokenSuggestions();
|
||||
} else if (stat == Statistic.CRAFT_ITEM) {
|
||||
return tabCompleteHelper.getAllItemNames(); //TODO fix
|
||||
} else {
|
||||
return tabCompleteHelper.getAllItemNames();
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
return tabCompleteHelper.getEntityKilledSuggestions();
|
||||
return tabCompleteHelper.getEntitySuggestions();
|
||||
}
|
||||
default -> {
|
||||
return commandOptions;
|
||||
|
@ -8,10 +8,10 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TabCompleteHelper {
|
||||
public final class TabCompleteHelper {
|
||||
|
||||
private static List<String> itemBrokenSuggestions;
|
||||
private static List<String> entityKilledSuggestions;
|
||||
private static List<String> entitySuggestions;
|
||||
|
||||
public TabCompleteHelper() {
|
||||
prepareLists();
|
||||
@ -29,12 +29,13 @@ public class TabCompleteHelper {
|
||||
return EnumHandler.getBlockNames();
|
||||
}
|
||||
|
||||
public List<String> getEntityKilledSuggestions() {
|
||||
return entityKilledSuggestions;
|
||||
public List<String> getEntitySuggestions() {
|
||||
return entitySuggestions;
|
||||
}
|
||||
|
||||
|
||||
private static void prepareLists() {
|
||||
//breaking an item means running its durability negative
|
||||
itemBrokenSuggestions = Arrays.stream(Material.values())
|
||||
.parallel()
|
||||
.filter(Material::isItem)
|
||||
@ -43,7 +44,8 @@ public class TabCompleteHelper {
|
||||
.map(String::toLowerCase)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
entityKilledSuggestions = Arrays.stream(EntityType.values())
|
||||
//the only statistics dealing with entities are killed_entity and entity_killed_by
|
||||
entitySuggestions = Arrays.stream(EntityType.values())
|
||||
.parallel()
|
||||
.filter(EntityType::isAlive)
|
||||
.map(EntityType::toString)
|
||||
|
@ -12,18 +12,18 @@ import java.io.File;
|
||||
|
||||
public class ConfigHandler {
|
||||
|
||||
private static Main plugin;
|
||||
private static int configVersion;
|
||||
|
||||
private File configFile;
|
||||
private FileConfiguration config;
|
||||
private final Main plugin;
|
||||
private final double configVersion;
|
||||
|
||||
public ConfigHandler(Main p) {
|
||||
plugin = p;
|
||||
configVersion = 6;
|
||||
|
||||
saveDefaultConfig();
|
||||
config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
configVersion = 5;
|
||||
checkConfigVersion();
|
||||
|
||||
MyLogger.setDebugLevel(getDebugLevel());
|
||||
@ -35,7 +35,7 @@ public class ConfigHandler {
|
||||
<p>PlayerStats 1.3: "config-version" is 3. </P>
|
||||
<p>PlayerStats 1.4: "config-version" is 4.</p>*/
|
||||
private void checkConfigVersion() {
|
||||
if (!config.contains("config-version") || config.getDouble("config-version") != configVersion) {
|
||||
if (!config.contains("config-version") || config.getInt("config-version") != configVersion) {
|
||||
new ConfigUpdateHandler(plugin, configFile, configVersion);
|
||||
reloadConfig();
|
||||
}
|
||||
@ -73,10 +73,25 @@ public class ConfigHandler {
|
||||
return config.getInt("debug-level", 1);
|
||||
}
|
||||
|
||||
/** Returns true if command-senders should be limited to one stat-request at a time.
|
||||
<p>Default: true</p>*/
|
||||
public boolean limitStatRequests() {
|
||||
return config.getBoolean("only-allow-one-lookup-at-a-time-per-player", true);
|
||||
}
|
||||
|
||||
/** Returns true if stat-sharing is allowed.
|
||||
<p>Default: true</p>*/
|
||||
public boolean allowStatSharing() {
|
||||
return config.getBoolean("enable-stat-sharing", true);
|
||||
}
|
||||
|
||||
/** Returns the number of minutes a player has to wait before being able to
|
||||
share another stat-result.
|
||||
<p>Default: 0</p>*/
|
||||
public int getStatShareWaitingTime() {
|
||||
return config.getInt("waiting-time-before-sharing-again", 0);
|
||||
}
|
||||
|
||||
/** Returns the config setting for include-whitelist-only.
|
||||
<p>Default: false</p>*/
|
||||
public boolean whitelistOnly() {
|
||||
@ -108,47 +123,6 @@ public class ConfigHandler {
|
||||
return config.getBoolean("enable-hover-text", true);
|
||||
}
|
||||
|
||||
public String getDistanceUnit(boolean isHoverText) {
|
||||
return getUnitString(isHoverText, "blocks", "km", "distance-unit");
|
||||
}
|
||||
|
||||
public String getDamageUnit(boolean isHoverText) {
|
||||
return getUnitString(isHoverText, "hearts", "hp", "damage-unit");
|
||||
}
|
||||
|
||||
public boolean autoDetectTimeUnit(boolean isHoverText) {
|
||||
String path = "auto-detect-biggest-time-unit";
|
||||
if (isHoverText) {
|
||||
path = path + "-for-hover-text";
|
||||
}
|
||||
boolean defaultValue = !isHoverText;
|
||||
return config.getBoolean(path, defaultValue);
|
||||
}
|
||||
|
||||
public int getNumberOfExtraTimeUnits(boolean isHoverText) {
|
||||
String path = "number-of-extra-units";
|
||||
if (isHoverText) {
|
||||
path = path + "-for-hover-text";
|
||||
}
|
||||
int defaultValue = isHoverText ? 0 : 1;
|
||||
return config.getInt(path, defaultValue);
|
||||
}
|
||||
|
||||
/** By default, getTimeUnit will return the maxUnit. If the optional minUnit flag is specified,
|
||||
the minimum unit will be returned instead. */
|
||||
public String getTimeUnit(boolean isHoverText) {
|
||||
return getTimeUnit(isHoverText, false);
|
||||
}
|
||||
|
||||
/** By default, getTimeUnit will return the maxUnit. If the optional smallUnit flag is specified,
|
||||
the minimum unit will be returned instead. */
|
||||
public String getTimeUnit(boolean isHoverText, boolean smallUnit) {
|
||||
if (smallUnit) {
|
||||
return getUnitString(isHoverText, "hours", "seconds", "smallest-time-unit");
|
||||
}
|
||||
return getUnitString(isHoverText, "days", "hours", "biggest-time-unit");
|
||||
}
|
||||
|
||||
/** Whether to use festive formatting, such as pride colors.
|
||||
<p>Default: true</p> */
|
||||
public boolean useFestiveFormatting() {
|
||||
@ -161,6 +135,23 @@ public class ConfigHandler {
|
||||
return config.getBoolean("rainbow-mode", false);
|
||||
}
|
||||
|
||||
public boolean useEnters(Target selection, boolean getSharedSetting) {
|
||||
ConfigurationSection section = config.getConfigurationSection("use-enters");
|
||||
boolean def = selection == Target.TOP && !getSharedSetting;
|
||||
if (section != null) {
|
||||
String path = switch (selection) {
|
||||
case TOP -> getSharedSetting ? "top-stats-shared" : "top-stats";
|
||||
case PLAYER -> getSharedSetting ? "player-stats-shared" : "player-stats";
|
||||
case SERVER -> getSharedSetting ? "server-stats-shared" : "server-stats";
|
||||
};
|
||||
return section.getBoolean(path, def);
|
||||
}
|
||||
MyLogger.logMsg("Config settings for use-enters could not be retrieved! " +
|
||||
"Please check your file if you want to use custom settings. " +
|
||||
"Using default values...", true);
|
||||
return def;
|
||||
}
|
||||
|
||||
/** Returns the config setting for use-dots.
|
||||
<p>Default: true</p>*/
|
||||
public boolean useDots() {
|
||||
@ -191,6 +182,57 @@ public class ConfigHandler {
|
||||
return config.getString("your-server-name", "this server");
|
||||
}
|
||||
|
||||
/** Returns the unit that should be used for distance-related statistics.
|
||||
<p>Default: Blocks for plain text, km for hover-text</p>*/
|
||||
public String getDistanceUnit(boolean isHoverText) {
|
||||
return getUnitString(isHoverText, "blocks", "km", "distance-unit");
|
||||
}
|
||||
|
||||
/** Returns the unit that should be used for damage-based statistics.
|
||||
<p>Default: Hearts for plain text, HP for hover-text.</p>*/
|
||||
public String getDamageUnit(boolean isHoverText) {
|
||||
return getUnitString(isHoverText, "hearts", "hp", "damage-unit");
|
||||
}
|
||||
|
||||
/** Whether PlayerStats should automatically detect the most suitable unit to use for time-based statistics.
|
||||
<p>Default: true</p>*/
|
||||
public boolean autoDetectTimeUnit(boolean isHoverText) {
|
||||
String path = "auto-detect-biggest-time-unit";
|
||||
if (isHoverText) {
|
||||
path = path + "-for-hover-text";
|
||||
}
|
||||
boolean defaultValue = !isHoverText;
|
||||
return config.getBoolean(path, defaultValue);
|
||||
}
|
||||
|
||||
/** How many additional units should be displayed next to the most suitable largest unit for time-based statistics.
|
||||
<p>Default: 1 for plain text, 0 for hover-text</p>*/
|
||||
public int getNumberOfExtraTimeUnits(boolean isHoverText) {
|
||||
String path = "number-of-extra-units";
|
||||
if (isHoverText) {
|
||||
path = path + "-for-hover-text";
|
||||
}
|
||||
int defaultValue = isHoverText ? 0 : 1;
|
||||
return config.getInt(path, defaultValue);
|
||||
}
|
||||
|
||||
/** Returns the unit that should be used for time-based statistics.
|
||||
(this will return the largest unit that should be used).
|
||||
<p>Default: days for plain text, hours for hover-text</p>*/
|
||||
public String getTimeUnit(boolean isHoverText) {
|
||||
return getTimeUnit(isHoverText, false);
|
||||
}
|
||||
|
||||
/** Returns the unit that should be used for time-based statistics. If the optional smallUnit flag is true,
|
||||
this will return the smallest unit (and otherwise the largest).
|
||||
<p>Default: hours for plain text, seconds for hover-text</p>*/
|
||||
public String getTimeUnit(boolean isHoverText, boolean smallUnit) {
|
||||
if (smallUnit) {
|
||||
return getUnitString(isHoverText, "hours", "seconds", "smallest-time-unit");
|
||||
}
|
||||
return getUnitString(isHoverText, "days", "hours", "biggest-time-unit");
|
||||
}
|
||||
|
||||
/** Returns an integer between 0 and 100 that represents how much lighter a hoverColor should be.
|
||||
So 20 would mean 20% lighter.
|
||||
<p>Default: 20</p>*/
|
||||
@ -198,11 +240,26 @@ public class ConfigHandler {
|
||||
return config.getInt("hover-text-amount-lighter", 20);
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
|
||||
* <p>Style: "italic"</p>
|
||||
* <p>Color: "gray"</p>*/
|
||||
public String getSharedByTextDecoration(boolean getStyleSetting) {
|
||||
String def = getStyleSetting ? "italic" : "gray";
|
||||
return getDecorationString(null, getStyleSetting, def, "shared-by");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
|
||||
* <p>Style: "none"</p>
|
||||
* <p>Color: "#845EC2"</p>*/
|
||||
public String getSharerNameDecoration(boolean getStyleSetting) {
|
||||
return getDecorationString(null, getStyleSetting, "#845EC2", "player-name");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color Top: "green"</p>
|
||||
<p>Color Individual/Server: "gold"</p>*/
|
||||
public String getPlayerNameDecoration(Target selection, boolean getStyle) {
|
||||
public String getPlayerNameDecoration(Target selection, boolean getStyleSetting) {
|
||||
String def;
|
||||
if (selection == Target.TOP) {
|
||||
def = "green";
|
||||
@ -210,7 +267,7 @@ public class ConfigHandler {
|
||||
else {
|
||||
def = "gold";
|
||||
}
|
||||
return getDecorationString(selection, getStyle, def, "player-names");
|
||||
return getDecorationString(selection, getStyleSetting, def, "player-names");
|
||||
}
|
||||
|
||||
/** Returns true if playerNames Style is "bold", false if it is not.
|
||||
@ -228,22 +285,22 @@ public class ConfigHandler {
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "yellow"</p>*/
|
||||
public String getStatNameDecoration(Target selection, boolean getStyle) {
|
||||
return getDecorationString(selection, getStyle, "yellow", "stat-names");
|
||||
public String getStatNameDecoration(Target selection, boolean getStyleSetting) {
|
||||
return getDecorationString(selection, getStyleSetting, "yellow", "stat-names");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "#FFD52B"</p>*/
|
||||
public String getSubStatNameDecoration(Target selection, boolean getStyle) {
|
||||
return getDecorationString(selection, getStyle, "#FFD52B", "sub-stat-names");
|
||||
public String getSubStatNameDecoration(Target selection, boolean getStyleSetting) {
|
||||
return getDecorationString(selection, getStyleSetting, "#FFD52B", "sub-stat-names");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color Top: "#55AAFF"</p>
|
||||
<p>Color Individual/Server: "#ADE7FF"</p> */
|
||||
public String getStatNumberDecoration(Target selection, boolean getStyle) {
|
||||
public String getStatNumberDecoration(Target selection, boolean getStyleSetting) {
|
||||
String def;
|
||||
if (selection == Target.TOP) {
|
||||
def = "#55AAFF";
|
||||
@ -251,14 +308,14 @@ public class ConfigHandler {
|
||||
else {
|
||||
def = "#ADE7FF";
|
||||
}
|
||||
return getDecorationString(selection, getStyle, def,"stat-numbers");
|
||||
return getDecorationString(selection, getStyleSetting, def,"stat-numbers");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color Top: "yellow"</p>
|
||||
<p>Color Server: "gold"</p>*/
|
||||
public String getTitleDecoration(Target selection, boolean getStyle) {
|
||||
public String getTitleDecoration(Target selection, boolean getStyleSetting) {
|
||||
String def;
|
||||
if (selection == Target.TOP) {
|
||||
def = "yellow";
|
||||
@ -266,35 +323,35 @@ public class ConfigHandler {
|
||||
else {
|
||||
def = "gold";
|
||||
}
|
||||
return getDecorationString(selection, getStyle, def, "title");
|
||||
return getDecorationString(selection, getStyleSetting, def, "title");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "gold"</p>*/
|
||||
public String getTitleNumberDecoration(boolean getStyle) {
|
||||
return getDecorationString(Target.TOP, getStyle, "gold", "title-number");
|
||||
public String getTitleNumberDecoration(boolean getStyleSetting) {
|
||||
return getDecorationString(Target.TOP, getStyleSetting, "gold", "title-number");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "#FFB80E"</p>*/
|
||||
public String getServerNameDecoration(boolean getStyle) {
|
||||
return getDecorationString(Target.SERVER, getStyle, "#FFB80E", "server-name");
|
||||
public String getServerNameDecoration(boolean getStyleSetting) {
|
||||
return getDecorationString(Target.SERVER, getStyleSetting, "#FFB80E", "server-name");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "gold"</p>*/
|
||||
public String getRankNumberDecoration(boolean getStyle) {
|
||||
return getDecorationString(Target.TOP, getStyle, "gold", "rank-numbers");
|
||||
public String getRankNumberDecoration(boolean getStyleSetting) {
|
||||
return getDecorationString(Target.TOP, getStyleSetting, "gold", "rank-numbers");
|
||||
}
|
||||
|
||||
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
|
||||
<p>Style: "none"</p>
|
||||
<p>Color: "dark_gray"</p> */
|
||||
public String getDotsDecoration(boolean getStyle) {
|
||||
return getDecorationString(Target.TOP, getStyle, "dark_gray", "dots");
|
||||
public String getDotsDecoration(boolean getStyleSetting) {
|
||||
return getDecorationString(Target.TOP, getStyleSetting, "dark_gray", "dots");
|
||||
}
|
||||
|
||||
/** Returns a String representing the Unit that should be used for a certain Unit.Type.
|
||||
@ -316,12 +373,12 @@ public class ConfigHandler {
|
||||
/** Returns the config value for a color or style option in string-format, the supplied default value,
|
||||
or null if no configSection was found.
|
||||
@param selection the Target this decoration is meant for (Player, Server or Top)
|
||||
@param getStyle if true, the result will be a style String, otherwise a color String
|
||||
@param getStyleSetting if true, the result will be a style String, otherwise a color String
|
||||
@param defaultColor the default color to return if the config value cannot be found (for style, the default is always "none")
|
||||
@param pathName the config path to retrieve the value from*/
|
||||
private @Nullable String getDecorationString(Target selection, boolean getStyle, String defaultColor, String pathName){
|
||||
String path = getStyle ? pathName + "-style" : pathName;
|
||||
String defaultValue = getStyle ? "none" : defaultColor;
|
||||
private @Nullable String getDecorationString(Target selection, boolean getStyleSetting, String defaultColor, String pathName){
|
||||
String path = getStyleSetting ? pathName + "-style" : pathName;
|
||||
String defaultValue = getStyleSetting ? "none" : defaultColor;
|
||||
|
||||
ConfigurationSection section = getRelevantSection(selection);
|
||||
return section != null ? section.getString(path, defaultValue) : null;
|
||||
@ -329,6 +386,9 @@ public class ConfigHandler {
|
||||
|
||||
/** Returns the config section that contains the relevant color or style option. */
|
||||
private @Nullable ConfigurationSection getRelevantSection(Target selection) {
|
||||
if (selection == null) { //rather than rework the whole Target enum, I have added shared-stats as the null-option for now
|
||||
return config.getConfigurationSection("shared-stats");
|
||||
}
|
||||
switch (selection) {
|
||||
case TOP -> {
|
||||
return config.getConfigurationSection("top-list");
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.config;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.Main;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
@ -11,7 +12,7 @@ import com.tchristofferson.configupdater.ConfigUpdater;
|
||||
public class ConfigUpdateHandler {
|
||||
|
||||
/** Add new key-value pairs to the config without losing comments, using <a href="https://github.com/tchristofferson/Config-Updater">tchristofferson's Config-Updater</a> */
|
||||
public ConfigUpdateHandler(Main plugin, File configFile, double configVersion) {
|
||||
public ConfigUpdateHandler(Main plugin, File configFile, int configVersion) {
|
||||
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
|
||||
updateTopListDefault(configuration);
|
||||
updateDefaultColors(configuration);
|
||||
@ -19,14 +20,14 @@ public class ConfigUpdateHandler {
|
||||
try {
|
||||
configuration.save(configFile);
|
||||
ConfigUpdater.update(plugin, configFile.getName(), configFile);
|
||||
plugin.getLogger().warning("Your config has been updated to version " + configVersion +
|
||||
". This version includes some slight changes in the default color scheme, but none of your custom settings should have been changed!");
|
||||
MyLogger.logMsg("Your config has been updated to version " + configVersion +
|
||||
", but all of your custom settings should still be there!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** Adjusts the value for "top-list" to migrate the config file from versions 1 or 2 to version 3.*/
|
||||
/** Adjusts the value for "top-list" to migrate the config file from versions 1 or 2 to version 3 and above.*/
|
||||
private void updateTopListDefault(YamlConfiguration configuration) {
|
||||
String oldTitle = configuration.getString("top-list-title");
|
||||
if (oldTitle != null && oldTitle.equalsIgnoreCase("Top [x]")) {
|
||||
@ -34,11 +35,14 @@ public class ConfigUpdateHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/** Adjusts some of the default colors to migrate from versions 2 or 3 to version 4.1.*/
|
||||
/** Adjusts some of the default colors to migrate from versions 2 or 3 to version 4 and above.*/
|
||||
private void updateDefaultColors(YamlConfiguration configuration) {
|
||||
updateColor(configuration, "top-list.title", "yellow", "#FFD52B");
|
||||
updateColor(configuration, "top-list.title", "#FFEA40", "#FFD52B");
|
||||
updateColor(configuration, "top-list.stat-names", "yellow", "#FFD52B");
|
||||
updateColor(configuration, "top-list.stat-names", "#FFEA40", "#FFD52B");
|
||||
updateColor(configuration, "top-list.sub-stat-names", "#FFD52B", "yellow");
|
||||
|
||||
updateColor(configuration, "individual-statistics.stat-names", "yellow", "#FFD52B");
|
||||
updateColor(configuration, "individual-statistics.sub-stat-names", "#FFD52B", "yellow");
|
||||
updateColor(configuration, "total-server.title", "gold", "#55AAFF");
|
||||
|
@ -3,6 +3,8 @@ package com.gmail.artemis.the.gr8.playerstats.enums;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** This enum represents the colorscheme PlayerStats uses in its output messages.
|
||||
<p>GRAY: ChatColor Gray</p>
|
||||
<p>DARK_PURPLE: #6E3485 (used for default sub-titles, title-underscores and brackets)</p>
|
||||
@ -16,12 +18,22 @@ import net.kyori.adventure.text.format.TextColor;
|
||||
public enum PluginColor {
|
||||
GRAY (NamedTextColor.GRAY), //#AAAAAA
|
||||
DARK_PURPLE (TextColor.fromHexString("#6E3485")),
|
||||
LIGHT_PURPLE (TextColor.fromHexString("#845EC2")),
|
||||
BLUE (NamedTextColor.BLUE),
|
||||
MEDIUM_BLUE (TextColor.fromHexString("#55AAFF")),
|
||||
LIGHT_BLUE (TextColor.fromHexString("#55C6FF")),
|
||||
GOLD (NamedTextColor.GOLD), //#FFAA00
|
||||
MEDIUM_GOLD (TextColor.fromHexString("#FFD52B")),
|
||||
LIGHT_GOLD (TextColor.fromHexString("#FFEA40")),
|
||||
LIGHT_YELLOW (TextColor.fromHexString("#FFFF8E"));
|
||||
LIGHT_YELLOW (TextColor.fromHexString("#FFFF8E")),
|
||||
|
||||
NAME_1 (NamedTextColor.BLUE), //#5555FF - blue
|
||||
NAME_2 (TextColor.fromHexString("#4287F5")), //between blue and medium_blue
|
||||
NAME_3 (TextColor.fromHexString("#55AAFF")), //same as medium_blue
|
||||
NAME_4 (TextColor.fromHexString("#D65DB1")), //magenta-purple
|
||||
NAME_5 (TextColor.fromHexString("#EE8A19")), //dark orange
|
||||
NAME_6 (TextColor.fromHexString("#01C1A7")), //aqua-cyan-green-ish
|
||||
NAME_7 (TextColor.fromHexString("#46D858")); //light green
|
||||
|
||||
|
||||
private final TextColor color;
|
||||
@ -37,4 +49,26 @@ public enum PluginColor {
|
||||
public TextColor getConsoleColor() {
|
||||
return NamedTextColor.nearestTo(color);
|
||||
}
|
||||
}
|
||||
|
||||
public static TextColor getRandomNameColor() {
|
||||
return getRandomNameColor(false);
|
||||
}
|
||||
|
||||
public static TextColor getRandomNameColor(boolean isConsole) {
|
||||
Random randomizer = new Random();
|
||||
PluginColor color = switch (randomizer.nextInt(7)) {
|
||||
case 0 -> NAME_1;
|
||||
case 2 -> NAME_3;
|
||||
case 3 -> NAME_4;
|
||||
case 4 -> NAME_5;
|
||||
case 5 -> NAME_6;
|
||||
case 6 -> NAME_7;
|
||||
default -> NAME_2;
|
||||
};
|
||||
return getCorrespondingColor(color, isConsole);
|
||||
}
|
||||
|
||||
private static TextColor getCorrespondingColor(PluginColor nameColor, boolean isConsole) {
|
||||
return isConsole ? nameColor.getConsoleColor() : nameColor.getColor();
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.enums;
|
||||
|
||||
public enum StandardMessage {
|
||||
RELOADED_CONFIG,
|
||||
STILL_RELOADING,
|
||||
MISSING_STAT_NAME,
|
||||
MISSING_PLAYER_NAME,
|
||||
REQUEST_ALREADY_RUNNING,
|
||||
STILL_ON_SHARE_COOLDOWN,
|
||||
RESULTS_ALREADY_SHARED,
|
||||
STAT_RESULTS_TOO_OLD,
|
||||
UNKNOWN_ERROR,
|
||||
}
|
@ -3,7 +3,6 @@ package com.gmail.artemis.the.gr8.playerstats.enums;
|
||||
import org.bukkit.Statistic;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public enum Unit {
|
||||
NUMBER (Type.UNTYPED, "Times"),
|
||||
KM (Type.DISTANCE, "km"),
|
||||
@ -182,4 +181,4 @@ public enum Unit {
|
||||
Type() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
public class JoinListener implements Listener {
|
||||
|
||||
private final ThreadManager threadManager;
|
||||
private static ThreadManager threadManager;
|
||||
|
||||
public JoinListener(ThreadManager t) {
|
||||
threadManager = t;
|
||||
|
@ -1,7 +1,6 @@
|
||||
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;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -9,8 +8,7 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class StatRequest {
|
||||
public final class StatRequest {
|
||||
|
||||
private final CommandSender sender;
|
||||
private Statistic statistic;
|
||||
@ -38,10 +36,6 @@ public class StatRequest {
|
||||
return sender instanceof ConsoleCommandSender;
|
||||
}
|
||||
|
||||
public boolean isBukkitConsoleSender() {
|
||||
return sender instanceof ConsoleCommandSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit");
|
||||
}
|
||||
|
||||
public void setStatistic(Statistic statistic) {
|
||||
this.statistic = statistic;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
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) {
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.msg;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
|
||||
public class BukkitConsoleComponentFactory extends ComponentFactory {
|
||||
|
||||
public BukkitConsoleComponentFactory(ConfigHandler config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareColors() {
|
||||
PREFIX = PluginColor.GOLD.getConsoleColor();
|
||||
BRACKETS = PluginColor.GRAY.getConsoleColor();
|
||||
UNDERSCORE = PluginColor.DARK_PURPLE.getConsoleColor();
|
||||
MSG_MAIN = PluginColor.MEDIUM_BLUE.getConsoleColor();
|
||||
MSG_MAIN_2 = PluginColor.GOLD.getConsoleColor();
|
||||
MSG_ACCENT_2A = PluginColor.MEDIUM_GOLD.getConsoleColor();
|
||||
MSG_ACCENT_2B = PluginColor.LIGHT_YELLOW.getConsoleColor();
|
||||
CLICKED_MSG = PluginColor.LIGHT_PURPLE.getConsoleColor();
|
||||
HOVER_MSG = PluginColor.LIGHT_BLUE.getConsoleColor();
|
||||
HOVER_ACCENT = PluginColor.LIGHT_GOLD.getConsoleColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextColor getSharerNameColor() {
|
||||
return PluginColor.NAME_5.getConsoleColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextComponent getComponent(String content, @NotNull TextColor color, @Nullable TextDecoration style) {
|
||||
return getComponentBuilder(content, NamedTextColor.nearestTo(color), style).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextComponent.Builder getComponentBuilder(@Nullable String content, @NotNull TextColor color, @Nullable TextDecoration style) {
|
||||
TextComponent.Builder builder = text()
|
||||
.decorations(TextDecoration.NAMES.values(), false)
|
||||
.color(NamedTextColor.nearestTo(color));
|
||||
if (content != null) {
|
||||
builder.append(text(content));
|
||||
}
|
||||
if (style != null) {
|
||||
builder.decorate(style);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextColor getHexColor(String hexColor) {
|
||||
TextColor hex = TextColor.fromHexString(hexColor);
|
||||
return hex != null ? NamedTextColor.nearestTo(hex) : NamedTextColor.WHITE;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.TranslatableComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
@ -17,49 +18,123 @@ import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.kyori.adventure.text.Component.*;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
|
||||
/** Creates Components with the desired formatting. This class can put Strings
|
||||
into formatted Components with TextColor and TextDecoration, and turn
|
||||
certain Strings into appropriate LanguageKeys to return a TranslatableComponent.*/
|
||||
into formatted Components with TextColor and TextDecoration, or return empty Components
|
||||
or ComponentBuilders with the desired formatting.*/
|
||||
public class ComponentFactory {
|
||||
|
||||
private static ConfigHandler config;
|
||||
|
||||
protected TextColor PREFIX; //gold
|
||||
protected TextColor BRACKETS; //gray
|
||||
protected TextColor UNDERSCORE; //dark_purple
|
||||
protected TextColor MSG_MAIN; //medium_blue
|
||||
protected TextColor MSG_ACCENT; //blue
|
||||
protected TextColor MSG_MAIN_2; //gold
|
||||
protected TextColor MSG_ACCENT_2A; //medium_gold
|
||||
protected TextColor MSG_ACCENT_2B; //light_yellow
|
||||
protected TextColor HOVER_MSG; //light_blue
|
||||
protected TextColor CLICKED_MSG; //light_purple
|
||||
protected TextColor HOVER_ACCENT; //light_gold
|
||||
|
||||
|
||||
public ComponentFactory(ConfigHandler c) {
|
||||
config = c;
|
||||
prepareColors();
|
||||
}
|
||||
|
||||
protected void prepareColors() {
|
||||
PREFIX = PluginColor.GOLD.getColor();
|
||||
BRACKETS = PluginColor.GRAY.getColor();
|
||||
UNDERSCORE = PluginColor.DARK_PURPLE.getColor();
|
||||
MSG_MAIN = PluginColor.MEDIUM_BLUE.getColor();
|
||||
MSG_ACCENT = PluginColor.BLUE.getColor();
|
||||
MSG_MAIN_2 = PluginColor.GOLD.getColor();
|
||||
MSG_ACCENT_2A = PluginColor.MEDIUM_GOLD.getColor();
|
||||
MSG_ACCENT_2B = PluginColor.LIGHT_YELLOW.getColor();
|
||||
CLICKED_MSG = PluginColor.LIGHT_PURPLE.getColor();
|
||||
HOVER_MSG = PluginColor.LIGHT_BLUE.getColor();
|
||||
HOVER_ACCENT = PluginColor.LIGHT_GOLD.getColor();
|
||||
}
|
||||
|
||||
public TextColor prefix() {
|
||||
return PREFIX;
|
||||
}
|
||||
public TextColor brackets() {
|
||||
return BRACKETS;
|
||||
}
|
||||
public TextColor underscore() {
|
||||
return UNDERSCORE;
|
||||
}
|
||||
public TextColor msgMain() {
|
||||
return MSG_MAIN;
|
||||
}
|
||||
public TextColor msgAccent() {
|
||||
return MSG_ACCENT;
|
||||
}
|
||||
public TextColor msgMain2() {
|
||||
return MSG_MAIN_2;
|
||||
}
|
||||
public TextColor msgAccent2A() {
|
||||
return MSG_ACCENT_2A;
|
||||
}
|
||||
public TextColor msgAccent2B() {
|
||||
return MSG_ACCENT_2B;
|
||||
}
|
||||
public TextColor clickedMsg() {
|
||||
return CLICKED_MSG;
|
||||
}
|
||||
public TextColor hoverMsg() {
|
||||
return HOVER_MSG;
|
||||
}
|
||||
public TextColor hoverAccent() {
|
||||
return HOVER_ACCENT;
|
||||
}
|
||||
|
||||
public TextColor getExampleNameColor() {
|
||||
return MSG_ACCENT_2B;
|
||||
}
|
||||
public TextColor getSharerNameColor() {
|
||||
return getColorFromString(config.getSharerNameDecoration(false));
|
||||
}
|
||||
|
||||
|
||||
/** Returns [PlayerStats]. */
|
||||
public TextComponent pluginPrefixComponent(boolean isBukkitConsole) {
|
||||
public TextComponent pluginPrefixComponent() {
|
||||
return text("[")
|
||||
.color(PluginColor.GRAY.getColor())
|
||||
.append(text("PlayerStats").color(PluginColor.GOLD.getColor()))
|
||||
.color(BRACKETS)
|
||||
.append(text("PlayerStats").color(PREFIX))
|
||||
.append(text("]"));
|
||||
}
|
||||
|
||||
/** Returns [PlayerStats] surrounded by underscores on both sides. */
|
||||
public TextComponent prefixTitleComponent(boolean isBukkitConsole) {
|
||||
String underscores = "____________"; //12 underscores for both console and in-game
|
||||
TextColor underscoreColor = isBukkitConsole ?
|
||||
PluginColor.DARK_PURPLE.getConsoleColor() : PluginColor.DARK_PURPLE.getColor();
|
||||
|
||||
return text(underscores).color(underscoreColor)
|
||||
public TextComponent prefixTitleComponent() {
|
||||
//12 underscores for both console and in-game
|
||||
return text("____________").color(UNDERSCORE)
|
||||
.append(text(" ")) //4 spaces
|
||||
.append(pluginPrefixComponent(isBukkitConsole))
|
||||
.append(pluginPrefixComponent())
|
||||
.append(text(" ")) //4 spaces
|
||||
.append(text(underscores));
|
||||
.append(text("____________"));
|
||||
}
|
||||
|
||||
/** Returns a TextComponent with the input String as content, with color Gray and decoration Italic.*/
|
||||
public TextComponent subTitleComponent(String content) {
|
||||
return text(content).color(PluginColor.GRAY.getColor()).decorate(TextDecoration.ITALIC);
|
||||
return text(content).color(BRACKETS).decorate(TextDecoration.ITALIC);
|
||||
}
|
||||
|
||||
/** Returns a TextComponents that represents a full message, with [PlayerStats] prepended. */
|
||||
/** Returns a TextComponents in the style of a default plugin message, with color Medium_Blue. */
|
||||
public TextComponent messageComponent() {
|
||||
return text().color(PluginColor.MEDIUM_BLUE.getColor()).build();
|
||||
return text().color(MSG_MAIN).build();
|
||||
}
|
||||
|
||||
public TextComponent messageAccentComponent() {
|
||||
return text().color(MSG_ACCENT).build();
|
||||
}
|
||||
|
||||
public TextComponent.Builder playerNameBuilder(String playerName, Target selection) {
|
||||
@ -197,15 +272,14 @@ public class ComponentFactory {
|
||||
if (!(unitName == null && unitKey == null)) {
|
||||
TextComponent.Builder statUnitBuilder = getComponentBuilder(null,
|
||||
getColorFromString(config.getSubStatNameDecoration(selection, false)),
|
||||
getStyleFromString(config.getSubStatNameDecoration(selection, true)))
|
||||
.append(text("["));
|
||||
getStyleFromString(config.getSubStatNameDecoration(selection, true)));
|
||||
if (unitKey != null) {
|
||||
statUnitBuilder.append(translatable()
|
||||
.key(unitKey));
|
||||
} else {
|
||||
statUnitBuilder.append(text(unitName));
|
||||
}
|
||||
return statUnitBuilder.append(text("]")).build();
|
||||
return surroundingBracketComponent(statUnitBuilder.build());
|
||||
}
|
||||
else {
|
||||
return Component.empty();
|
||||
@ -224,14 +298,51 @@ public class ComponentFactory {
|
||||
if (config.useHoverText()) {
|
||||
heartComponent.hoverEvent(HoverEvent.showText(
|
||||
text(Unit.HEART.getLabel())
|
||||
.color(PluginColor.LIGHT_GOLD.getColor())
|
||||
.decorate(TextDecoration.ITALIC)));
|
||||
.color(HOVER_ACCENT)));
|
||||
}
|
||||
return Component.text().color(PluginColor.GRAY.getColor())
|
||||
return surroundingBracketComponent(heartComponent.build());
|
||||
}
|
||||
|
||||
public TextComponent shareButtonComponent(UUID shareCode) {
|
||||
return surroundingBracketComponent(
|
||||
text("Share")
|
||||
.color(HOVER_MSG)
|
||||
.clickEvent(ClickEvent.runCommand("/statshare " + shareCode))
|
||||
.hoverEvent(HoverEvent.showText(text("Click here to share this statistic in chat!")
|
||||
.color(HOVER_ACCENT))));
|
||||
}
|
||||
|
||||
public TextComponent hoveringStatResultComponent(TextComponent statResult) {
|
||||
return surroundingBracketComponent(
|
||||
text().append(text("Hover Here")
|
||||
.color(CLICKED_MSG)
|
||||
.decorate(TextDecoration.ITALIC)
|
||||
.hoverEvent(HoverEvent.showText(statResult)))
|
||||
.build());
|
||||
}
|
||||
|
||||
public TextComponent messageSharedComponent(Component playerName) {
|
||||
return surroundingBracketComponent(
|
||||
text().append(
|
||||
getComponent("Shared by",
|
||||
getColorFromString(config.getSharedByTextDecoration(false)),
|
||||
getStyleFromString(config.getSharedByTextDecoration(true))))
|
||||
.append(space())
|
||||
.append(playerName)
|
||||
.build());
|
||||
}
|
||||
|
||||
public TextComponent sharerNameComponent(String sharerName) {
|
||||
return getComponent(sharerName,
|
||||
getSharerNameColor(),
|
||||
getStyleFromString(config.getSharerNameDecoration(true)));
|
||||
}
|
||||
|
||||
private TextComponent surroundingBracketComponent(TextComponent component) {
|
||||
return getComponent(null, BRACKETS, null)
|
||||
.append(text("["))
|
||||
.append(heartComponent)
|
||||
.append(text("]"))
|
||||
.build();
|
||||
.append(component)
|
||||
.append(text("]"));
|
||||
}
|
||||
|
||||
public TextComponent titleComponent(String content, Target selection) {
|
||||
@ -266,11 +377,11 @@ public class ComponentFactory {
|
||||
getStyleFromString(config.getDotsDecoration(true)));
|
||||
}
|
||||
|
||||
private TextComponent getComponent(String content, TextColor color, @Nullable TextDecoration style) {
|
||||
protected TextComponent getComponent(String content, @NotNull TextColor color, @Nullable TextDecoration style) {
|
||||
return getComponentBuilder(content, color, style).build();
|
||||
}
|
||||
|
||||
private TextComponent.Builder getComponentBuilder(@Nullable String content, TextColor color, @Nullable TextDecoration style) {
|
||||
protected TextComponent.Builder getComponentBuilder(@Nullable String content, TextColor color, @Nullable TextDecoration style) {
|
||||
TextComponent.Builder builder = text()
|
||||
.decorations(TextDecoration.NAMES.values(), false)
|
||||
.color(color);
|
||||
@ -287,7 +398,7 @@ public class ComponentFactory {
|
||||
if (configString != null) {
|
||||
try {
|
||||
if (configString.contains("#")) {
|
||||
return TextColor.fromHexString(configString);
|
||||
return getHexColor(configString);
|
||||
}
|
||||
else {
|
||||
return getTextColorByName(configString);
|
||||
@ -300,6 +411,10 @@ public class ComponentFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected TextColor getHexColor(String hexColor) {
|
||||
return TextColor.fromHexString(hexColor);
|
||||
}
|
||||
|
||||
private TextColor getTextColorByName(String textColor) {
|
||||
Index<String, NamedTextColor> names = NamedTextColor.NAMES;
|
||||
return names.value(textColor);
|
||||
|
@ -4,135 +4,296 @@ import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.*;
|
||||
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.EnumHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static net.kyori.adventure.text.Component.*;
|
||||
|
||||
/** Composes messages to send to Players or Console. This class is responsible
|
||||
/** Composes messages to send to a Player or Console. This class is responsible
|
||||
for constructing a final Component with the text content of the desired message.
|
||||
The component parts (with appropriate formatting) are supplied by a ComponentFactory.*/
|
||||
The component parts (with appropriate formatting) are supplied by a ComponentFactory.
|
||||
By default, this class works with the default ComponentFactory, but you can
|
||||
give it a different ComponentFactory upon creation.*/
|
||||
public class MessageWriter {
|
||||
|
||||
private static ConfigHandler config;
|
||||
private static ComponentFactory componentFactory;
|
||||
protected static ConfigHandler config;
|
||||
|
||||
private final ComponentFactory componentFactory;
|
||||
private final LanguageKeyHandler languageKeyHandler;
|
||||
private final NumberFormatter formatter;
|
||||
|
||||
public MessageWriter(ConfigHandler c) {
|
||||
config = c;
|
||||
public MessageWriter(ConfigHandler config) {
|
||||
this (config, new ComponentFactory(config));
|
||||
}
|
||||
|
||||
public MessageWriter(ConfigHandler configHandler, ComponentFactory factory) {
|
||||
config = configHandler;
|
||||
componentFactory = factory;
|
||||
|
||||
formatter = new NumberFormatter();
|
||||
languageKeyHandler = new LanguageKeyHandler();
|
||||
getComponentFactory();
|
||||
MyLogger.logMsg("MessageWriter created with factory: " + componentFactory.getClass().getSimpleName(), DebugLevel.MEDIUM);
|
||||
}
|
||||
|
||||
public static void updateComponentFactory() {
|
||||
getComponentFactory();
|
||||
}
|
||||
|
||||
private static void getComponentFactory() {
|
||||
if (config.useFestiveFormatting() || config.useRainbowMode()) {
|
||||
componentFactory = new PrideComponentFactory(config);
|
||||
}
|
||||
else {
|
||||
componentFactory = new ComponentFactory(config);
|
||||
}
|
||||
}
|
||||
|
||||
public TextComponent reloadedConfig(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent reloadedConfig() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content("Config reloaded!"));
|
||||
}
|
||||
|
||||
public TextComponent stillReloading(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent stillReloading() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"The plugin is (re)loading, your request will be processed when it is done!"));
|
||||
}
|
||||
|
||||
public TextComponent waitAMoment(boolean longWait, boolean isBukkitConsole) {
|
||||
public TextComponent waitAMoment(boolean longWait) {
|
||||
String msg = longWait ? "Calculating statistics, this may take a minute..." :
|
||||
"Calculating statistics, this may take a few moments...";
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(msg));
|
||||
}
|
||||
|
||||
public TextComponent missingStatName(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent missingStatName() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"Please provide a valid statistic name!"));
|
||||
}
|
||||
|
||||
public TextComponent missingSubStatName(Statistic.Type statType, boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent missingSubStatName(Statistic.Type statType) {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"Please add a valid " + EnumHandler.getSubStatTypeName(statType) + " to look up this statistic!"));
|
||||
}
|
||||
|
||||
public TextComponent missingPlayerName(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent missingPlayerName() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"Please specify a valid player-name!"));
|
||||
}
|
||||
|
||||
public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry, boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent wrongSubStatType(Statistic.Type statType, String subStatName) {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageAccentComponent().content("\"" + subStatName + "\""))
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"\"" + subStatEntry + "\" is not a valid " + EnumHandler.getSubStatTypeName(statType) + "!"));
|
||||
"is not a valid " + EnumHandler.getSubStatTypeName(statType) + "!"));
|
||||
}
|
||||
|
||||
public TextComponent requestAlreadyRunning(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
public TextComponent requestAlreadyRunning() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"Please wait for your previous lookup to finish!"));
|
||||
}
|
||||
|
||||
public TextComponent unknownError(boolean isBukkitConsole) {
|
||||
return componentFactory.pluginPrefixComponent(isBukkitConsole)
|
||||
//TODO Make this say amount of time left
|
||||
public TextComponent stillOnShareCoolDown() {
|
||||
int waitTime = config.getStatShareWaitingTime();
|
||||
String minutes = waitTime == 1 ? " minute" : " minutes";
|
||||
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content("You need to wait")
|
||||
.append(space())
|
||||
.append(componentFactory.messageAccentComponent()
|
||||
.content(waitTime + minutes))
|
||||
.append(space())
|
||||
.append(text("between sharing!")));
|
||||
}
|
||||
|
||||
public TextComponent resultsAlreadyShared() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content("You already shared these results!"));
|
||||
}
|
||||
|
||||
public TextComponent statResultsTooOld() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"It has been too long since you looked up this statistic, please repeat the original command!"));
|
||||
}
|
||||
|
||||
public TextComponent unknownError() {
|
||||
return componentFactory.pluginPrefixComponent()
|
||||
.append(space())
|
||||
.append(componentFactory.messageComponent().content(
|
||||
"Something went wrong with your request, " +
|
||||
"please try again or see /statistic for a usage explanation!"));
|
||||
}
|
||||
|
||||
public TextComponent formatPlayerStat(int stat, @NotNull StatRequest request) {
|
||||
return Component.text()
|
||||
public TextComponent usageExamples() {
|
||||
return new ExampleMessage(componentFactory);
|
||||
}
|
||||
|
||||
public TextComponent helpMsg(boolean isConsoleSender) {
|
||||
return new HelpMessage(componentFactory,
|
||||
(!isConsoleSender && config.useHoverText()),
|
||||
config.getTopListMaxSize());
|
||||
}
|
||||
|
||||
public BiFunction<UUID, CommandSender, TextComponent> formattedPlayerStatFunction(int stat, @NotNull StatRequest request) {
|
||||
TextComponent playerStat = Component.text()
|
||||
.append(componentFactory.playerNameBuilder(request.getPlayerName(), Target.PLAYER)
|
||||
.append(text(":"))
|
||||
.append(space()))
|
||||
.append(getStatNumberComponent(request.getStatistic(), stat, Target.PLAYER, request.isConsoleSender()))
|
||||
.append(space())
|
||||
.append(getStatNameComponent(request))
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender()))
|
||||
.build(); //space is provided by statUnitComponent
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender())) //space is provided by statUnitComponent
|
||||
.build();
|
||||
|
||||
return getFormattingFunction(playerStat, Target.PLAYER);
|
||||
}
|
||||
|
||||
public TextComponent formatTopStats(@NotNull LinkedHashMap<String, Integer> topStats, @NotNull StatRequest request) {
|
||||
TextComponent.Builder topList = Component.text()
|
||||
.append(newline())
|
||||
.append(componentFactory.pluginPrefixComponent(request.isBukkitConsoleSender())).append(space())
|
||||
.append(componentFactory.titleComponent(config.getTopStatsTitle(), Target.TOP)).append(space())
|
||||
.append(componentFactory.titleNumberComponent(topStats.size())).append(space())
|
||||
.append(getStatNameComponent(request)) //space is provided by statUnitComponent
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender()));
|
||||
public BiFunction<UUID, CommandSender, TextComponent> formattedServerStatFunction(long stat, @NotNull StatRequest request) {
|
||||
TextComponent serverStat = text()
|
||||
.append(componentFactory.titleComponent(config.getServerTitle(), Target.SERVER))
|
||||
.append(space())
|
||||
.append(componentFactory.serverNameComponent(config.getServerName()))
|
||||
.append(space())
|
||||
.append(getStatNumberComponent(request.getStatistic(), stat, Target.SERVER, request.isConsoleSender()))
|
||||
.append(space())
|
||||
.append(getStatNameComponent(request))
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender())) //space is provided by statUnit
|
||||
.build();
|
||||
|
||||
return getFormattingFunction(serverStat, Target.SERVER);
|
||||
}
|
||||
|
||||
public BiFunction<UUID, CommandSender, TextComponent> formattedTopStatFunction(@NotNull LinkedHashMap<String, Integer> topStats, @NotNull StatRequest request) {
|
||||
final TextComponent title = getTopStatsTitle(request, topStats.size());
|
||||
final TextComponent shortTitle = getTopStatsTitleShort(request, topStats.size());
|
||||
final TextComponent list = getTopStatList(topStats, request);
|
||||
final boolean useEnters = config.useEnters(Target.TOP, false);
|
||||
final boolean useEntersForShared = config.useEnters(Target.TOP, true);
|
||||
|
||||
return (shareCode, sender) -> {
|
||||
TextComponent.Builder topBuilder = text();
|
||||
|
||||
//if we're adding a share-button
|
||||
if (shareCode != null) {
|
||||
if (useEnters) {
|
||||
topBuilder.append(newline());
|
||||
}
|
||||
topBuilder.append(title)
|
||||
.append(space())
|
||||
.append(componentFactory.shareButtonComponent(shareCode))
|
||||
.append(list);
|
||||
}
|
||||
//if we're adding a "shared by" component
|
||||
else if (sender != null) {
|
||||
if (useEntersForShared) {
|
||||
topBuilder.append(newline());
|
||||
}
|
||||
topBuilder.append(shortTitle)
|
||||
.append(space())
|
||||
.append(componentFactory.hoveringStatResultComponent(text()
|
||||
.append(title)
|
||||
.append(list)
|
||||
.build()))
|
||||
.append(newline())
|
||||
.append(componentFactory.messageSharedComponent(
|
||||
getSharerNameComponent(sender)));
|
||||
}
|
||||
//if we're not adding a share-button or a "shared by" component
|
||||
else {
|
||||
if (useEnters) {
|
||||
topBuilder.append(newline());
|
||||
}
|
||||
topBuilder.append(title)
|
||||
.append(list);
|
||||
}
|
||||
return topBuilder.build();
|
||||
};
|
||||
}
|
||||
|
||||
private BiFunction<UUID, CommandSender, TextComponent> getFormattingFunction(@NotNull TextComponent statResult, Target selection) {
|
||||
boolean useEnters = config.useEnters(selection, false);
|
||||
boolean useEntersForShared = config.useEnters(selection, true);
|
||||
|
||||
return (shareCode, sender) -> {
|
||||
TextComponent.Builder statBuilder = text();
|
||||
|
||||
//if we're adding a share-button
|
||||
if (shareCode != null) {
|
||||
if (useEnters) {
|
||||
statBuilder.append(newline());
|
||||
}
|
||||
statBuilder.append(statResult)
|
||||
.append(space())
|
||||
.append(componentFactory.shareButtonComponent(shareCode));
|
||||
}
|
||||
//if we're adding a "shared by" component
|
||||
else if (sender != null) {
|
||||
if (useEntersForShared) {
|
||||
statBuilder.append(newline());
|
||||
}
|
||||
statBuilder.append(statResult)
|
||||
.append(newline())
|
||||
.append(componentFactory.messageSharedComponent(
|
||||
getSharerNameComponent(sender)));
|
||||
}
|
||||
//if we're not adding a share-button or a "shared by" component
|
||||
else {
|
||||
if (useEnters) {
|
||||
statBuilder.append(newline());
|
||||
}
|
||||
statBuilder.append(statResult);
|
||||
}
|
||||
return statBuilder.build();
|
||||
};
|
||||
}
|
||||
|
||||
private Component getSharerNameComponent(CommandSender sender) {
|
||||
if (sender instanceof Player player) {
|
||||
Component senderName = EasterEggProvider.getPlayerName(player);
|
||||
if (senderName != null) {
|
||||
return senderName;
|
||||
}
|
||||
}
|
||||
return componentFactory.sharerNameComponent(sender.getName());
|
||||
}
|
||||
|
||||
private TextComponent getTopStatsTitle(StatRequest request, int statListSize) {
|
||||
return Component.text()
|
||||
.append(componentFactory.pluginPrefixComponent()).append(space())
|
||||
.append(componentFactory.titleComponent(config.getTopStatsTitle(), Target.TOP)).append(space())
|
||||
.append(componentFactory.titleNumberComponent(statListSize)).append(space())
|
||||
.append(getStatNameComponent(request)) //space is provided by statUnitComponent
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private TextComponent getTopStatsTitleShort(StatRequest request, int statListSize) {
|
||||
return Component.text()
|
||||
.append(componentFactory.titleComponent(config.getTopStatsTitle(), Target.TOP)).append(space())
|
||||
.append(componentFactory.titleNumberComponent(statListSize)).append(space())
|
||||
.append(getStatNameComponent(request)) //space is provided by statUnitComponent
|
||||
.build();
|
||||
}
|
||||
|
||||
private TextComponent getTopStatList(LinkedHashMap<String, Integer> topStats, StatRequest request) {
|
||||
TextComponent.Builder topList = Component.text();
|
||||
boolean useDots = config.useDots();
|
||||
boolean boldNames = config.playerNameIsBold();
|
||||
Set<String> playerNames = topStats.keySet();
|
||||
@ -141,7 +302,7 @@ public class MessageWriter {
|
||||
for (String playerName : playerNames) {
|
||||
TextComponent.Builder playerNameBuilder = componentFactory.playerNameBuilder(playerName, Target.TOP);
|
||||
topList.append(newline())
|
||||
.append(componentFactory.rankingNumberComponent(++count + "."))
|
||||
.append(componentFactory.rankingNumberComponent(" " + ++count + "."))
|
||||
.append(space());
|
||||
if (useDots) {
|
||||
topList.append(playerNameBuilder)
|
||||
@ -159,30 +320,6 @@ public class MessageWriter {
|
||||
return topList.build();
|
||||
}
|
||||
|
||||
public TextComponent formatServerStat(long stat, @NotNull StatRequest request) {
|
||||
return Component.text()
|
||||
.append(componentFactory.titleComponent(config.getServerTitle(), Target.SERVER))
|
||||
.append(space())
|
||||
.append(componentFactory.serverNameComponent(config.getServerName()))
|
||||
.append(space())
|
||||
.append(getStatNumberComponent(request.getStatistic(), stat, Target.SERVER, request.isConsoleSender()))
|
||||
.append(space())
|
||||
.append(getStatNameComponent(request))
|
||||
.append(getStatUnitComponent(request.getStatistic(), request.getSelection(), request.isConsoleSender())) //space is provided by statUnit
|
||||
.build();
|
||||
}
|
||||
|
||||
public TextComponent usageExamples(boolean isBukkitConsole) {
|
||||
return new ExampleMessage(componentFactory, isBukkitConsole);
|
||||
}
|
||||
|
||||
public TextComponent helpMsg(boolean isConsoleSender) {
|
||||
return new HelpMessage(componentFactory,
|
||||
config.useHoverText() && !isConsoleSender,
|
||||
isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit"),
|
||||
config.getTopListMaxSize());
|
||||
}
|
||||
|
||||
/** Depending on the config settings, return either a TranslatableComponent representing
|
||||
the statName (and potential subStatName), or a TextComponent with capitalized English names.*/
|
||||
private TextComponent getStatNameComponent(StatRequest request) {
|
||||
|
@ -0,0 +1,158 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.msg;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.Main;
|
||||
import com.gmail.artemis.the.gr8.playerstats.ShareManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage.*;
|
||||
|
||||
public class OutputManager {
|
||||
|
||||
private static BukkitAudiences adventure;
|
||||
private static ShareManager shareManager;
|
||||
private static MessageWriter msg;
|
||||
private static MessageWriter consoleMsg;
|
||||
|
||||
private static EnumMap<StandardMessage, Function<MessageWriter, TextComponent>> standardMessages;
|
||||
|
||||
public OutputManager(ConfigHandler config) {
|
||||
adventure = Main.adventure();
|
||||
shareManager = ShareManager.getInstance(config);
|
||||
|
||||
getMessageWriters(config);
|
||||
prepareFunctions();
|
||||
}
|
||||
|
||||
public void updateMessageWriters(ConfigHandler config) {
|
||||
getMessageWriters(config);
|
||||
}
|
||||
|
||||
public void sendFeedbackMsg(CommandSender sender, StandardMessage message) {
|
||||
if (message != null) {
|
||||
adventure.sender(sender).sendMessage(standardMessages.get(message)
|
||||
.apply(getWriter(sender)));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFeedbackMsgWaitAMoment(CommandSender sender, boolean longWait) {
|
||||
adventure.sender(sender).sendMessage(getWriter(sender)
|
||||
.waitAMoment(longWait));
|
||||
}
|
||||
|
||||
public void sendFeedbackMsgMissingSubStat(CommandSender sender, Statistic.Type statType) {
|
||||
adventure.sender(sender).sendMessage(getWriter(sender)
|
||||
.missingSubStatName(statType));
|
||||
}
|
||||
|
||||
public void sendFeedbackMsgWrongSubStat(CommandSender sender, Statistic.Type statType, String subStatName) {
|
||||
if (subStatName == null) {
|
||||
sendFeedbackMsgMissingSubStat(sender, statType);
|
||||
} else {
|
||||
adventure.sender(sender).sendMessage(getWriter(sender)
|
||||
.wrongSubStatType(statType, subStatName));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendExamples(CommandSender sender) {
|
||||
adventure.sender(sender).sendMessage(getWriter(sender)
|
||||
.usageExamples());
|
||||
}
|
||||
|
||||
public void sendHelp(CommandSender sender) {
|
||||
adventure.sender(sender).sendMessage(getWriter(sender)
|
||||
.helpMsg(sender instanceof ConsoleCommandSender));
|
||||
}
|
||||
|
||||
public void shareStatResults(@NotNull TextComponent statResult) {
|
||||
adventure.players().sendMessage(statResult);
|
||||
}
|
||||
|
||||
public void sendPlayerStat(@NotNull StatRequest request, int playerStat) {
|
||||
CommandSender sender = request.getCommandSender();
|
||||
BiFunction<UUID, CommandSender, TextComponent> buildFunction =
|
||||
getWriter(sender).formattedPlayerStatFunction(playerStat, request);
|
||||
|
||||
processAndSend(sender, buildFunction);
|
||||
}
|
||||
|
||||
public void sendServerStat(@NotNull StatRequest request, long serverStat) {
|
||||
CommandSender sender = request.getCommandSender();
|
||||
BiFunction<UUID, CommandSender, TextComponent> buildFunction =
|
||||
getWriter(sender).formattedServerStatFunction(serverStat, request);
|
||||
|
||||
processAndSend(sender, buildFunction);
|
||||
}
|
||||
|
||||
public void sendTopStat(@NotNull StatRequest request, LinkedHashMap<String, Integer> topStats) {
|
||||
CommandSender sender = request.getCommandSender();
|
||||
BiFunction<UUID, CommandSender, TextComponent> buildFunction =
|
||||
getWriter(sender).formattedTopStatFunction(topStats, request);
|
||||
|
||||
processAndSend(sender, buildFunction);
|
||||
}
|
||||
|
||||
private void processAndSend(CommandSender sender, BiFunction<UUID, CommandSender, TextComponent> buildFunction) {
|
||||
if (shareManager.isEnabled() && shareManager.senderHasPermission(sender)) {
|
||||
|
||||
UUID shareCode = shareManager.saveStatResult(sender.getName(), buildFunction.apply(null, sender));
|
||||
adventure.sender(sender).sendMessage(
|
||||
buildFunction.apply(shareCode, null));
|
||||
}
|
||||
else {
|
||||
adventure.sender(sender).sendMessage(
|
||||
buildFunction.apply(null, null));
|
||||
}
|
||||
}
|
||||
|
||||
private MessageWriter getWriter(CommandSender sender) {
|
||||
return sender instanceof ConsoleCommandSender ? consoleMsg : msg;
|
||||
}
|
||||
|
||||
private void getMessageWriters(ConfigHandler config) {
|
||||
boolean isBukkit = Bukkit.getName().equalsIgnoreCase("CraftBukkit");
|
||||
if (config.useRainbowMode() ||
|
||||
(config.useFestiveFormatting() && LocalDate.now().getMonth().equals(Month.JUNE))) {
|
||||
msg = new MessageWriter(config, new PrideComponentFactory(config));
|
||||
}
|
||||
else {
|
||||
msg = new MessageWriter(config);
|
||||
}
|
||||
|
||||
if (!isBukkit) {
|
||||
consoleMsg = msg;
|
||||
} else {
|
||||
consoleMsg = new MessageWriter(config, new BukkitConsoleComponentFactory(config));
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareFunctions() {
|
||||
standardMessages = new EnumMap<>(StandardMessage.class);
|
||||
|
||||
standardMessages.put(RELOADED_CONFIG, (MessageWriter::reloadedConfig));
|
||||
standardMessages.put(STILL_RELOADING, (MessageWriter::stillReloading));
|
||||
standardMessages.put(MISSING_STAT_NAME, (MessageWriter::missingStatName));
|
||||
standardMessages.put(MISSING_PLAYER_NAME, (MessageWriter::missingPlayerName));
|
||||
standardMessages.put(REQUEST_ALREADY_RUNNING, (MessageWriter::requestAlreadyRunning));
|
||||
standardMessages.put(STILL_ON_SHARE_COOLDOWN, (MessageWriter::stillOnShareCoolDown));
|
||||
standardMessages.put(RESULTS_ALREADY_SHARED, (MessageWriter::resultsAlreadyShared));
|
||||
standardMessages.put(STAT_RESULTS_TOO_OLD, (MessageWriter::statResultsTooOld));
|
||||
standardMessages.put(UNKNOWN_ERROR, (MessageWriter::unknownError));
|
||||
}
|
||||
}
|
@ -2,66 +2,95 @@ package com.gmail.artemis.the.gr8.playerstats.msg;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.util.Random;
|
||||
|
||||
import static net.kyori.adventure.text.Component.*;
|
||||
|
||||
|
||||
public class PrideComponentFactory extends ComponentFactory {
|
||||
|
||||
private static ConfigHandler config;
|
||||
|
||||
public PrideComponentFactory(ConfigHandler c) {
|
||||
super(c);
|
||||
config = c;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TextComponent prefixTitleComponent(boolean isBukkitConsole) {
|
||||
if (cancelRainbow(isBukkitConsole)) {
|
||||
return super.prefixTitleComponent(isBukkitConsole);
|
||||
}
|
||||
else {
|
||||
String title = "<rainbow:16>____________ [PlayerStats] ____________</rainbow>"; //12 underscores
|
||||
return text()
|
||||
.append(MiniMessage.miniMessage().deserialize(title))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponent pluginPrefixComponent(boolean isConsoleSender) {
|
||||
if (cancelRainbow(isConsoleSender)) {
|
||||
return super.pluginPrefixComponent(isConsoleSender);
|
||||
}
|
||||
protected void prepareColors() {
|
||||
PREFIX = PluginColor.GOLD.getColor();
|
||||
BRACKETS = PluginColor.GRAY.getColor();
|
||||
UNDERSCORE = PluginColor.DARK_PURPLE.getColor();
|
||||
MSG_MAIN = PluginColor.GRAY.getColor(); //difference 1
|
||||
MSG_ACCENT = PluginColor.LIGHT_GOLD.getColor(); //difference 2
|
||||
MSG_MAIN_2 = PluginColor.GOLD.getColor();
|
||||
MSG_ACCENT_2A = PluginColor.MEDIUM_GOLD.getColor();
|
||||
MSG_ACCENT_2B = PluginColor.LIGHT_YELLOW.getColor();
|
||||
CLICKED_MSG = PluginColor.LIGHT_PURPLE.getColor();
|
||||
HOVER_MSG = PluginColor.LIGHT_BLUE.getColor();
|
||||
HOVER_ACCENT = PluginColor.LIGHT_GOLD.getColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextColor getExampleNameColor() {
|
||||
return getSharerNameColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextColor getSharerNameColor() {
|
||||
return PluginColor.getRandomNameColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextComponent prefixTitleComponent() {
|
||||
String title = "<rainbow:16>____________ [PlayerStats] ____________</rainbow>"; //12 underscores
|
||||
return text()
|
||||
.append(MiniMessage.miniMessage()
|
||||
.deserialize("<#fe3e3e>[</#fe3e3e>" +
|
||||
"<#fe5640>P</#fe5640>" +
|
||||
"<#f67824>l</#f67824>" +
|
||||
"<#ee8a19>a</#ee8a19>" +
|
||||
"<#e49b0f>y</#e49b0f>" +
|
||||
"<#cbbd03>e</#cbbd03>" +
|
||||
"<#bccb01>r</#bccb01>" +
|
||||
"<#8aee08>S</#8aee08>" +
|
||||
"<#45fe31>t</#45fe31>" +
|
||||
"<#01c1a7>a</#01c1a7>" +
|
||||
"<#0690d4>t</#0690d4>" +
|
||||
"<#205bf3>s</#205bf3>" +
|
||||
"<#6c15fa>]</#6c15fa>"))
|
||||
.append(MiniMessage.miniMessage().deserialize(title))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Don't use rainbow formatting if the rainbow Prefix is disabled,
|
||||
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))) ||
|
||||
(isBukkitConsole);
|
||||
@Override
|
||||
public TextComponent pluginPrefixComponent() {
|
||||
Random randomizer = new Random();
|
||||
if (randomizer.nextBoolean()) {
|
||||
return backwardsPluginPrefixComponent();
|
||||
}
|
||||
return text()
|
||||
.append(MiniMessage.miniMessage()
|
||||
.deserialize("<#f74040>[</#f74040>" +
|
||||
"<#F54D39>P</#F54D39>" +
|
||||
"<#F16E28>l</#F16E28>" +
|
||||
"<#ee8a19>a</#ee8a19>" +
|
||||
"<#EEA019>y</#EEA019>" +
|
||||
"<#F7C522>e</#F7C522>" +
|
||||
"<#C1DA15>r</#C1DA15>" +
|
||||
"<#84D937>S</#84D937>" +
|
||||
"<#46D858>t</#46D858>" +
|
||||
"<#01c1a7>a</#01c1a7>" +
|
||||
"<#1F8BEB>t</#1F8BEB>" +
|
||||
"<#3341E6>s</#3341E6>" +
|
||||
"<#631ae6>]</#631ae6>"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public TextComponent backwardsPluginPrefixComponent() {
|
||||
return text()
|
||||
.append(MiniMessage.miniMessage()
|
||||
.deserialize("<#631ae6>[</#631ae6>" +
|
||||
"<#3341E6>P</#3341E6>" +
|
||||
"<#1F8BEB>l</#1F8BEB>" +
|
||||
"<#01c1a7>a</#01c1a7>" +
|
||||
"<#46D858>y</#46D858>" +
|
||||
"<#84D937>e</#84D937>" +
|
||||
"<#C1DA15>r</#C1DA15>" +
|
||||
"<#F7C522>S</#F7C522>" +
|
||||
"<#EEA019>t</#EEA019>" +
|
||||
"<#ee8a19>a</#ee8a19>" +
|
||||
"<#f67824>t</#f67824>" +
|
||||
"<#f76540>s</#f76540>" +
|
||||
"<#f74040>]</#f74040>"))
|
||||
.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
//This class is just for fun, and adds some silly names for players on my server.
|
||||
//It does not impact the rest of the plugin, and will only be used for the players mentioned in here.
|
||||
public class EasterEggProvider {
|
||||
|
||||
private static final Random random;
|
||||
|
||||
static{
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
public static Component getPlayerName(Player player) {
|
||||
int sillyNumber = getSillyNumber();
|
||||
String playerName = null;
|
||||
switch (player.getUniqueId().toString()) {
|
||||
case "8fb811dc-2ceb-4528-9951-cf803e0550a1" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<bold><#7d330e><#D17300>b</#D17300>e<#D17300>e</#D17300> b<#D17300>o</#D17300>i";
|
||||
}
|
||||
}
|
||||
case "b7d2e46f-cc89-434c-9757-f71a681e168a" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:#7402d1:#e31bc5:#7402d1>purple slime</gradient>";
|
||||
}
|
||||
}
|
||||
case "46dd0c5a-2b51-4ee6-80e8-29deca6dedc1" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:#f74040:#FF6600:#f74040>fire demon</gradient>";
|
||||
}
|
||||
else if (sillyNumberIsBetween(sillyNumber, 69, 69)) {
|
||||
playerName = "<gradient:blue:#b01bd1:blue>best admin</gradient>";
|
||||
}
|
||||
}
|
||||
case "0dc5336b-acd2-4dc3-a5e9-0aa9b8f113f7" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:#f73bdb:#fc8bec:#f73bdb>an UwU sister</gradient>";
|
||||
}
|
||||
}
|
||||
case "10dd9f02-5ec2-4f60-816c-48bb9e2ddf47" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:gold:#fc7f03:-1>gottem</gradient>";
|
||||
}
|
||||
}
|
||||
case "e4c5dfef-bbcc-4012-9f74-879d28fff431" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 69, 69)) {
|
||||
playerName = "<gradient:blue:#03befc:blue>nice admin</gradient>";
|
||||
}
|
||||
}
|
||||
case "29c0911d-695a-4c31-817f-3a065a7144b7" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:gold:#00ff7b:#03b6fc>Tzzzzzzzzz</gradient>";
|
||||
}
|
||||
}
|
||||
case "0410f9c7-f042-479c-ac80-49d46be655e9" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 20)) {
|
||||
playerName = "<gradient:gold:#ff245e:#a511f0:#7c0aff>SamanthaCation</gradient>";
|
||||
}
|
||||
}
|
||||
case "0bd803b6-f6c2-41bd-9872-74d8754a29fd" -> {
|
||||
if (sillyNumberIsBetween(sillyNumber, 0, 30)) {
|
||||
playerName = "<gradient:#14f7a0:#4287f5>Bradwurst</gradient>";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (playerName == null) {
|
||||
return null;
|
||||
} else {
|
||||
return MiniMessage.miniMessage().deserialize(playerName);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getSillyNumber() {
|
||||
return random.nextInt(100);
|
||||
}
|
||||
|
||||
private static boolean sillyNumberIsBetween(int sillyNumber, int lowerBound, int upperBound) {
|
||||
return sillyNumber >= lowerBound && sillyNumber <= upperBound;
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.BukkitConsoleComponentFactory;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.ComponentFactory;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
@ -19,37 +18,35 @@ public class ExampleMessage implements TextComponent {
|
||||
private final TextComponent exampleMessage;
|
||||
private final ComponentFactory componentFactory;
|
||||
|
||||
public ExampleMessage(ComponentFactory componentFactory, boolean isBukkitConsole) {
|
||||
this.componentFactory = componentFactory;
|
||||
exampleMessage = getExampleMessage(isBukkitConsole);
|
||||
public ExampleMessage(ComponentFactory factory) {
|
||||
componentFactory = factory;
|
||||
exampleMessage = getExampleMessage();
|
||||
}
|
||||
|
||||
public TextComponent getExampleMessage(boolean isBukkitConsole) {
|
||||
TextColor mainColor = isBukkitConsole ? PluginColor.GOLD.getConsoleColor() : PluginColor.GOLD.getColor();
|
||||
TextColor accentColor1 = isBukkitConsole ? PluginColor.MEDIUM_GOLD.getConsoleColor() : PluginColor.MEDIUM_GOLD.getColor();
|
||||
TextColor accentColor3 = isBukkitConsole ? PluginColor.LIGHT_YELLOW.getConsoleColor() : PluginColor.LIGHT_YELLOW.getColor();
|
||||
String arrow = isBukkitConsole ? " -> " : " → "; //4 spaces, alt + 26, 1 space
|
||||
public TextComponent getExampleMessage() {
|
||||
String arrow = componentFactory instanceof BukkitConsoleComponentFactory ? " -> " : " → "; //4 spaces, alt + 26, 1 space
|
||||
|
||||
return Component.newline()
|
||||
.append(componentFactory.prefixTitleComponent(isBukkitConsole))
|
||||
.append(componentFactory.prefixTitleComponent())
|
||||
.append(Component.newline())
|
||||
.append(text("Examples: ").color(mainColor))
|
||||
.append(text("Examples: ").color(componentFactory.msgMain2()))
|
||||
.append(Component.newline())
|
||||
.append(text(arrow).color(mainColor)
|
||||
.append(text(arrow).color(componentFactory.msgMain2())
|
||||
.append(text("/statistic ")
|
||||
.append(text("animals_bred ").color(accentColor1)
|
||||
.append(text("top").color(accentColor3)))))
|
||||
.append(text("animals_bred ").color(componentFactory.msgAccent2A())
|
||||
.append(text("top").color(componentFactory.msgAccent2B())))))
|
||||
.append(Component.newline())
|
||||
.append(text(arrow).color(mainColor)
|
||||
.append(text(arrow).color(componentFactory.msgMain2())
|
||||
.append(text("/statistic ")
|
||||
.append(text("mine_block diorite ").color(accentColor1)
|
||||
.append(text("me").color(accentColor3)))))
|
||||
.append(text("mine_block diorite ").color(componentFactory.msgAccent2A())
|
||||
.append(text("me").color(componentFactory.msgAccent2B())))))
|
||||
.append(Component.newline())
|
||||
.append(text(arrow).color(mainColor)
|
||||
.append(text(arrow).color(componentFactory.msgMain2())
|
||||
.append(text("/statistic ")
|
||||
.append(text("deaths ").color(accentColor1)
|
||||
.append(text("player ").color(accentColor3)
|
||||
.append(text("Artemis_the_gr8"))))));
|
||||
.append(text("deaths ").color(componentFactory.msgAccent2A())
|
||||
.append(text("player ").color(componentFactory.msgAccent2B())
|
||||
.append(text("Artemis_the_gr8")
|
||||
.color(componentFactory.getExampleNameColor()))))));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,4 +83,4 @@ public class ExampleMessage implements TextComponent {
|
||||
public @NotNull TextComponent style(@NotNull Style style) {
|
||||
return exampleMessage.style(style);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,4 +16,4 @@ public final class FontUtils {
|
||||
return (int) Math.round((130.0 - (MinecraftFont.Font.getWidth(displayText) * 1.5))/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.BukkitConsoleComponentFactory;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.ComponentFactory;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
@ -21,145 +19,122 @@ public class HelpMessage implements TextComponent {
|
||||
|
||||
private final ComponentFactory componentFactory;
|
||||
private final TextComponent helpMessage;
|
||||
boolean isBukkitConsole;
|
||||
TextColor GRAY;
|
||||
TextColor DARK_PURPLE;
|
||||
TextColor GOLD;
|
||||
TextColor MEDIUM_GOLD;
|
||||
TextColor LIGHT_GOLD;
|
||||
TextColor LIGHT_BLUE;
|
||||
|
||||
|
||||
public HelpMessage(ComponentFactory componentFactory, boolean useHover, boolean isBukkitConsole, int listSize) {
|
||||
public HelpMessage(ComponentFactory componentFactory, boolean useHover, int listSize) {
|
||||
this.componentFactory = componentFactory;
|
||||
this.isBukkitConsole = isBukkitConsole;
|
||||
getPluginColors(isBukkitConsole);
|
||||
|
||||
if (!useHover || isBukkitConsole) {
|
||||
helpMessage = getPlainHelpMsg(isBukkitConsole, listSize);
|
||||
if (!useHover) {
|
||||
helpMessage = getPlainHelpMsg(listSize);
|
||||
} else {
|
||||
helpMessage = helpMsgHover(listSize);
|
||||
}
|
||||
}
|
||||
|
||||
private TextComponent getPlainHelpMsg(boolean isBukkitConsole, int listSize) {
|
||||
String arrowSymbol = isBukkitConsole ? "->" : "→"; //alt + 26
|
||||
String bulletSymbol = isBukkitConsole ? "*" : "•"; //alt + 7
|
||||
private TextComponent getPlainHelpMsg(int listSize) {
|
||||
String arrowSymbol = "→"; //alt + 26
|
||||
String bulletSymbol = "•"; //alt + 7
|
||||
|
||||
if (componentFactory instanceof BukkitConsoleComponentFactory) {
|
||||
arrowSymbol = "->";
|
||||
bulletSymbol = "*";
|
||||
}
|
||||
TextComponent spaces = text(" "); //4 spaces
|
||||
TextComponent arrow = text(arrowSymbol).color(NamedTextColor.GOLD);
|
||||
TextComponent bullet = text(bulletSymbol).color(NamedTextColor.GOLD);
|
||||
TextComponent arrow = text(arrowSymbol).color(componentFactory.msgMain2());
|
||||
TextComponent bullet = text(bulletSymbol).color(componentFactory.msgMain2());
|
||||
|
||||
return Component.newline()
|
||||
.append(componentFactory.prefixTitleComponent(isBukkitConsole))
|
||||
.append(componentFactory.prefixTitleComponent())
|
||||
.append(newline())
|
||||
.append(text("Type \"/statistic examples\" to see examples!").color(GRAY).decorate(TextDecoration.ITALIC))
|
||||
.append(text("Type \"/statistic examples\" to see examples!").color(componentFactory.brackets()).decorate(TextDecoration.ITALIC))
|
||||
.append(newline())
|
||||
.append(text("Usage:").color(GOLD)).append(space())
|
||||
.append(text("/statistic").color(LIGHT_GOLD))
|
||||
.append(text("Usage:").color(componentFactory.msgMain2())).append(space())
|
||||
.append(text("/statistic").color(componentFactory.hoverAccent()))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("name").color(LIGHT_GOLD))
|
||||
.append(text("name").color(componentFactory.hoverAccent()))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("{sub-statistic}").color(LIGHT_GOLD)).append(space())
|
||||
.append(text("(a block, item or entity)").color(GRAY))
|
||||
.append(text("{sub-statistic}").color(componentFactory.hoverAccent())).append(space())
|
||||
.append(text("(a block, item or entity)").color(componentFactory.brackets()))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("me | player | server | top").color(LIGHT_GOLD))
|
||||
.append(text("me | player | server | top").color(componentFactory.hoverAccent()))
|
||||
.append(newline())
|
||||
.append(spaces).append(spaces).append(bullet).append(space())
|
||||
.append(text("me:").color(MEDIUM_GOLD)).append(space())
|
||||
.append(text("your own statistic").color(GRAY))
|
||||
.append(text("me:").color(componentFactory.msgAccent2A())).append(space())
|
||||
.append(text("your own statistic").color(componentFactory.brackets()))
|
||||
.append(newline())
|
||||
.append(spaces).append(spaces).append(bullet).append(space())
|
||||
.append(text("player:").color(MEDIUM_GOLD)).append(space())
|
||||
.append(text("choose a player").color(GRAY))
|
||||
.append(text("player:").color(componentFactory.msgAccent2A())).append(space())
|
||||
.append(text("choose a player").color(componentFactory.brackets()))
|
||||
.append(newline())
|
||||
.append(spaces).append(spaces).append(bullet).append(space())
|
||||
.append(text("server:").color(MEDIUM_GOLD)).append(space())
|
||||
.append(text("everyone on the server combined").color(GRAY))
|
||||
.append(text("server:").color(componentFactory.msgAccent2A())).append(space())
|
||||
.append(text("everyone on the server combined").color(componentFactory.brackets()))
|
||||
.append(newline())
|
||||
.append(spaces).append(spaces).append(bullet).append(space())
|
||||
.append(text("top:").color(MEDIUM_GOLD)).append(space())
|
||||
.append(text("the top").color(GRAY).append(space()).append(text(listSize)))
|
||||
.append(text("top:").color(componentFactory.msgAccent2A())).append(space())
|
||||
.append(text("the top").color(componentFactory.brackets()).append(space()).append(text(listSize)))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("{player-name}").color(LIGHT_GOLD));
|
||||
.append(text("{player-name}").color(componentFactory.hoverAccent()));
|
||||
}
|
||||
|
||||
private TextComponent helpMsgHover(int listSize) {
|
||||
TextComponent spaces = text(" ");
|
||||
TextComponent arrow = text("→").color(GOLD);
|
||||
TextComponent arrow = text("→").color(componentFactory.msgMain2());
|
||||
|
||||
return Component.newline()
|
||||
.append(componentFactory.prefixTitleComponent(false))
|
||||
.append(componentFactory.prefixTitleComponent())
|
||||
.append(newline())
|
||||
.append(componentFactory.subTitleComponent("Hover over the arguments for more information!"))
|
||||
.append(newline())
|
||||
.append(text("Usage:").color(GOLD)).append(space())
|
||||
.append(text("/statistic").color(LIGHT_GOLD))
|
||||
.append(text("Usage:").color(componentFactory.msgMain2())).append(space())
|
||||
.append(text("/statistic").color(componentFactory.hoverAccent()))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("name").color(LIGHT_GOLD)
|
||||
.hoverEvent(HoverEvent.showText(text("The name that describes the statistic").color(LIGHT_BLUE)
|
||||
.append(text("name").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(text("The name that describes the statistic").color(componentFactory.hoverMsg())
|
||||
.append(newline())
|
||||
.append(text("Example: ").color(GOLD))
|
||||
.append(text("\"animals_bred\"").color(LIGHT_GOLD)))))
|
||||
.append(text("Example: ").color(componentFactory.msgMain2()))
|
||||
.append(text("\"animals_bred\"").color(componentFactory.hoverAccent())))))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("sub-statistic").color(LIGHT_GOLD)
|
||||
.append(text("sub-statistic").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("Some statistics need an item, block or entity as extra input").color(LIGHT_BLUE)
|
||||
text("Some statistics need an item, block or entity as extra input").color(componentFactory.hoverMsg())
|
||||
.append(newline())
|
||||
.append(text("Example: ").color(GOLD)
|
||||
.append(text("\"mine_block diorite\"").color(LIGHT_GOLD))))))
|
||||
.append(text("Example: ").color(componentFactory.msgMain2())
|
||||
.append(text("\"mine_block diorite\"").color(componentFactory.hoverAccent()))))))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("Choose one").color(DARK_PURPLE)))).append(space())
|
||||
.append(text("me").color(LIGHT_GOLD)
|
||||
text("Choose one").color(componentFactory.underscore())))).append(space())
|
||||
.append(text("me").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("See your own statistic").color(LIGHT_BLUE))))
|
||||
.append(text(" | ").color(LIGHT_GOLD))
|
||||
.append(text("player").color(LIGHT_GOLD)
|
||||
text("See your own statistic").color(componentFactory.hoverMsg()))))
|
||||
.append(text(" | ").color(componentFactory.hoverAccent()))
|
||||
.append(text("player").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("Choose any player that has played on your server").color(LIGHT_BLUE))))
|
||||
.append(text(" | ").color(LIGHT_GOLD))
|
||||
.append(text("server").color(LIGHT_GOLD)
|
||||
text("Choose any player that has played on your server").color(componentFactory.hoverMsg()))))
|
||||
.append(text(" | ").color(componentFactory.hoverAccent()))
|
||||
.append(text("server").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("See the combined total for everyone on your server").color(LIGHT_BLUE))))
|
||||
.append(text(" | ").color(LIGHT_GOLD))
|
||||
.append(text("top").color(LIGHT_GOLD)
|
||||
text("See the combined total for everyone on your server").color(componentFactory.hoverMsg()))))
|
||||
.append(text(" | ").color(componentFactory.hoverAccent()))
|
||||
.append(text("top").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("See the top").color(LIGHT_BLUE).append(space())
|
||||
text("See the top").color(componentFactory.hoverMsg()).append(space())
|
||||
.append(text(listSize)))))
|
||||
.append(newline())
|
||||
.append(spaces).append(arrow).append(space())
|
||||
.append(text("player-name").color(LIGHT_GOLD)
|
||||
.append(text("player-name").color(componentFactory.hoverAccent())
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("In case you typed").color(LIGHT_BLUE).append(space())
|
||||
.append(text("\"player\"").color(LIGHT_GOLD))
|
||||
text("In case you typed").color(componentFactory.hoverMsg()).append(space())
|
||||
.append(text("\"player\"").color(componentFactory.hoverAccent()))
|
||||
.append(text(", add the player's name")))));
|
||||
}
|
||||
|
||||
private void getPluginColors(boolean isBukkitConsole) {
|
||||
if (isBukkitConsole) {
|
||||
GRAY = PluginColor.GRAY.getConsoleColor();
|
||||
DARK_PURPLE = PluginColor.DARK_PURPLE.getConsoleColor();
|
||||
GOLD = PluginColor.GOLD.getConsoleColor();
|
||||
MEDIUM_GOLD = PluginColor.MEDIUM_GOLD.getConsoleColor();
|
||||
LIGHT_GOLD = PluginColor.LIGHT_GOLD.getConsoleColor();
|
||||
LIGHT_BLUE = PluginColor.LIGHT_BLUE.getConsoleColor();
|
||||
} else {
|
||||
GRAY = PluginColor.GRAY.getColor();
|
||||
DARK_PURPLE = PluginColor.DARK_PURPLE.getColor();
|
||||
GOLD = PluginColor.GOLD.getColor();
|
||||
MEDIUM_GOLD = PluginColor.MEDIUM_GOLD.getColor();
|
||||
LIGHT_GOLD = PluginColor.LIGHT_GOLD.getColor();
|
||||
LIGHT_BLUE = PluginColor.LIGHT_BLUE.getColor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String content() {
|
||||
return helpMessage.content();
|
||||
|
@ -13,11 +13,10 @@ import java.util.HashMap;
|
||||
|
||||
public final class LanguageKeyHandler {
|
||||
|
||||
private final HashMap<Statistic, String> statNameKeys;
|
||||
private static HashMap<Statistic, String> statNameKeys;
|
||||
|
||||
public LanguageKeyHandler() {
|
||||
statNameKeys = new HashMap<>();
|
||||
generateStatNameKeys();
|
||||
statNameKeys = generateStatNameKeys();
|
||||
}
|
||||
|
||||
public String getStatKey(@NotNull Statistic statistic) {
|
||||
@ -72,47 +71,46 @@ public final class LanguageKeyHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateDefaultKeys() {
|
||||
Arrays.stream(Statistic.values()).forEach(statistic -> statNameKeys.put(statistic, statistic.toString().toLowerCase()));
|
||||
}
|
||||
|
||||
private void generateStatNameKeys() {
|
||||
private @NotNull HashMap<Statistic, String> generateStatNameKeys() {
|
||||
//get the enum names for all statistics first
|
||||
generateDefaultKeys();
|
||||
HashMap<Statistic, String> statNames = new HashMap<>(Statistic.values().length);
|
||||
Arrays.stream(Statistic.values()).forEach(statistic -> statNames.put(statistic, statistic.toString().toLowerCase()));
|
||||
|
||||
//replace the ones for which the language key is different from the enum name
|
||||
statNameKeys.put(Statistic.ARMOR_CLEANED, "clean_armor");
|
||||
statNameKeys.put(Statistic.BANNER_CLEANED, "clean_banner");
|
||||
statNameKeys.put(Statistic.DROP_COUNT, "drop");
|
||||
statNameKeys.put(Statistic.CAKE_SLICES_EATEN, "eat_cake_slice");
|
||||
statNameKeys.put(Statistic.ITEM_ENCHANTED, "enchant_item");
|
||||
statNameKeys.put(Statistic.CAULDRON_FILLED, "fill_cauldron");
|
||||
statNameKeys.put(Statistic.DISPENSER_INSPECTED, "inspect_dispenser");
|
||||
statNameKeys.put(Statistic.DROPPER_INSPECTED, "inspect_dropper");
|
||||
statNameKeys.put(Statistic.HOPPER_INSPECTED, "inspect_hopper");
|
||||
statNameKeys.put(Statistic.BEACON_INTERACTION, "interact_with_beacon");
|
||||
statNameKeys.put(Statistic.BREWINGSTAND_INTERACTION, "interact_with_brewingstand");
|
||||
statNameKeys.put(Statistic.CRAFTING_TABLE_INTERACTION, "interact_with_crafting_table");
|
||||
statNameKeys.put(Statistic.FURNACE_INTERACTION, "interact_with_furnace");
|
||||
statNameKeys.put(Statistic.CHEST_OPENED, "open_chest");
|
||||
statNameKeys.put(Statistic.ENDERCHEST_OPENED, "open_enderchest");
|
||||
statNameKeys.put(Statistic.SHULKER_BOX_OPENED, "open_shulker_box");
|
||||
statNameKeys.put(Statistic.NOTEBLOCK_PLAYED, "play_noteblock");
|
||||
statNameKeys.put(Statistic.PLAY_ONE_MINUTE, "play_time");
|
||||
statNameKeys.put(Statistic.RECORD_PLAYED, "play_record");
|
||||
statNameKeys.put(Statistic.FLOWER_POTTED, "pot_flower");
|
||||
statNameKeys.put(Statistic.TRAPPED_CHEST_TRIGGERED, "trigger_trapped_chest");
|
||||
statNameKeys.put(Statistic.NOTEBLOCK_TUNED, "tune_noteblock");
|
||||
statNameKeys.put(Statistic.CAULDRON_USED, "use_cauldron");
|
||||
statNames.put(Statistic.ARMOR_CLEANED, "clean_armor");
|
||||
statNames.put(Statistic.BANNER_CLEANED, "clean_banner");
|
||||
statNames.put(Statistic.DROP_COUNT, "drop");
|
||||
statNames.put(Statistic.CAKE_SLICES_EATEN, "eat_cake_slice");
|
||||
statNames.put(Statistic.ITEM_ENCHANTED, "enchant_item");
|
||||
statNames.put(Statistic.CAULDRON_FILLED, "fill_cauldron");
|
||||
statNames.put(Statistic.DISPENSER_INSPECTED, "inspect_dispenser");
|
||||
statNames.put(Statistic.DROPPER_INSPECTED, "inspect_dropper");
|
||||
statNames.put(Statistic.HOPPER_INSPECTED, "inspect_hopper");
|
||||
statNames.put(Statistic.BEACON_INTERACTION, "interact_with_beacon");
|
||||
statNames.put(Statistic.BREWINGSTAND_INTERACTION, "interact_with_brewingstand");
|
||||
statNames.put(Statistic.CRAFTING_TABLE_INTERACTION, "interact_with_crafting_table");
|
||||
statNames.put(Statistic.FURNACE_INTERACTION, "interact_with_furnace");
|
||||
statNames.put(Statistic.CHEST_OPENED, "open_chest");
|
||||
statNames.put(Statistic.ENDERCHEST_OPENED, "open_enderchest");
|
||||
statNames.put(Statistic.SHULKER_BOX_OPENED, "open_shulker_box");
|
||||
statNames.put(Statistic.NOTEBLOCK_PLAYED, "play_noteblock");
|
||||
statNames.put(Statistic.PLAY_ONE_MINUTE, "play_time");
|
||||
statNames.put(Statistic.RECORD_PLAYED, "play_record");
|
||||
statNames.put(Statistic.FLOWER_POTTED, "pot_flower");
|
||||
statNames.put(Statistic.TRAPPED_CHEST_TRIGGERED, "trigger_trapped_chest");
|
||||
statNames.put(Statistic.NOTEBLOCK_TUNED, "tune_noteblock");
|
||||
statNames.put(Statistic.CAULDRON_USED, "use_cauldron");
|
||||
|
||||
//do the same for the statistics that have a subtype
|
||||
statNameKeys.put(Statistic.DROP, "dropped");
|
||||
statNameKeys.put(Statistic.PICKUP, "picked_up");
|
||||
statNameKeys.put(Statistic.MINE_BLOCK, "mined");
|
||||
statNameKeys.put(Statistic.USE_ITEM, "used");
|
||||
statNameKeys.put(Statistic.BREAK_ITEM, "broken");
|
||||
statNameKeys.put(Statistic.CRAFT_ITEM, "crafted");
|
||||
statNameKeys.put(Statistic.KILL_ENTITY, "killed");
|
||||
statNameKeys.put(Statistic.ENTITY_KILLED_BY, "killed_by");
|
||||
statNames.put(Statistic.DROP, "dropped");
|
||||
statNames.put(Statistic.PICKUP, "picked_up");
|
||||
statNames.put(Statistic.MINE_BLOCK, "mined");
|
||||
statNames.put(Statistic.USE_ITEM, "used");
|
||||
statNames.put(Statistic.BREAK_ITEM, "broken");
|
||||
statNames.put(Statistic.CRAFT_ITEM, "crafted");
|
||||
statNames.put(Statistic.KILL_ENTITY, "killed");
|
||||
statNames.put(Statistic.ENTITY_KILLED_BY, "killed_by");
|
||||
|
||||
return statNames;
|
||||
}
|
||||
}
|
@ -22,4 +22,4 @@ public final class StringUtils {
|
||||
}
|
||||
return capitals.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.reload;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.UnixTimeHandler;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@ -8,9 +9,9 @@ import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
|
||||
public class ReloadAction extends RecursiveAction {
|
||||
public final class ReloadAction extends RecursiveAction {
|
||||
|
||||
private final int threshold;
|
||||
private static int threshold;
|
||||
|
||||
private final OfflinePlayer[] players;
|
||||
private final int start;
|
||||
@ -20,21 +21,20 @@ public class ReloadAction extends RecursiveAction {
|
||||
private final ConcurrentHashMap<String, UUID> offlinePlayerUUIDs;
|
||||
|
||||
/** Fills a ConcurrentHashMap with PlayerNames and UUIDs for all OfflinePlayers that should be included in statistic calculations.
|
||||
* @param threshold the maximum length of OfflinePlayers to process in one task
|
||||
* @param players array of all OfflinePlayers (straight from Bukkit)
|
||||
* @param lastPlayedLimit whether to set a limit based on last-played-date
|
||||
* @param offlinePlayerUUIDs the ConcurrentHashMap to put resulting playerNames and UUIDs on
|
||||
*/
|
||||
public ReloadAction(int threshold, OfflinePlayer[] players,
|
||||
public ReloadAction(OfflinePlayer[] players,
|
||||
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
|
||||
|
||||
this(threshold, players, 0, players.length, lastPlayedLimit, offlinePlayerUUIDs);
|
||||
this(players, 0, players.length, lastPlayedLimit, offlinePlayerUUIDs);
|
||||
}
|
||||
|
||||
protected ReloadAction(int threshold, OfflinePlayer[] players, int start, int end,
|
||||
private ReloadAction(OfflinePlayer[] players, int start, int end,
|
||||
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
|
||||
threshold = ThreadManager.getTaskThreshold();
|
||||
|
||||
this.threshold = threshold;
|
||||
this.players = players;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
@ -53,9 +53,9 @@ public class ReloadAction extends RecursiveAction {
|
||||
}
|
||||
else {
|
||||
final int split = length / 2;
|
||||
final ReloadAction subTask1 = new ReloadAction(threshold, players, start, (start + split),
|
||||
final ReloadAction subTask1 = new ReloadAction(players, start, (start + split),
|
||||
lastPlayedLimit, offlinePlayerUUIDs);
|
||||
final ReloadAction subTask2 = new ReloadAction(threshold, players, (start + split), end,
|
||||
final ReloadAction subTask2 = new ReloadAction(players, (start + split), end,
|
||||
lastPlayedLimit, offlinePlayerUUIDs);
|
||||
|
||||
//queue and compute all subtasks in the right order
|
||||
|
@ -1,17 +1,17 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.reload;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.ShareManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
@ -22,24 +22,26 @@ import java.util.function.Predicate;
|
||||
|
||||
public class ReloadThread extends Thread {
|
||||
|
||||
private final int threshold;
|
||||
private final int reloadThreadID;
|
||||
|
||||
private final BukkitAudiences adventure;
|
||||
private static ConfigHandler config;
|
||||
private static MessageWriter messageWriter;
|
||||
private static OutputManager messageSender;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
private static ShareManager shareManager;
|
||||
|
||||
private final int reloadThreadID;
|
||||
private final StatThread statThread;
|
||||
|
||||
private final CommandSender sender;
|
||||
|
||||
public ReloadThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, int threshold, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
|
||||
this.threshold = threshold;
|
||||
reloadThreadID = ID;
|
||||
|
||||
adventure = a;
|
||||
public ReloadThread(ConfigHandler c, OutputManager m, OfflinePlayerHandler o, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
|
||||
config = c;
|
||||
messageWriter = m;
|
||||
messageSender = m;
|
||||
offlinePlayerHandler = o;
|
||||
|
||||
shareManager = ShareManager.getInstance(c);
|
||||
|
||||
reloadThreadID = ID;
|
||||
statThread = s;
|
||||
sender = se;
|
||||
|
||||
@ -57,31 +59,34 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (reloadThreadID != 1 && config.reloadConfig()) { //during a reload
|
||||
MyLogger.logMsg("Reloading!", false);
|
||||
MyLogger.setDebugLevel(config.getDebugLevel());
|
||||
MessageWriter.updateComponentFactory();
|
||||
loadOfflinePlayers();
|
||||
reloadEverything();
|
||||
|
||||
boolean isBukkitConsole = sender instanceof ConsoleCommandSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit");
|
||||
if (sender != null) {
|
||||
adventure.sender(sender).sendMessage(
|
||||
messageWriter.reloadedConfig(isBukkitConsole));
|
||||
messageSender.sendFeedbackMsg(sender, StandardMessage.RELOADED_CONFIG);
|
||||
}
|
||||
}
|
||||
else { //during first start-up
|
||||
MyLogger.setDebugLevel(config.getDebugLevel());
|
||||
loadOfflinePlayers();
|
||||
offlinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers());
|
||||
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadOfflinePlayers() {
|
||||
private void reloadEverything() {
|
||||
MyLogger.setDebugLevel(config.getDebugLevel());
|
||||
messageSender.updateMessageWriters(config);
|
||||
offlinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers());
|
||||
shareManager.updateSettings(config);
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<String, UUID> loadOfflinePlayers() {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
OfflinePlayer[] offlinePlayers;
|
||||
@ -114,13 +119,13 @@ public class ReloadThread extends Thread {
|
||||
int size = offlinePlayers != null ? offlinePlayers.length : 16;
|
||||
ConcurrentHashMap<String, UUID> playerMap = new ConcurrentHashMap<>(size);
|
||||
|
||||
ReloadAction task = new ReloadAction(threshold, offlinePlayers, config.getLastPlayedLimit(), playerMap);
|
||||
ReloadAction task = new ReloadAction(offlinePlayers, config.getLastPlayedLimit(), playerMap);
|
||||
MyLogger.actionCreated((offlinePlayers != null) ? offlinePlayers.length : 0);
|
||||
ForkJoinPool.commonPool().invoke(task);
|
||||
MyLogger.actionFinished(1);
|
||||
|
||||
OfflinePlayerHandler.updateOfflinePlayerList(playerMap);
|
||||
MyLogger.logTimeTaken("ReloadThread",
|
||||
("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
|
||||
("loaded " + playerMap.size() + " offline players"), time);
|
||||
return playerMap;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.statistic;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
|
||||
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;
|
||||
@ -10,10 +12,11 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.RecursiveAction;
|
||||
|
||||
|
||||
public class TopStatAction extends RecursiveAction {
|
||||
public final class StatAction extends RecursiveAction {
|
||||
|
||||
private final int threshold;
|
||||
private static int threshold;
|
||||
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
private final ImmutableList<String> playerNames;
|
||||
private final StatRequest request;
|
||||
private final ConcurrentHashMap<String, Integer> playerStats;
|
||||
@ -21,15 +24,16 @@ public class TopStatAction extends RecursiveAction {
|
||||
/**
|
||||
* Gets the statistic numbers for all players whose name is on the list, puts them in a ConcurrentHashMap
|
||||
* using the default ForkJoinPool, and returns the ConcurrentHashMap when everything is done
|
||||
* @param threshold the maximum length of playerNames to process in one task
|
||||
* @param offlinePlayerHandler the OfflinePlayerHandler to convert playerNames into Players
|
||||
* @param playerNames ImmutableList of playerNames for players that should be included in stat calculations
|
||||
* @param statRequest a validated statRequest
|
||||
* @param playerStats the ConcurrentHashMap to put the results on
|
||||
*/
|
||||
public TopStatAction(int threshold, ImmutableList<String> playerNames, StatRequest statRequest, ConcurrentHashMap<String, Integer> playerStats) {
|
||||
this.threshold = threshold;
|
||||
this.playerNames = playerNames;
|
||||
public StatAction(OfflinePlayerHandler offlinePlayerHandler, ImmutableList<String> playerNames, StatRequest statRequest, ConcurrentHashMap<String, Integer> playerStats) {
|
||||
threshold = ThreadManager.getTaskThreshold();
|
||||
|
||||
this.offlinePlayerHandler = offlinePlayerHandler;
|
||||
this.playerNames = playerNames;
|
||||
this.request = statRequest;
|
||||
this.playerStats = playerStats;
|
||||
|
||||
@ -42,8 +46,8 @@ public class TopStatAction extends RecursiveAction {
|
||||
getStatsDirectly();
|
||||
}
|
||||
else {
|
||||
final TopStatAction subTask1 = new TopStatAction(threshold, playerNames.subList(0, playerNames.size()/2), request, playerStats);
|
||||
final TopStatAction subTask2 = new TopStatAction(threshold, playerNames.subList(playerNames.size()/2, playerNames.size()), request, playerStats);
|
||||
final StatAction subTask1 = new StatAction(offlinePlayerHandler, playerNames.subList(0, playerNames.size()/2), request, playerStats);
|
||||
final StatAction subTask2 = new StatAction(offlinePlayerHandler, playerNames.subList(playerNames.size()/2, playerNames.size()), request, playerStats);
|
||||
|
||||
//queue and compute all subtasks in the right order
|
||||
invokeAll(subTask1, subTask2);
|
||||
@ -56,7 +60,7 @@ public class TopStatAction extends RecursiveAction {
|
||||
do {
|
||||
String playerName = iterator.next();
|
||||
MyLogger.actionRunning(Thread.currentThread().getName(), playerName, 2);
|
||||
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName);
|
||||
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName);
|
||||
if (player != null) {
|
||||
int statistic = 0;
|
||||
switch (request.getStatistic().getType()) {
|
@ -1,15 +1,15 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.statistic;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.Main;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
|
||||
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
|
||||
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
|
||||
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread;
|
||||
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
|
||||
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -21,81 +21,59 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class StatThread extends Thread {
|
||||
|
||||
private final int threshold;
|
||||
|
||||
private final StatRequest request;
|
||||
private final ReloadThread reloadThread;
|
||||
|
||||
private final BukkitAudiences adventure;
|
||||
private static ConfigHandler config;
|
||||
private static MessageWriter messageWriter;
|
||||
private final Main plugin;
|
||||
private final OutputManager outputManager;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
//constructor (called on thread creation)
|
||||
public StatThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p, int ID, int threshold, StatRequest s, @Nullable ReloadThread r) {
|
||||
this.threshold = threshold;
|
||||
private final ReloadThread reloadThread;
|
||||
private final StatRequest request;
|
||||
|
||||
request = s;
|
||||
reloadThread = r;
|
||||
|
||||
adventure = a;
|
||||
public StatThread(ConfigHandler c, OutputManager m, OfflinePlayerHandler o, int ID, StatRequest s, @Nullable ReloadThread r) {
|
||||
config = c;
|
||||
messageWriter = m;
|
||||
plugin = p;
|
||||
outputManager = m;
|
||||
offlinePlayerHandler = o;
|
||||
|
||||
reloadThread = r;
|
||||
request = s;
|
||||
|
||||
this.setName("StatThread-" + request.getCommandSender().getName() + "-" + ID);
|
||||
MyLogger.threadCreated(this.getName());
|
||||
}
|
||||
|
||||
//what the thread will do once started
|
||||
@Override
|
||||
public void run() throws IllegalStateException, NullPointerException {
|
||||
MyLogger.threadStart(this.getName());
|
||||
|
||||
if (messageWriter == null || plugin == null) {
|
||||
throw new IllegalStateException("Not all classes off the plugin are running!");
|
||||
}
|
||||
if (request == null) {
|
||||
throw new NullPointerException("No statistic request was found!");
|
||||
}
|
||||
|
||||
if (reloadThread != null && reloadThread.isAlive()) {
|
||||
try {
|
||||
MyLogger.waitingForOtherThread(this.getName(), reloadThread.getName());
|
||||
adventure.sender(request.getCommandSender())
|
||||
.sendMessage(messageWriter
|
||||
.stillReloading(request.isBukkitConsoleSender()));
|
||||
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.STILL_RELOADING);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Target selection = request.getSelection();
|
||||
if (selection == Target.PLAYER) {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(
|
||||
messageWriter.formatPlayerStat(getIndividualStat(), request));
|
||||
long lastCalc = ThreadManager.getLastRecordedCalcTime();
|
||||
if (lastCalc > 2000) {
|
||||
outputManager.sendFeedbackMsgWaitAMoment(request.getCommandSender(), lastCalc > 20000);
|
||||
}
|
||||
else {
|
||||
if (ThreadManager.getLastRecordedCalcTime() > 2000) {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(
|
||||
messageWriter.waitAMoment(ThreadManager.getLastRecordedCalcTime() > 20000, request.isBukkitConsoleSender()));
|
||||
|
||||
Target selection = request.getSelection();
|
||||
try {
|
||||
switch (selection) {
|
||||
case PLAYER -> outputManager.sendPlayerStat(request, getIndividualStat());
|
||||
case TOP -> outputManager.sendTopStat(request, getTopStats());
|
||||
case SERVER -> outputManager.sendServerStat(request, getServerTotal());
|
||||
}
|
||||
try {
|
||||
if (selection == Target.TOP) {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(
|
||||
messageWriter.formatTopStats(getTopStats(), request));
|
||||
} else {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(
|
||||
messageWriter.formatServerStat(getServerTotal(), request));
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
if (!request.isConsoleSender()) {
|
||||
adventure.sender(request.getCommandSender()).sendMessage(
|
||||
messageWriter.unknownError(false));
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
if (!request.isConsoleSender()) {
|
||||
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,7 +85,7 @@ public class StatThread extends Thread {
|
||||
}
|
||||
|
||||
private long getServerTotal() {
|
||||
List<Integer> numbers = getAllStats().values().stream().toList();
|
||||
List<Integer> numbers = getAllStats().values().parallelStream().toList();
|
||||
return numbers.parallelStream().mapToLong(Integer::longValue).sum();
|
||||
}
|
||||
|
||||
@ -115,11 +93,11 @@ public class StatThread extends Thread {
|
||||
private @NotNull ConcurrentHashMap<String, Integer> 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<String, Integer> playerStats = new ConcurrentHashMap<>(size);
|
||||
ImmutableList<String> playerNames = ImmutableList.copyOf(OfflinePlayerHandler.getOfflinePlayerNames());
|
||||
ImmutableList<String> playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames());
|
||||
|
||||
TopStatAction task = new TopStatAction(threshold, playerNames, request, playerStats);
|
||||
StatAction task = new StatAction(offlinePlayerHandler, playerNames, request, playerStats);
|
||||
MyLogger.actionCreated(playerNames.size());
|
||||
ForkJoinPool commonPool = ForkJoinPool.commonPool();
|
||||
|
||||
@ -127,7 +105,8 @@ public class StatThread extends Thread {
|
||||
commonPool.invoke(task);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
MyLogger.logMsg("The request could not be executed due to a ConcurrentModificationException. " +
|
||||
"This likely happened because Bukkit hasn't fully initialized all player-data yet. Try again and it should be fine!", true);
|
||||
"This likely happened because Bukkit hasn't fully initialized all player-data yet. " +
|
||||
"Try again and it should be fine!", true);
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
}
|
||||
|
||||
@ -141,22 +120,14 @@ public class StatThread extends Thread {
|
||||
/** Gets the statistic data for an individual player. If somehow the player
|
||||
cannot be found, this returns 0.*/
|
||||
private int getIndividualStat() {
|
||||
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(request.getPlayerName());
|
||||
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(request.getPlayerName());
|
||||
if (player != null) {
|
||||
switch (request.getStatistic().getType()) {
|
||||
case UNTYPED -> {
|
||||
return player.getStatistic(request.getStatistic());
|
||||
}
|
||||
case ENTITY -> {
|
||||
return player.getStatistic(request.getStatistic(), request.getEntity());
|
||||
}
|
||||
case BLOCK -> {
|
||||
return player.getStatistic(request.getStatistic(), request.getBlock());
|
||||
}
|
||||
case ITEM -> {
|
||||
return player.getStatistic(request.getStatistic(), request.getItem());
|
||||
}
|
||||
}
|
||||
return switch (request.getStatistic().getType()) {
|
||||
case UNTYPED -> player.getStatistic(request.getStatistic());
|
||||
case ENTITY -> player.getStatistic(request.getStatistic(), request.getEntity());
|
||||
case BLOCK -> player.getStatistic(request.getStatistic(), request.getBlock());
|
||||
case ITEM -> player.getStatistic(request.getStatistic(), request.getItem());
|
||||
};
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,7 +12,12 @@ import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class EnumHandler {
|
||||
/** This class deals with Bukkit Enumerators. It holds private lists of all
|
||||
block-, item-, entity- and statistic-names, and has one big list of all
|
||||
possible sub-statistic-entries (block/item/entity). It can give the names
|
||||
of all aforementioned enums, check if something is a valid enum constant,
|
||||
and turn a name into its corresponding enum constant. */
|
||||
public final class EnumHandler {
|
||||
|
||||
private final static List<String> blockNames;
|
||||
private final static List<String> entityNames;
|
||||
@ -50,14 +55,22 @@ public class EnumHandler {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private EnumHandler() {
|
||||
/** Returns all block-names in lowercase */
|
||||
public static List<String> getBlockNames() {
|
||||
return blockNames;
|
||||
}
|
||||
|
||||
/** Returns all item-names in lowercase*/
|
||||
public static List<String> getItemNames() {
|
||||
return itemNames;
|
||||
}
|
||||
|
||||
/** Returns corresponding item enum constant for an itemName
|
||||
/** Returns all statistic-names in lowercase */
|
||||
public static List<String> getStatNames() {
|
||||
return statNames;
|
||||
}
|
||||
|
||||
/** Returns the corresponding Material enum constant for an itemName
|
||||
@param itemName String, case-insensitive
|
||||
@return Material enum constant, uppercase */
|
||||
public static @Nullable Material getItemEnum(String itemName) {
|
||||
@ -67,12 +80,7 @@ public class EnumHandler {
|
||||
return (item != null && item.isItem()) ? item : null;
|
||||
}
|
||||
|
||||
/** Returns all entitytype names in lowercase */
|
||||
public static List<String> getEntityNames() {
|
||||
return entityNames;
|
||||
}
|
||||
|
||||
/** Returns corresponding EntityType enum constant for an entityName
|
||||
/** Returns the corresponding EntityType enum constant for an entityName
|
||||
@param entityName String, case-insensitive
|
||||
@return EntityType enum constant, uppercase */
|
||||
public static @Nullable EntityType getEntityEnum(String entityName) {
|
||||
@ -84,12 +92,7 @@ public class EnumHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns all block names in lowercase */
|
||||
public static List<String> getBlockNames() {
|
||||
return blockNames;
|
||||
}
|
||||
|
||||
/** Returns corresponding block enum constant for a materialName
|
||||
/** Returns the corresponding Material enum constant for a materialName
|
||||
@param materialName String, case-insensitive
|
||||
@return Material enum constant, uppercase */
|
||||
public static @Nullable Material getBlockEnum(String materialName) {
|
||||
@ -99,6 +102,17 @@ public class EnumHandler {
|
||||
return (block != null && block.isBlock()) ? block : null;
|
||||
}
|
||||
|
||||
/** Returns the statistic enum constant, or null if that failed.
|
||||
@param statName String, case-insensitive */
|
||||
public static @Nullable Statistic getStatEnum(@NotNull String statName) {
|
||||
try {
|
||||
return Statistic.valueOf(statName.toUpperCase());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if string is a valid statistic
|
||||
@param statName String, case-insensitive */
|
||||
public static boolean isStatistic(@NotNull String statName) {
|
||||
@ -111,20 +125,10 @@ public class EnumHandler {
|
||||
statName.equalsIgnoreCase(Statistic.KILL_ENTITY.toString());
|
||||
}
|
||||
|
||||
/** Returns the names of all general statistics in lowercase */
|
||||
public static List<String> getStatNames() {
|
||||
return statNames;
|
||||
}
|
||||
|
||||
/** Returns the statistic enum constant, or null if that failed.
|
||||
@param statName String, case-insensitive */
|
||||
public static @Nullable Statistic getStatEnum(@NotNull String statName) {
|
||||
try {
|
||||
return Statistic.valueOf(statName.toUpperCase());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
/** Checks if this statistic is a subStatEntry, meaning it is a block, item or entity
|
||||
@param statName String, case-insensitive*/
|
||||
public static boolean isSubStatEntry(@NotNull String statName) {
|
||||
return subStatNames.contains(statName.toLowerCase());
|
||||
}
|
||||
|
||||
/** Returns "block", "entity", "item", or "sub-statistic" if the provided Type is null. */
|
||||
@ -138,10 +142,4 @@ public class EnumHandler {
|
||||
}
|
||||
return subStat;
|
||||
}
|
||||
|
||||
/** Checks if this statistic is a subStatEntry, meaning it is a block, item or entity
|
||||
@param statName String, case-insensitive*/
|
||||
public static boolean isSubStatEntry(@NotNull String statName) {
|
||||
return subStatNames.contains(statName.toLowerCase());
|
||||
}
|
||||
}
|
@ -12,27 +12,24 @@ public class OfflinePlayerHandler {
|
||||
private static ConcurrentHashMap<String, UUID> offlinePlayerUUIDs;
|
||||
private static ArrayList<String> playerNames;
|
||||
|
||||
static {
|
||||
public OfflinePlayerHandler() {
|
||||
offlinePlayerUUIDs = new ConcurrentHashMap<>();
|
||||
playerNames = new ArrayList<>();
|
||||
}
|
||||
|
||||
private OfflinePlayerHandler() {
|
||||
}
|
||||
|
||||
/** Checks if a given playerName is on the private HashMap of players that should be included in statistic calculations
|
||||
@param playerName String, case-sensitive */
|
||||
public static boolean isRelevantPlayer(String playerName) {
|
||||
public boolean isRelevantPlayer(String playerName) {
|
||||
return offlinePlayerUUIDs.containsKey(playerName);
|
||||
}
|
||||
|
||||
/** Returns the number of OfflinePlayers that are included in statistic calculations */
|
||||
public static int getOfflinePlayerCount() {
|
||||
public int getOfflinePlayerCount() {
|
||||
return offlinePlayerUUIDs.size();
|
||||
}
|
||||
|
||||
/** Get an ArrayList of names from all OfflinePlayers that should be included in statistic calculations */
|
||||
public static ArrayList<String> getOfflinePlayerNames() {
|
||||
public ArrayList<String> getOfflinePlayerNames() {
|
||||
return playerNames;
|
||||
}
|
||||
|
||||
@ -41,7 +38,7 @@ public class OfflinePlayerHandler {
|
||||
* This HashMap is stored as a private variable in OfflinePlayerHandler.
|
||||
* @param playerList ConcurrentHashMap with keys: playerNames and values: UUIDs
|
||||
*/
|
||||
public static void updateOfflinePlayerList(ConcurrentHashMap<String, UUID> playerList) {
|
||||
public void updateOfflinePlayerList(ConcurrentHashMap<String, UUID> playerList) {
|
||||
offlinePlayerUUIDs = playerList;
|
||||
playerNames = Collections.list(offlinePlayerUUIDs.keys());
|
||||
}
|
||||
@ -51,7 +48,7 @@ public class OfflinePlayerHandler {
|
||||
* @param playerName name of the target player
|
||||
* @return OfflinePlayer (if this player is on the list, otherwise null)
|
||||
*/
|
||||
public static @Nullable OfflinePlayer getOfflinePlayer(String playerName) {
|
||||
public @Nullable OfflinePlayer getOfflinePlayer(String playerName) {
|
||||
if (offlinePlayerUUIDs.get(playerName) != null) {
|
||||
return Bukkit.getOfflinePlayer(offlinePlayerUUIDs.get(playerName));
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.utils;
|
||||
|
||||
|
||||
public class UnixTimeHandler {
|
||||
public final class UnixTimeHandler {
|
||||
|
||||
/** Calculates whether a player has played recently enough to fall within the lastPlayedLimit.
|
||||
If lastPlayedLimit == 0, this always returns true (since there is no limit).
|
||||
|
@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------------------------------------------ #
|
||||
# PlayerStats Configuration #
|
||||
# ------------------------------------------------------------------------------------------------------ #
|
||||
config-version: 5
|
||||
config-version: 6
|
||||
|
||||
|
||||
# # ------------------------------- # #
|
||||
@ -15,9 +15,16 @@ config-version: 5
|
||||
debug-level: 1
|
||||
|
||||
# Whether players have to wait for their lookup to finish before they can request another statistic
|
||||
# Warning: disabling this could allow players to stress out your server by spamming the stat-command!
|
||||
# Warning: disabling this could put stress on your server if players spam the stat-command!
|
||||
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 (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
|
||||
exclude-banned-players: false
|
||||
@ -38,37 +45,21 @@ translate-to-client-language: true
|
||||
# Use hover-text for additional info about statistic numbers
|
||||
enable-hover-text: true
|
||||
|
||||
# The unit to display certain statistics in.
|
||||
# Minecraft measures distance in cm. PlayerStats supports: blocks, cm, m (= blocks), miles, km
|
||||
distance-unit: blocks
|
||||
distance-unit-for-hover-text: km
|
||||
|
||||
# Minecraft measures damage in 0.5 hearts (1HP). PlayerStats supports: hp, hearts
|
||||
damage-unit: hearts
|
||||
damage-unit-for-hover-text: hp
|
||||
|
||||
# Minecraft measures time in ticks. With the below settings, PlayerStats will:
|
||||
# Auto-detect the best maximum unit to use (weeks/days/hours/minutes/seconds) for your players' statistics
|
||||
# Show a specified amount of additional smaller units (example: "x days" would become "x days, y hours, z minutes")
|
||||
auto-detect-biggest-time-unit: true
|
||||
number-of-extra-units: 1
|
||||
auto-detect-biggest-time-unit-for-hover-text: false
|
||||
number-of-extra-units-for-hover-text: 0
|
||||
|
||||
# If you don't want the unit to be auto-detected, set the auto-detect settings to false and specify your own range here
|
||||
# If the max and min are the same, only that unit will be displayed
|
||||
# PlayerStats supports: days, hours, minutes, seconds (and ticks if you want the original number)
|
||||
biggest-time-unit: days
|
||||
smallest-time-unit: hours
|
||||
biggest-time-unit-for-hover-text: hours
|
||||
smallest-time-unit-for-hover-text: seconds
|
||||
|
||||
# Automatically use themed formatting for the duration of certain holidays or festivals
|
||||
enable-festive-formatting: true
|
||||
|
||||
# Always use rainbow for the [PlayerStats] prefix instead of the default gold/purple
|
||||
# Always use the rainbow theme
|
||||
rainbow-mode: false
|
||||
|
||||
# Start the below stat-results with an empty line in chat before the result
|
||||
use-enters:
|
||||
top-stats: true
|
||||
top-stats-shared: false
|
||||
player-stats: false
|
||||
player-stats-shared: false
|
||||
server-stats: false
|
||||
server-stats-shared: false
|
||||
|
||||
# Align the stat-numbers in the top list with dots
|
||||
use-dots: true
|
||||
|
||||
@ -84,6 +75,35 @@ total-server-stat-title: 'Total on'
|
||||
your-server-name: 'this server'
|
||||
|
||||
|
||||
# # ------------------------------- # #
|
||||
# # Units # #
|
||||
# # ------------------------------- # #
|
||||
|
||||
# Minecraft measures distance in cm. PlayerStats supports: blocks, cm, m (= blocks), miles, km
|
||||
distance-unit: blocks
|
||||
distance-unit-for-hover-text: km
|
||||
|
||||
# Minecraft measures damage in 0.5 hearts (1HP). PlayerStats supports: hp, hearts
|
||||
damage-unit: hearts
|
||||
damage-unit-for-hover-text: hp
|
||||
|
||||
# Minecraft measures time in ticks. With the below settings, PlayerStats will:
|
||||
# Auto-detect the biggest unit to use (weeks/days/hours/minutes/seconds) for your players' statistics
|
||||
# Show as many additional smaller units as you choose (so for 3 extra units, "9D" would become "9D 5H 20M")
|
||||
auto-detect-biggest-time-unit: true
|
||||
number-of-extra-units: 1
|
||||
auto-detect-biggest-time-unit-for-hover-text: false
|
||||
number-of-extra-units-for-hover-text: 0
|
||||
|
||||
# To always use the same units, set the auto-detect settings to false and select your own unit range here
|
||||
# If the biggest and smallest unit are the same, only that unit will be displayed
|
||||
# PlayerStats supports: days, hours, minutes, seconds (and ticks if you want the original number)
|
||||
biggest-time-unit: days
|
||||
smallest-time-unit: hours
|
||||
biggest-time-unit-for-hover-text: hours
|
||||
smallest-time-unit-for-hover-text: seconds
|
||||
|
||||
|
||||
# # ------------------------------- # #
|
||||
# # Color & Style # #
|
||||
# # ------------------------------- # #
|
||||
@ -107,14 +127,22 @@ hover-text-amount-lighter: 40
|
||||
# # black white # #
|
||||
# # ------------------------------ # #
|
||||
|
||||
shared-stats:
|
||||
shared-by: gray
|
||||
shared-by-style: italic
|
||||
|
||||
player-name: "#EE8A19"
|
||||
player-name-style: italic
|
||||
|
||||
|
||||
top-list:
|
||||
title: '#FFEA40'
|
||||
title: '#FFD52B'
|
||||
title-style: none
|
||||
|
||||
title-number: gold
|
||||
title-number-style: none
|
||||
|
||||
stat-names: '#FFEA40'
|
||||
stat-names: '#FFD52B'
|
||||
stat-names-style: none
|
||||
|
||||
sub-stat-names: yellow
|
||||
|
@ -1,7 +1,7 @@
|
||||
main: com.gmail.artemis.the.gr8.playerstats.Main
|
||||
name: PlayerStats
|
||||
version: 1.5
|
||||
api-version: 1.18
|
||||
version: 1.6
|
||||
api-version: 1.13
|
||||
description: adds commands to view player statistics in chat
|
||||
author: Artemis_the_gr8
|
||||
commands:
|
||||
@ -11,17 +11,27 @@ commands:
|
||||
- stats
|
||||
description: general statistic command
|
||||
permission: playerstats.stat
|
||||
statisticshare:
|
||||
aliases:
|
||||
- statshare
|
||||
- statsshare
|
||||
description: shares last stat lookup in chat
|
||||
usage: "§b/statshare"
|
||||
permission: playerstats.share
|
||||
statisticreload:
|
||||
aliases:
|
||||
- statreload
|
||||
- statsreload
|
||||
description: reloads the config
|
||||
usage: "§a/statisticreload"
|
||||
usage: "§a/statreload"
|
||||
permission: playerstats.reload
|
||||
permissions:
|
||||
playerstats.stat:
|
||||
description: allows usage of /statistic
|
||||
default: true
|
||||
playerstats.share:
|
||||
description: allows sharing stats in chat
|
||||
default: true
|
||||
playerstats.reload:
|
||||
description: allows usage of /statreload
|
||||
default: op
|
Loading…
Reference in New Issue
Block a user