diff options
Diffstat (limited to 'spark-common/src')
58 files changed, 3075 insertions, 960 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java index f92abf3..dae04ff 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java @@ -45,13 +45,16 @@ import me.lucko.spark.common.monitor.ping.PingStatistics; import me.lucko.spark.common.monitor.ping.PlayerPingProvider; import me.lucko.spark.common.monitor.tick.TickStatistics; import me.lucko.spark.common.platform.PlatformStatisticsProvider; +import me.lucko.spark.common.sampler.BackgroundSamplerManager; +import me.lucko.spark.common.sampler.SamplerContainer; +import me.lucko.spark.common.sampler.source.ClassSourceLookup; import me.lucko.spark.common.tick.TickHook; import me.lucko.spark.common.tick.TickReporter; import me.lucko.spark.common.util.BytebinClient; -import me.lucko.spark.common.util.ClassSourceLookup; import me.lucko.spark.common.util.Configuration; import me.lucko.spark.common.util.TemporaryFiles; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import java.io.IOException; @@ -62,6 +65,7 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; @@ -72,13 +76,11 @@ import java.util.stream.Collectors; import static net.kyori.adventure.text.Component.space; import static net.kyori.adventure.text.Component.text; -import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY; import static net.kyori.adventure.text.format.NamedTextColor.GOLD; import static net.kyori.adventure.text.format.NamedTextColor.GRAY; import static net.kyori.adventure.text.format.NamedTextColor.RED; import static net.kyori.adventure.text.format.NamedTextColor.WHITE; import static net.kyori.adventure.text.format.TextDecoration.BOLD; -import static net.kyori.adventure.text.format.TextDecoration.UNDERLINED; /** * Abstract spark implementation used by all platforms. @@ -89,6 +91,7 @@ public class SparkPlatform { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss"); private final SparkPlugin plugin; + private final TemporaryFiles temporaryFiles; private final Configuration configuration; private final String viewerUrl; private final BytebinClient bytebinClient; @@ -97,6 +100,8 @@ public class SparkPlatform { private final List<Command> commands; private final ReentrantLock commandExecuteLock = new ReentrantLock(true); private final ActivityLog activityLog; + private final SamplerContainer samplerContainer; + private final BackgroundSamplerManager backgroundSamplerManager; private final TickHook tickHook; private final TickReporter tickReporter; private final TickStatistics tickStatistics; @@ -109,6 +114,7 @@ public class SparkPlatform { public SparkPlatform(SparkPlugin plugin) { this.plugin = plugin; + this.temporaryFiles = new TemporaryFiles(this.plugin.getPluginDirectory().resolve("tmp")); this.configuration = new Configuration(this.plugin.getPluginDirectory().resolve("config.json")); this.viewerUrl = this.configuration.getString("viewerUrl", "https://spark.lucko.me/"); @@ -135,6 +141,9 @@ public class SparkPlatform { this.activityLog = new ActivityLog(plugin.getPluginDirectory().resolve("activity.json")); this.activityLog.load(); + this.samplerContainer = new SamplerContainer(); + this.backgroundSamplerManager = new BackgroundSamplerManager(this, this.configuration); + this.tickHook = plugin.createTickHook(); this.tickReporter = plugin.createTickReporter(); this.tickStatistics = this.tickHook != null || this.tickReporter != null ? new TickStatistics() : null; @@ -173,6 +182,8 @@ public class SparkPlatform { SparkApi api = new SparkApi(this); this.plugin.registerApi(api); SparkApi.register(api); + + this.backgroundSamplerManager.initialise(); } public void disable() { @@ -190,15 +201,21 @@ public class SparkPlatform { module.close(); } + this.samplerContainer.close(); + SparkApi.unregister(); - TemporaryFiles.deleteTemporaryFiles(); + this.temporaryFiles.deleteTemporaryFiles(); } public SparkPlugin getPlugin() { return this.plugin; } + public TemporaryFiles getTemporaryFiles() { + return this.temporaryFiles; + } + public Configuration getConfiguration() { return this.configuration; } @@ -223,6 +240,14 @@ public class SparkPlatform { return this.activityLog; } + public SamplerContainer getSamplerContainer() { + return this.samplerContainer; + } + + public BackgroundSamplerManager getBackgroundSamplerManager() { + return this.backgroundSamplerManager; + } + public TickHook getTickHook() { return this.tickHook; } @@ -356,14 +381,15 @@ public class SparkPlatform { .append(text("v" + getPlugin().getVersion(), GRAY)) .build() ); + + String helpCmd = "/" + getPlugin().getCommandName() + " help"; resp.replyPrefixed(text() .color(GRAY) - .append(text("Use ")) + .append(text("Run ")) .append(text() - .content("/" + getPlugin().getCommandName() + " help") + .content(helpCmd) .color(WHITE) - .decoration(UNDERLINED, true) - .clickEvent(ClickEvent.runCommand("/" + getPlugin().getCommandName() + " help")) + .clickEvent(ClickEvent.runCommand(helpCmd)) .build() ) .append(text(" to view usage information.")) @@ -379,7 +405,7 @@ public class SparkPlatform { if (command.aliases().contains(alias)) { resp.setCommandPrimaryAlias(command.primaryAlias()); try { - command.executor().execute(this, sender, resp, new Arguments(rawArgs)); + command.executor().execute(this, sender, resp, new Arguments(rawArgs, command.allowSubCommand())); } catch (Arguments.ParseException e) { resp.replyPrefixed(text(e.getMessage(), RED)); } @@ -427,35 +453,53 @@ public class SparkPlatform { ); for (Command command : commands) { String usage = "/" + getPlugin().getCommandName() + " " + command.primaryAlias(); - ClickEvent clickEvent = ClickEvent.suggestCommand(usage); - sender.reply(text() - .append(text(">", GOLD, BOLD)) - .append(space()) - .append(text().content(usage).color(GRAY).clickEvent(clickEvent).build()) - .build() - ); - for (Command.ArgumentInfo arg : command.arguments()) { - if (arg.requiresParameter()) { + + if (command.allowSubCommand()) { + Map<String, List<Command.ArgumentInfo>> argumentsBySubCommand = command.arguments().stream() + .collect(Collectors.groupingBy(Command.ArgumentInfo::subCommandName, LinkedHashMap::new, Collectors.toList())); + + argumentsBySubCommand.forEach((subCommand, arguments) -> { + String subCommandUsage = usage + " " + subCommand; + sender.reply(text() - .content(" ") - .append(text("[", DARK_GRAY)) - .append(text("--" + arg.argumentName(), GRAY)) + .append(text(">", GOLD, BOLD)) .append(space()) - .append(text("<" + arg.parameterDescription() + ">", DARK_GRAY)) - .append(text("]", DARK_GRAY)) - .build() - ); - } else { - sender.reply(text() - .content(" ") - .append(text("[", DARK_GRAY)) - .append(text("--" + arg.argumentName(), GRAY)) - .append(text("]", DARK_GRAY)) + .append(text().content(subCommandUsage).color(GRAY).clickEvent(ClickEvent.suggestCommand(subCommandUsage)).build()) .build() ); + + for (Command.ArgumentInfo arg : arguments) { + if (arg.argumentName().isEmpty()) { + continue; + } + sender.reply(arg.toComponent(" ")); + } + }); + } else { + sender.reply(text() + .append(text(">", GOLD, BOLD)) + .append(space()) + .append(text().content(usage).color(GRAY).clickEvent(ClickEvent.suggestCommand(usage)).build()) + .build() + ); + + for (Command.ArgumentInfo arg : command.arguments()) { + sender.reply(arg.toComponent(" ")); } } } + + sender.reply(Component.empty()); + sender.replyPrefixed(text() + .append(text("For full usage information, please go to: ")) + .append(text() + .content("https://spark.lucko.me/docs/Command-Usage") + .color(WHITE) + .clickEvent(ClickEvent.openUrl("https://spark.lucko.me/docs/Command-Usage")) + .build() + ) + .build() + ); } } diff --git a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java index 1116b04..b7aef2a 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java +++ b/spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java @@ -23,15 +23,19 @@ package me.lucko.spark.common; import me.lucko.spark.api.Spark; import me.lucko.spark.common.command.sender.CommandSender; import me.lucko.spark.common.monitor.ping.PlayerPingProvider; +import me.lucko.spark.common.platform.MetadataProvider; import me.lucko.spark.common.platform.PlatformInfo; import me.lucko.spark.common.platform.serverconfig.ServerConfigProvider; import me.lucko.spark.common.platform.world.WorldInfoProvider; import me.lucko.spark.common.sampler.ThreadDumper; +import me.lucko.spark.common.sampler.source.ClassSourceLookup; +import me.lucko.spark.common.sampler.source.SourceMetadata; import me.lucko.spark.common.tick.TickHook; import me.lucko.spark.common.tick.TickReporter; -import me.lucko.spark.common.util.ClassSourceLookup; import java.nio.file.Path; +import java.util.Collection; +import java.util.Collections; import java.util.logging.Level; import java.util.stream.Stream; @@ -133,6 +137,15 @@ public interface SparkPlugin { } /** + * Gets a list of known sources (plugins/mods) on the platform. + * + * @return a list of sources + */ + default Collection<SourceMetadata> getKnownSources() { + return Collections.emptyList(); + } + + /** * Creates a player ping provider function. * * <p>Returns {@code null} if the platform does not support querying player pings</p> @@ -149,7 +162,16 @@ public interface SparkPlugin { * @return the server config provider function */ default ServerConfigProvider createServerConfigProvider() { - return ServerConfigProvider.NO_OP; + return null; + } + + /** + * Creates a metadata provider for the platform. + * + * @return the platform extra metadata provider + */ + default MetadataProvider createExtraMetadataProvider() { + return null; } /** diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java index 17c49e2..ad8c777 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/Arguments.java @@ -38,8 +38,9 @@ public class Arguments { private final List<String> rawArgs; private final SetMultimap<String, String> parsedArgs; + private String parsedSubCommand = null; - public Arguments(List<String> rawArgs) { + public Arguments(List<String> rawArgs, boolean allowSubCommand) { this.rawArgs = rawArgs; this.parsedArgs = HashMultimap.create(); @@ -52,7 +53,9 @@ public class Arguments { Matcher matcher = FLAG_REGEX.matcher(arg); boolean matches = matcher.matches(); - if (flag == null || matches) { + if (i == 0 && allowSubCommand && !matches) { + this.parsedSubCommand = arg; + } else if (flag == null || matches) { if (!matches) { throw new ParseException("Expected flag at position " + i + " but got '" + arg + "' instead!"); } @@ -80,6 +83,10 @@ public class Arguments { return this.rawArgs; } + public String subCommand() { + return this.parsedSubCommand; + } + public int intFlag(String key) { Iterator<String> it = this.parsedArgs.get(key).iterator(); if (it.hasNext()) { diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java index dad15e6..c6871a9 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/Command.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/Command.java @@ -25,10 +25,17 @@ import com.google.common.collect.ImmutableList; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.sender.CommandSender; +import net.kyori.adventure.text.Component; + import java.util.Collections; import java.util.List; import java.util.Objects; +import static net.kyori.adventure.text.Component.space; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.DARK_GRAY; +import static net.kyori.adventure.text.format.NamedTextColor.GRAY; + public class Command { public static Builder builder() { @@ -39,12 +46,14 @@ public class Command { private final List<ArgumentInfo> arguments; private final Executor executor; private final TabCompleter tabCompleter; + private final boolean allowSubCommand; - private Command(List<String> aliases, List<ArgumentInfo> arguments, Executor executor, TabCompleter tabCompleter) { + private Command(List<String> aliases, List<ArgumentInfo> arguments, Executor executor, TabCompleter tabCompleter, boolean allowSubCommand) { this.aliases = aliases; this.arguments = arguments; this.executor = executor; this.tabCompleter = tabCompleter; + this.allowSubCommand = allowSubCommand; } public List<String> aliases() { @@ -67,11 +76,16 @@ public class Command { return this.aliases.get(0); } + public boolean allowSubCommand() { + return this.allowSubCommand; + } + public static final class Builder { private final ImmutableList.Builder<String> aliases = ImmutableList.builder(); private final ImmutableList.Builder<ArgumentInfo> arguments = ImmutableList.builder(); private Executor executor = null; private TabCompleter tabCompleter = null; + private boolean allowSubCommand = false; Builder() { @@ -82,8 +96,13 @@ public class Command { return this; } + public Builder argumentUsage(String subCommandName, String argumentName, String parameterDescription) { + this.arguments.add(new ArgumentInfo(subCommandName, argumentName, parameterDescription)); + return this; + } + public Builder argumentUsage(String argumentName, String parameterDescription) { - this.arguments.add(new ArgumentInfo(argumentName, parameterDescription)); + this.arguments.add(new ArgumentInfo("", argumentName, parameterDescription)); return this; } @@ -97,6 +116,11 @@ public class Command { return this; } + public Builder allowSubCommand(boolean allowSubCommand) { + this.allowSubCommand = allowSubCommand; + return this; + } + public Command build() { List<String> aliases = this.aliases.build(); if (aliases.isEmpty()) { @@ -108,7 +132,7 @@ public class Command { if (this.tabCompleter == null) { this.tabCompleter = TabCompleter.empty(); } - return new Command(aliases, this.arguments.build(), this.executor, this.tabCompleter); + return new Command(aliases, this.arguments.build(), this.executor, this.tabCompleter, this.allowSubCommand); } } @@ -127,14 +151,20 @@ public class Command { } public static final class ArgumentInfo { + private final String subCommandName; private final String argumentName; private final String parameterDescription; - public ArgumentInfo(String argumentName, String parameterDescription) { + public ArgumentInfo(String subCommandName, String argumentName, String parameterDescription) { + this.subCommandName = subCommandName; this.argumentName = argumentName; this.parameterDescription = parameterDescription; } + public String subCommandName() { + return this.subCommandName; + } + public String argumentName() { return this.argumentName; } @@ -146,6 +176,26 @@ public class Command { public boolean requiresParameter() { return this.parameterDescription != null; } + + public Component toComponent(String padding) { + if (requiresParameter()) { + return text() + .content(padding) + .append(text("[", DARK_GRAY)) + .append(text("--" + argumentName(), GRAY)) + .append(space()) + .append(text("<" + parameterDescription() + ">", DARK_GRAY)) + .append(text("]", DARK_GRAY)) + .build(); + } else { + return text() + .content(padding) + .append(text("[", DARK_GRAY)) + .append(text("--" + argumentName(), GRAY)) + . |
