let there be jank
This commit is contained in:
@@ -2,73 +2,73 @@
|
||||
<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/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>me.youhavetrouble</groupId>
|
||||
<artifactId>StatsTop</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<groupId>me.youhavetrouble</groupId>
|
||||
<artifactId>StatsTop</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>StatsTop</name>
|
||||
<name>StatsTop</name>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>16</source>
|
||||
<target>16</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc-repo</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/groups/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.20-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.20-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# StatsTop
|
||||
|
||||
Simple plugin adding a command that shows the top players on the server for given statistic.
|
||||
|
||||
Some stats are probably broken or don't have correct translation strings set.
|
||||
|
||||
This project is completely unsupported, use at your own risk.
|
||||
@@ -1,2 +1,86 @@
|
||||
package me.youhavetrouble.statstop;public class StatsData {
|
||||
package me.youhavetrouble.statstop;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class StatsData {
|
||||
|
||||
private final LinkedHashMap<String, Integer> data = new LinkedHashMap<>();
|
||||
private final long timestamp;
|
||||
private final Statistic stat;
|
||||
private final Object arg;
|
||||
|
||||
protected StatsData(LinkedHashMap<String, Integer> data, Statistic stat, Object arg) {
|
||||
this.data.putAll(data);
|
||||
this.stat = stat;
|
||||
this.arg = arg;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public Component getTop(int amount, CommandSender sender) {
|
||||
Component component = Component.text("Top", NamedTextColor.GOLD)
|
||||
.append(Component.space())
|
||||
.append(Component.translatable("stat.minecraft." + stat.toString().toLowerCase(Locale.ENGLISH), NamedTextColor.GOLD));
|
||||
|
||||
if (arg != null) {
|
||||
if (arg instanceof EntityType entityType) {
|
||||
component = component
|
||||
.append(Component.text(" - "))
|
||||
.append(Component.translatable("entity.minecraft." + entityType.toString().toLowerCase(Locale.ENGLISH)));
|
||||
} else if (arg instanceof Material material) {
|
||||
component = component.append(Component.text(" - ", NamedTextColor.GOLD));
|
||||
|
||||
if (material.isBlock()) {
|
||||
component = component.append(Component.translatable("block.minecraft." + material.toString().toLowerCase(Locale.ENGLISH), NamedTextColor.GOLD));
|
||||
} else if (material.isItem()) {
|
||||
component = component.append(Component.translatable("item.minecraft." + material.toString().toLowerCase(Locale.ENGLISH), NamedTextColor.GOLD));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String senderName = null;
|
||||
|
||||
if (sender instanceof Player player) {
|
||||
senderName = player.getName();
|
||||
}
|
||||
|
||||
HashSet<String> names = new HashSet<>(amount);
|
||||
Component senderData = null;
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Integer> key : data.entrySet()) {
|
||||
if (key.getKey().equals(senderName)) {
|
||||
senderData = getLine(i + 1, key.getKey(), key.getValue(), NamedTextColor.YELLOW);
|
||||
}
|
||||
if (i >= amount && senderData != null) continue;
|
||||
names.add(key.getKey());
|
||||
component = component.append(Component.newline());
|
||||
component = component.append(key.getKey().equals(senderName) ? senderData : getLine(i + 1, key.getKey(), key.getValue(), NamedTextColor.WHITE));
|
||||
i++;
|
||||
}
|
||||
|
||||
if (sender instanceof Player player && !names.contains(player.getName()) && senderData != null) {
|
||||
component = component.append(senderData);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
private Component getLine(int place, String name, int value, TextColor textColor) {
|
||||
return Component.text(place + ". " + name + " - " + value, textColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ public final class StatsTop extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
getServer().getCommandMap().register("top", new StatsTopCommand());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,245 @@
|
||||
package me.youhavetrouble.statstop;public class StatsTopCommand {
|
||||
package me.youhavetrouble.statstop;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.bukkit.util.StringUtil;
|
||||
import org.bukkit.util.permissions.DefaultPermissions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class StatsTopCommand extends Command {
|
||||
|
||||
private final HashMap<String, StatsData> cachedData = new HashMap<>();
|
||||
|
||||
protected StatsTopCommand() {
|
||||
super("top", "Display top for given stat", "/top <stat> [arg]", new ArrayList<>());
|
||||
setPermission("statstop.top");
|
||||
DefaultPermissions.registerPermission("statstop.top", "Allows you to use /top", PermissionDefault.TRUE);
|
||||
}
|
||||
|
||||
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) {
|
||||
List<String> completions = new ArrayList<>();
|
||||
|
||||
if (args.length == 1) {
|
||||
for (Statistic stat : Statistic.values()) {
|
||||
if (StringUtil.startsWithIgnoreCase(stat.toString(), args[0])) {
|
||||
completions.add(stat.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args.length == 2) {
|
||||
Statistic statistic;
|
||||
try {
|
||||
statistic = Statistic.valueOf(args[0].toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return completions;
|
||||
}
|
||||
|
||||
switch (statistic.getType()) {
|
||||
case ITEM -> {
|
||||
for (Material material : Material.values()) {
|
||||
if (!material.isItem()) continue;
|
||||
if (StringUtil.startsWithIgnoreCase(material.toString(), args[1])) {
|
||||
completions.add(material.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
}
|
||||
case BLOCK -> {
|
||||
for (Material material : Material.values()) {
|
||||
if (!material.isBlock()) continue;
|
||||
if (StringUtil.startsWithIgnoreCase(material.toString(), args[1])) {
|
||||
completions.add(material.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
for (EntityType entityType : EntityType.values()) {
|
||||
if (StringUtil.startsWithIgnoreCase(entityType.toString(), args[1])) {
|
||||
completions.add(entityType.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return completions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender commandSender, @NotNull String s, @NotNull String[] strings) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Component.text("Only players can use this command"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strings.length < 1) return false;
|
||||
String statName = strings[0];
|
||||
Statistic stat = null;
|
||||
|
||||
try {
|
||||
stat = Statistic.valueOf(statName.toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException e) {
|
||||
commandSender.sendMessage(Component.text(getUsage()));
|
||||
return false;
|
||||
}
|
||||
|
||||
Object arg = null;
|
||||
|
||||
if (strings.length == 2) {
|
||||
try {
|
||||
arg = Material.valueOf(strings[1].toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
if (arg == null) {
|
||||
try {
|
||||
arg = EntityType.valueOf(strings[1].toUpperCase(Locale.ENGLISH));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
if (arg == null) {
|
||||
commandSender.sendMessage(Component.text(getUsage()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
StatsData statsData = null;
|
||||
|
||||
switch (stat.getType()) {
|
||||
case UNTYPED -> {
|
||||
statsData = cachedData.get(getStatId(stat, null));
|
||||
if (statsData == null || statsData.getTimestamp() + 60000 < System.currentTimeMillis()) {
|
||||
statsData = createData(stat);
|
||||
cachedData.put(getStatId(stat, null), statsData);
|
||||
}
|
||||
}
|
||||
case ITEM, BLOCK -> {
|
||||
if (!(arg instanceof Material material)) return false;
|
||||
statsData = cachedData.get(getStatId(stat, arg));
|
||||
if (statsData == null || statsData.getTimestamp() + 60000 < System.currentTimeMillis()) {
|
||||
statsData = createData(stat, material);
|
||||
cachedData.put(getStatId(stat, arg), statsData);
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
if (!(arg instanceof EntityType entityType)) return false;
|
||||
statsData = cachedData.get(getStatId(stat, arg));
|
||||
if (statsData == null || statsData.getTimestamp() + 60000 < System.currentTimeMillis()) {
|
||||
statsData = createData(stat, entityType);
|
||||
cachedData.put(getStatId(stat, arg), statsData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (statsData == null) {
|
||||
commandSender.sendMessage(Component.text("Error getting data"));
|
||||
return false;
|
||||
}
|
||||
|
||||
Component topComponent = statsData.getTop(10, commandSender);
|
||||
commandSender.sendMessage(topComponent);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getStatId(Statistic stat, Object arg) {
|
||||
|
||||
String statKey = stat.getKey().getKey();
|
||||
|
||||
return switch (stat.getType()) {
|
||||
case UNTYPED -> statKey;
|
||||
case ITEM, BLOCK -> {
|
||||
if (arg instanceof Material item) {
|
||||
yield statKey + "." + item;
|
||||
} else {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
if (arg instanceof EntityType entityType) {
|
||||
yield statKey + "." + entityType;
|
||||
} else {
|
||||
yield null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map of player names to their stat value. This will return null for stats that are not UNTYPED.
|
||||
*
|
||||
* @param stat statistic to get data for
|
||||
* @return map of player names to their stat value
|
||||
*/
|
||||
private StatsData createData(Statistic stat) {
|
||||
if (stat.getType() != Statistic.Type.UNTYPED) return null;
|
||||
HashMap<String, Integer> data = new HashMap<>();
|
||||
for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
|
||||
int statValue = offlinePlayer.getStatistic(stat);
|
||||
String playerName = offlinePlayer.getName();
|
||||
data.put(playerName, statValue);
|
||||
}
|
||||
|
||||
LinkedHashMap<String, Integer> sortedData = new LinkedHashMap<>();
|
||||
data.entrySet()
|
||||
.stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.forEachOrdered(x -> sortedData.put(x.getKey(), x.getValue()));
|
||||
|
||||
return new StatsData(sortedData, stat, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map of player names to their stat value. This will return null for stats that are not ENTITY.
|
||||
*
|
||||
* @param stat statistic to get data for
|
||||
* @return map of player names to their stat value
|
||||
*/
|
||||
private StatsData createData(Statistic stat, EntityType entityType) {
|
||||
if (stat.getType() != Statistic.Type.ENTITY) return null;
|
||||
HashMap<String, Integer> data = new HashMap<>();
|
||||
for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
|
||||
int statValue = offlinePlayer.getStatistic(stat, entityType);
|
||||
String playerName = offlinePlayer.getName();
|
||||
data.put(playerName, statValue);
|
||||
}
|
||||
|
||||
LinkedHashMap<String, Integer> sortedData = new LinkedHashMap<>();
|
||||
data.entrySet()
|
||||
.stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.forEachOrdered(x -> sortedData.put(x.getKey(), x.getValue()));
|
||||
|
||||
return new StatsData(sortedData, stat, entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map of player names to their stat value. This will return null for stats that are not BLOCK or ITEM.
|
||||
*
|
||||
* @param stat statistic to get data for
|
||||
* @return map of player names to their stat value
|
||||
*/
|
||||
private StatsData createData(Statistic stat, Material item) {
|
||||
if (stat.getType() != Statistic.Type.ITEM && stat.getType() != Statistic.Type.BLOCK) return null;
|
||||
HashMap<String, Integer> data = new HashMap<>();
|
||||
for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) {
|
||||
int statValue = offlinePlayer.getStatistic(stat, item);
|
||||
String playerName = offlinePlayer.getName();
|
||||
data.put(playerName, statValue);
|
||||
}
|
||||
|
||||
LinkedHashMap<String, Integer> sortedData = new LinkedHashMap<>();
|
||||
data.entrySet()
|
||||
.stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.forEachOrdered(x -> sortedData.put(x.getKey(), x.getValue()));
|
||||
|
||||
return new StatsData(sortedData, stat, item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
name: StatsTop
|
||||
version: '${project.version}'
|
||||
main: me.youhavetrouble.statstop.StatsTop
|
||||
api-version: 1.20
|
||||
authors:
|
||||
- YouHaveTrouble
|
||||
api-version: "1.20"
|
||||
|
||||
Reference in New Issue
Block a user