diff --git a/README.md b/README.md index e208c33..c86e06b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,27 @@ Placeholders are delimited by '-'. Since they're applied with a replace, errors Maximum supported cooldown is 12 Hours aka 43200 seconds. Cooldowns are not saved to file, so they reset on server reload/restart. Use for example the player placeholder inside aac's configuration as part of the arbitrarykey. +#### With [spark](https://spark.lucko.me/]) installed +- ```tps:```\ +**Windows:** 5s, 10s, 1m, 5m, 15m +- ```mspt:, ]>```\ +**Functions:** min[window], max[window], mean[window], median[window], percentile[percentile, window]\ +**Windows:** 10s, 1m\ +**Returns:** the tick time in milliseconds\ +**Examples**: + - mspt:min[10s] + - mspt:percentile[95, 1m] + +- ```cpu:]>```\ + **Functions:** sys[window], proc[window]\ + **Windows:** 10s, 1m, 15m\ + **Returns:** on a scale from 0.0 to 1.0\ + **Examples**: + - mspt:sys[10s] + - mspt:proc[1m] + +**Note:** ```tps``` without parameters will stop working if spark is installed! + ### Multi command / delayed commands In the 'do' clause of the statement, multiple commands can be executed at once, and selected commands can be delayed if desired. The command delimiter is ```//```, where the integer between ```/``` and ```/``` denotes the delay before the command should be executed in ticks. Here are some examples: diff --git a/pom.xml b/pom.xml index 4925776..36a2361 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,10 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots + @@ -34,9 +38,15 @@ junit junit - 4.12 + 4.13.2 test + + me.lucko + spark-api + 0.1-SNAPSHOT + provided + @@ -44,7 +54,7 @@ maven-compiler-plugin - 3.3 + 3.11.0 1.7 1.7 diff --git a/src/main/java/me/konsolas/conditionalcommands/ConditionalCommands.java b/src/main/java/me/konsolas/conditionalcommands/ConditionalCommands.java index 29edfb1..4f591f2 100644 --- a/src/main/java/me/konsolas/conditionalcommands/ConditionalCommands.java +++ b/src/main/java/me/konsolas/conditionalcommands/ConditionalCommands.java @@ -1,6 +1,8 @@ package me.konsolas.conditionalcommands; import me.konsolas.conditionalcommands.placeholders.PlaceholderCooldown; +import me.konsolas.conditionalcommands.placeholders.PlaceholderTPSBukkit; +import me.konsolas.conditionalcommands.placeholders.PlaceholderTPSSpark; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; @@ -20,6 +22,10 @@ public void onEnable() { saveDefaultConfig(); getLogger().info("Initializing placeholders..."); + Placeholders.TPS.setPlaceHolder(Bukkit.getPluginManager().isPluginEnabled("spark") + ? new PlaceholderTPSSpark() + : new PlaceholderTPSBukkit()); + for (Placeholders placeholder : Placeholders.values()) { placeholder.getPlaceholder().init(this); } @@ -182,7 +188,7 @@ private void dispatchCommand(final CommandSender sender, String command) { } final String cmd = command.substring(cmdStart, cmdEnd).trim(); - + if (delay <= 0) { protectedDispatch(sender, cmd); } else { diff --git a/src/main/java/me/konsolas/conditionalcommands/Placeholders.java b/src/main/java/me/konsolas/conditionalcommands/Placeholders.java index aeabc70..8856544 100644 --- a/src/main/java/me/konsolas/conditionalcommands/Placeholders.java +++ b/src/main/java/me/konsolas/conditionalcommands/Placeholders.java @@ -4,7 +4,9 @@ enum Placeholders { PING(new PlaceholderPing()), - TPS(new PlaceholderTPS()), + TPS(null), + MSPT(new PlaceholderMSPT()), + CPU(new PlaceholderCPU()), TIME_ONLINE(new PlaceholderTimeOnline()), PLAYER_COUNT(new PlaceholderPlayerCount()), UPTIME(new PlaceholderUptime()), @@ -14,7 +16,7 @@ enum Placeholders { CHANCE(new PlaceholderChance()), COOLDOWN(new PlaceholderCooldown()); - private final Placeholder placeholder; + private Placeholder placeholder; Placeholders(Placeholder placeholder) { this.placeholder = placeholder; @@ -23,4 +25,8 @@ enum Placeholders { public Placeholder getPlaceholder() { return placeholder; } + + public void setPlaceHolder(Placeholder placeholder) { + this.placeholder = placeholder; + } } diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractParameteredPlaceholder.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractParameteredPlaceholder.java index f80607c..7f7248b 100644 --- a/src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractParameteredPlaceholder.java +++ b/src/main/java/me/konsolas/conditionalcommands/placeholders/AbstractParameteredPlaceholder.java @@ -10,7 +10,7 @@ public abstract class AbstractParameteredPlaceholder implements Placeholder { private final Pattern pattern; AbstractParameteredPlaceholder(String base) { - this.pattern = Pattern.compile("-" + base + ":([A-Za-z0-9%._]*)-"); + this.pattern = Pattern.compile("-" + base + ":([A-Za-z0-9%._]+(\\[[^]]*])?)-"); } @Override @@ -25,7 +25,9 @@ public String doSubstitution(String input, Player player) { Matcher matcher = pattern.matcher(input); while (matcher.find()) { - input = input.replaceAll(Pattern.quote(matcher.group()), getSub(player, matcher.group(1))); + String fullMatch = matcher.group(0); + String param = matcher.group(1); + input = input.replace(fullMatch, getSub(player, param)); } return input; diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderCPU.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderCPU.java new file mode 100644 index 0000000..385b569 --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderCPU.java @@ -0,0 +1,38 @@ +package me.konsolas.conditionalcommands.placeholders; + +import me.konsolas.conditionalcommands.providers.CPUProviderSpark; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class PlaceholderCPU extends AbstractParameteredPlaceholder { + + private CPUProviderSpark cpu; + + public PlaceholderCPU() { + super("cpu"); + } + + @Override + protected String getSub(Player player, String param) { + if (cpu == null) { + throw new RuntimeException("Cannot do cpu placeholder because spark is not installed"); + } + + return cpu.getValue(param).toString(); + } + + @Override + public void init(Plugin plugin) { + Bukkit.getScheduler().runTaskLater(plugin, new Runnable() { + @Override + public void run() { + if (Bukkit.getPluginManager().isPluginEnabled("spark")) { + cpu = new CPUProviderSpark(); + } else { + Bukkit.getLogger().warning("Spark is not installed!"); + } + } + }, 20); + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderMSPT.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderMSPT.java new file mode 100644 index 0000000..5f79c1b --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderMSPT.java @@ -0,0 +1,38 @@ +package me.konsolas.conditionalcommands.placeholders; + +import me.konsolas.conditionalcommands.providers.MSPTProviderSpark; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class PlaceholderMSPT extends AbstractParameteredPlaceholder { + + private MSPTProviderSpark mspt; + + public PlaceholderMSPT() { + super("mspt"); + } + + @Override + protected String getSub(Player player, String param) { + if (mspt == null) { + throw new RuntimeException("Cannot do mspt placeholder because spark is not installed"); + } + + return mspt.getValue(param).toString(); + } + + @Override + public void init(Plugin plugin) { + Bukkit.getScheduler().runTaskLater(plugin, new Runnable() { + @Override + public void run() { + if (Bukkit.getPluginManager().isPluginEnabled("spark")) { + mspt = new MSPTProviderSpark(); + } else { + Bukkit.getLogger().warning("Spark is not installed!"); + } + } + }, 20); + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPS.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPS.java deleted file mode 100644 index a4e3aec..0000000 --- a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPS.java +++ /dev/null @@ -1,65 +0,0 @@ -package me.konsolas.conditionalcommands.placeholders; - -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitRunnable; - -import java.util.ArrayDeque; -import java.util.Collections; -import java.util.Deque; - -public class PlaceholderTPS extends AbstractStandardPlaceholder { - private TPS tps; - - public PlaceholderTPS() { - super("tps"); - } - - @Override - public void init(Plugin plugin) { - tps = new TPS(plugin); - } - - @Override - public double getStat(Player player) { - return tps.getCurrentTPS(); - } - - private static class TPS extends BukkitRunnable { - private int resolution = 40; - private long lastTick; - private Deque tickIntervals; - - private TPS(Plugin plugin) { - lastTick = System.currentTimeMillis(); - tickIntervals = new ArrayDeque<>(Collections.nCopies(resolution, 50L)); - this.runTaskTimer(plugin, 1, 1); - } - - double getCurrentTPS() { - try { - double tps = 1000D / getDelta(); - return tps > 20D ? 20D : tps; - } catch (Exception e) { - return 20D; - } - } - - @Override - public void run() { - long curr = System.currentTimeMillis(); - long delta = curr - lastTick; - lastTick = curr; - tickIntervals.removeFirst(); - tickIntervals.addLast(delta); - } - - private double getDelta() { - int base = 0; - for (long delta : tickIntervals) { - base += delta; - } - return (double) base / (double) resolution; - } - } -} diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSBukkit.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSBukkit.java new file mode 100644 index 0000000..4d34d75 --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSBukkit.java @@ -0,0 +1,24 @@ +package me.konsolas.conditionalcommands.placeholders; + +import me.konsolas.conditionalcommands.providers.Provider; +import me.konsolas.conditionalcommands.providers.TPSProviderBukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class PlaceholderTPSBukkit extends AbstractStandardPlaceholder { + private Provider tps; + + public PlaceholderTPSBukkit() { + super("tps"); + } + + @Override + protected double getStat(Player player) { + return tps.getValue(); + } + + @Override + public void init(Plugin plugin) { + tps = new TPSProviderBukkit(plugin); + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSSpark.java b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSSpark.java new file mode 100644 index 0000000..a5869ce --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/placeholders/PlaceholderTPSSpark.java @@ -0,0 +1,32 @@ +package me.konsolas.conditionalcommands.placeholders; + +import me.konsolas.conditionalcommands.providers.ParameteredProvider; +import me.konsolas.conditionalcommands.providers.TPSProviderSpark; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class PlaceholderTPSSpark extends AbstractParameteredPlaceholder { + private ParameteredProvider tps; + + public PlaceholderTPSSpark() { + super("tps"); + } + + @Override + protected String getSub(Player player, String param) { + return tps.getValue(param).toString(); + } + + @Override + public void init(Plugin plugin) { + Bukkit.getScheduler().runTaskLater(plugin, new Runnable() { + @Override + public void run() { + if (Bukkit.getPluginManager().isPluginEnabled("spark")) { + tps = new TPSProviderSpark(); + } + } + }, 20); + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/CPUProviderSpark.java b/src/main/java/me/konsolas/conditionalcommands/providers/CPUProviderSpark.java new file mode 100644 index 0000000..af53839 --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/CPUProviderSpark.java @@ -0,0 +1,79 @@ +package me.konsolas.conditionalcommands.providers; + +import me.lucko.spark.api.Spark; +import me.lucko.spark.api.statistic.StatisticWindow; +import me.lucko.spark.api.statistic.types.DoubleStatistic; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CPUProviderSpark implements ParameteredProvider { + + private DoubleStatistic sys, proc; + + public CPUProviderSpark() { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(Spark.class); + if (provider != null) { + sys = provider.getProvider().cpuSystem(); + proc = provider.getProvider().cpuProcess(); + } + } + + public Double getValue(String param) { + Command command = parseCommand(param); + if (command == null) { + return proc.poll(StatisticWindow.CpuUsage.SECONDS_10); + } + + switch (command.function) { + case "sys": + return sys.poll(command.window); + case "proc": + return proc.poll(command.window); + default: + throw new RuntimeException("Could not parse CPU command for input: \"" + param + "\""); + } + } + + private Command parseCommand(String param) { + Pattern pattern = Pattern.compile("(\\w+)\\[(\\d+)([sm])?\\]"); + Matcher matcher = pattern.matcher(param); + + if (matcher.matches()) { + String function = matcher.group(1); + String arg = matcher.group(2); + String timeUnit = matcher.group(3); + StatisticWindow.CpuUsage window = parseWindow(arg + timeUnit); + int percentile = function.equals("percentile") ? Integer.parseInt(arg) : 95; + return new Command(function, window, percentile); + } + return null; + } + + private StatisticWindow.CpuUsage parseWindow(String arg) { + switch (arg) { + case "10s": + return StatisticWindow.CpuUsage.SECONDS_10; + case "1m": + return StatisticWindow.CpuUsage.MINUTES_1; + case "15m": + return StatisticWindow.CpuUsage.MINUTES_15; + default: + throw new IllegalArgumentException("Invalid time unit " + arg); + } + } + + private static class Command { + String function; + StatisticWindow.CpuUsage window; + int percentile; + + Command(String function, StatisticWindow.CpuUsage window, int percentile) { + this.function = function; + this.window = window; + this.percentile = percentile; + } + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/MSPTProviderSpark.java b/src/main/java/me/konsolas/conditionalcommands/providers/MSPTProviderSpark.java new file mode 100644 index 0000000..ceecb86 --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/MSPTProviderSpark.java @@ -0,0 +1,88 @@ +package me.konsolas.conditionalcommands.providers; + +import me.lucko.spark.api.Spark; +import me.lucko.spark.api.statistic.StatisticWindow; +import me.lucko.spark.api.statistic.misc.DoubleAverageInfo; +import me.lucko.spark.api.statistic.types.GenericStatistic; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MSPTProviderSpark implements ParameteredProvider { + + private GenericStatistic msptSpark; + + public MSPTProviderSpark() { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(Spark.class); + if (provider != null) { + msptSpark = provider.getProvider().mspt(); + } + } + + public Double getValue(String param) { + Command command = parseCommand(param); + if (command == null) { + return msptSpark.poll(StatisticWindow.MillisPerTick.SECONDS_10).max(); + } + + switch (command.function) { + case "max": + return msptSpark.poll(command.window).max(); + case "mean": + return msptSpark.poll(command.window).mean(); + case "min": + return msptSpark.poll(command.window).min(); + case "median": + return msptSpark.poll(command.window).median(); + case "percentile": + return msptSpark.poll(command.window).percentile(command.percentile); + default: + throw new RuntimeException("Could not parse MSPT command for input: \"" + param + "\""); + } + } + + private Command parseCommand(String param) { + Pattern pattern = Pattern.compile("(\\w+)\\[(\\d+)([sm])?\\]"); + Matcher matcher = pattern.matcher(param); + + if (matcher.matches()) { + String function = matcher.group(1); + String arg = matcher.group(2); + String timeUnit = matcher.group(3); + StatisticWindow.MillisPerTick window = parseWindow(arg, timeUnit); + int percentile = function.equals("percentile") ? Integer.parseInt(arg) : 95; + return new Command(function, window, percentile); + } + return null; + } + + private StatisticWindow.MillisPerTick parseWindow(String arg, String timeUnit) { + if (arg == null || timeUnit == null) { + return StatisticWindow.MillisPerTick.SECONDS_10; + } + + int duration = Integer.parseInt(arg); + switch (timeUnit) { + case "s": + return duration == 10 ? StatisticWindow.MillisPerTick.SECONDS_10 : null; + case "m": + return duration == 1 ? StatisticWindow.MillisPerTick.MINUTES_1 : null; + default: + return StatisticWindow.MillisPerTick.SECONDS_10; + } + } + + private static class Command { + String function; + StatisticWindow.MillisPerTick window; + int percentile; + + Command(String function, StatisticWindow.MillisPerTick window, int percentile) { + this.function = function; + this.window = window; + this.percentile = percentile; + } + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/ParameteredProvider.java b/src/main/java/me/konsolas/conditionalcommands/providers/ParameteredProvider.java new file mode 100644 index 0000000..492febc --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/ParameteredProvider.java @@ -0,0 +1,7 @@ +package me.konsolas.conditionalcommands.providers; + +public interface ParameteredProvider { + + T getValue(String param); + +} \ No newline at end of file diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/Provider.java b/src/main/java/me/konsolas/conditionalcommands/providers/Provider.java new file mode 100644 index 0000000..98bab22 --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/Provider.java @@ -0,0 +1,7 @@ +package me.konsolas.conditionalcommands.providers; + +public interface Provider { + + T getValue(); + +} diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderBukkit.java b/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderBukkit.java new file mode 100644 index 0000000..3fb8d3a --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderBukkit.java @@ -0,0 +1,46 @@ +package me.konsolas.conditionalcommands.providers; + +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.ArrayDeque; +import java.util.Collections; +import java.util.Deque; + +public class TPSProviderBukkit extends BukkitRunnable implements Provider { + private final int resolution = 40; + private final Deque tickIntervals; + private long lastTick; + + public TPSProviderBukkit(Plugin plugin) { + lastTick = System.currentTimeMillis(); + tickIntervals = new ArrayDeque<>(Collections.nCopies(resolution, 50L)); + this.runTaskTimer(plugin, 1, 1); + } + + public Double getValue() { + try { + double tps = 1000D / getDelta(); + return Math.min(tps, 20D); + } catch (Exception e) { + return 20D; + } + } + + @Override + public void run() { + long curr = System.currentTimeMillis(); + long delta = curr - lastTick; + lastTick = curr; + tickIntervals.removeFirst(); + tickIntervals.addLast(delta); + } + + private double getDelta() { + int base = 0; + for (long delta : tickIntervals) { + base += (int) delta; + } + return (double) base / (double) resolution; + } +} diff --git a/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderSpark.java b/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderSpark.java new file mode 100644 index 0000000..70504ab --- /dev/null +++ b/src/main/java/me/konsolas/conditionalcommands/providers/TPSProviderSpark.java @@ -0,0 +1,68 @@ +package me.konsolas.conditionalcommands.providers; + +import me.lucko.spark.api.Spark; +import me.lucko.spark.api.statistic.StatisticWindow; +import me.lucko.spark.api.statistic.types.DoubleStatistic; +import org.bukkit.Bukkit; +import org.bukkit.plugin.RegisteredServiceProvider; + +public class TPSProviderSpark implements + ParameteredProvider { + private final DoubleStatistic tpsSpark; + + public TPSProviderSpark() { + RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(Spark.class); + tpsSpark = provider.getProvider().tps(); + } + + @Override + public Double getValue(String param) { + StatisticWindow.TicksPerSecond window = parseWindow(param); + return tpsSpark.poll(window); + } + + private StatisticWindow.TicksPerSecond parseWindow(String param) { + if (param == null || param.isEmpty()) { + return StatisticWindow.TicksPerSecond.SECONDS_5; // Default value + } + + int duration; + String timeUnit; + + if (param.matches("\\d+[sm]")) { // Matches patterns like "5s", "10m", etc. + duration = Integer.parseInt(param.substring(0, param.length() - 1)); + timeUnit = param.substring(param.length() - 1); + } else if (param.matches("\\d+")) { // Matches patterns like "5", "10", etc. + duration = Integer.parseInt(param); + timeUnit = "s"; // Default to seconds + } else if (param.equals("m")) { + duration = 1; + timeUnit = "m"; + } else { + return StatisticWindow.TicksPerSecond.SECONDS_5; // Default value for unrecognized patterns + } + + switch (timeUnit) { + case "s": + switch (duration) { + case 5: + return StatisticWindow.TicksPerSecond.SECONDS_5; + case 10: + return StatisticWindow.TicksPerSecond.SECONDS_10; + default: + return StatisticWindow.TicksPerSecond.SECONDS_5; + } + case "m": + switch (duration) { + case 1: + return StatisticWindow.TicksPerSecond.MINUTES_1; + case 15: + return StatisticWindow.TicksPerSecond.MINUTES_15; + default: + return StatisticWindow.TicksPerSecond.MINUTES_1; + } + default: + return StatisticWindow.TicksPerSecond.SECONDS_5; // Default value for unrecognized time unit + } + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 428f8cd..d5ec78e 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,7 +5,7 @@ api-version: 1.13 description: Excecute commands based on conditions. Useful in configured commands from other plugins author: konsolas website: https://github.com/konsolas/ConditionalCommands -softdepend: [AAC] +softdepend: [AAC, spark] commands: ccmd: usage: / [args...]