diff options
author | Luck <git@lucko.me> | 2019-04-28 15:37:32 +0100 |
---|---|---|
committer | Luck <git@lucko.me> | 2019-05-04 23:22:32 +0100 |
commit | c3ae37e88f967a21522af7d0cb79a571326cd7e9 (patch) | |
tree | 7ce46c61407dacdff3cabaeb1ffc8eb5e7cd614a /spark-common/src/main/java/me/lucko/spark | |
parent | 51fa2b3e64f021c3c0535f9f931d3fae27ca7adc (diff) | |
download | spark-c3ae37e88f967a21522af7d0cb79a571326cd7e9.tar.gz spark-c3ae37e88f967a21522af7d0cb79a571326cd7e9.tar.bz2 spark-c3ae37e88f967a21522af7d0cb79a571326cd7e9.zip |
Start implementing activity log feature
Diffstat (limited to 'spark-common/src/main/java/me/lucko/spark')
14 files changed, 486 insertions, 100 deletions
diff --git a/spark-common/src/main/java/me/lucko/spark/common/ActivityLog.java b/spark-common/src/main/java/me/lucko/spark/common/ActivityLog.java new file mode 100644 index 0000000..4b5e942 --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/ActivityLog.java @@ -0,0 +1,195 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.common; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class ActivityLog { + + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final JsonParser PARSER = new JsonParser(); + + private final Path file; + + private final List<Activity> log = new LinkedList<>(); + private final Object[] mutex = new Object[0]; + + public ActivityLog(Path file) { + this.file = file; + } + + public void addToLog(Activity activity) { + synchronized (this.mutex) { + this.log.add(activity); + } + save(); + } + + public List<Activity> getLog() { + synchronized (this.mutex) { + return new LinkedList<>(this.log); + } + } + + public void save() { + JsonArray array = new JsonArray(); + synchronized (this.mutex) { + for (Activity activity : this.log) { + if (!activity.shouldExpire()) { + array.add(activity.serialize()); + } + } + } + + try { + Files.createDirectories(this.file.getParent()); + } catch (IOException e) { + // ignore + } + + try (BufferedWriter writer = Files.newBufferedWriter(this.file, StandardCharsets.UTF_8)) { + GSON.toJson(array, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + if (!Files.exists(this.file)) { + synchronized (this.mutex) { + this.log.clear(); + return; + } + } + + JsonArray array; + try (BufferedReader reader = Files.newBufferedReader(this.file, StandardCharsets.UTF_8)) { + array = PARSER.parse(reader).getAsJsonArray(); + } catch (IOException e) { + e.printStackTrace(); + return; + } + + boolean save = false; + + synchronized (this.mutex) { + this.log.clear(); + for (JsonElement element : array) { + try { + Activity activity = Activity.deserialize(element); + if (activity.shouldExpire()) { + save = true; + } + this.log.add(activity); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + if (save) { + try { + save(); + } catch (Exception e) { + // ignore + } + } + } + + public static final class Activity { + private final String user; + private final long time; + private final String type; + private final String url; + + public Activity(String user, long time, String type, String url) { + this.user = user; + this.time = time; + this.type = type; + this.url = url; + } + + public String getUser() { + return this.user; + } + + public long getTime() { + return this.time; + } + + public String getType() { + return this.type; + } + + public String getUrl() { + return this.url; + } + + public boolean shouldExpire() { + return (System.currentTimeMillis() - this.time) > TimeUnit.DAYS.toMillis(7); + } + + public JsonObject serialize() { + JsonObject object = new JsonObject(); + + JsonObject user = new JsonObject(); + user.add("name", new JsonPrimitive(this.user)); + object.add("user", user); + + object.add("time", new JsonPrimitive(this.time)); + object.add("type", new JsonPrimitive(this.type)); + + JsonObject data = new JsonObject(); + data.add("url", new JsonPrimitive(this.url)); + object.add("data", data); + + return object; + } + + public static Activity deserialize(JsonElement element) { + JsonObject object = element.getAsJsonObject(); + + String user = object.get("user").getAsJsonObject().get("name").getAsJsonPrimitive().getAsString(); + long time = object.get("time").getAsJsonPrimitive().getAsLong(); + String type = object.get("type").getAsJsonPrimitive().getAsString(); + String url = object.get("data").getAsJsonObject().get("url").getAsJsonPrimitive().getAsString(); + + return new Activity(user, time, type, url); + } + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/CommandSender.java b/spark-common/src/main/java/me/lucko/spark/common/CommandSender.java new file mode 100644 index 0000000..9d2ecc0 --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/CommandSender.java @@ -0,0 +1,33 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.common; + +import net.kyori.text.Component; + +public interface CommandSender { + + String getName(); + + void sendMessage(Component message); + + boolean hasPermission(String permission); + +} 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 6b3eb21..d05d9e8 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 @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableList; import me.lucko.spark.common.command.Arguments; import me.lucko.spark.common.command.Command; import me.lucko.spark.common.command.CommandResponseHandler; +import me.lucko.spark.common.command.modules.ActivityLogModule; import me.lucko.spark.common.command.modules.HealthModule; import me.lucko.spark.common.command.modules.MemoryModule; import me.lucko.spark.common.command.modules.SamplerModule; @@ -35,6 +36,7 @@ import me.lucko.spark.common.sampler.TickCounter; import me.lucko.spark.common.util.BytebinClient; import net.kyori.text.Component; import net.kyori.text.TextComponent; +import net.kyori.text.event.ClickEvent; import net.kyori.text.format.TextColor; import net.kyori.text.format.TextDecoration; import okhttp3.OkHttpClient; @@ -47,10 +49,8 @@ import java.util.stream.Collectors; /** * Abstract spark implementation used by all platforms. - * - * @param <S> the sender (e.g. CommandSender) type used by the platform */ -public class SparkPlatform<S> { +public class SparkPlatform { /** The URL of the viewer frontend */ public static final String VIEWER_URL = "https://sparkprofiler.github.io/#"; @@ -59,22 +59,26 @@ public class SparkPlatform<S> { /** The bytebin instance used by the platform */ public static final BytebinClient BYTEBIN_CLIENT = new BytebinClient(OK_HTTP_CLIENT, "https://bytebin.lucko.me/", "spark-plugin"); - private final List<Command<S>> commands; - private final SparkPlugin<S> plugin; - + private final SparkPlugin plugin; + private final List<Command> commands; + private final ActivityLog activityLog; private final TickCounter tickCounter; private final TpsCalculator tpsCalculator; - public SparkPlatform(SparkPlugin<S> plugin) { + public SparkPlatform(SparkPlugin plugin) { this.plugin = plugin; - ImmutableList.Builder<Command<S>> commandsBuilder = ImmutableList.builder(); - new SamplerModule<S>().registerCommands(commandsBuilder::add); - new HealthModule<S>().registerCommands(commandsBuilder::add); - new TickMonitoringModule<S>().registerCommands(commandsBuilder::add); - new MemoryModule<S>().registerCommands(commandsBuilder::add); + ImmutableList.Builder<Command> commandsBuilder = ImmutableList.builder(); + new SamplerModule().registerCommands(commandsBuilder::add); + new HealthModule().registerCommands(commandsBuilder::add); + new TickMonitoringModule().registerCommands(commandsBuilder::add); + new MemoryModule().registerCommands(commandsBuilder::add); + new ActivityLogModule().registerCommands(commandsBuilder::add); this.commands = commandsBuilder.build(); + this.activityLog = new ActivityLog(plugin.getPluginFolder().resolve("activity.json")); + this.activityLog.load(); + this.tickCounter = plugin.createTickCounter(); this.tpsCalculator = this.tickCounter != null ? new TpsCalculator() : null; } @@ -92,10 +96,14 @@ public class SparkPlatform<S> { } } - public SparkPlugin<S> getPlugin() { + public SparkPlugin getPlugin() { return this.plugin; } + public ActivityLog getActivityLog() { + return this.activityLog; + } + public TickCounter getTickCounter() { return this.tickCounter; } @@ -104,17 +112,39 @@ public class SparkPlatform<S> { return this.tpsCalculator; } - public void executeCommand(S sender, String[] args) { - CommandResponseHandler<S> resp = new CommandResponseHandler<>(this, sender); + public void executeCommand(CommandSender sender, String[] args) { + CommandResponseHandler resp = new CommandResponseHandler(this, sender); + + if (!sender.hasPermission("spark")) { + resp.replyPrefixed(TextComponent.of("You do not have permission to use this command.", TextColor.RED)); + return; + } + if (args.length == 0) { - sendUsage(resp); + resp.replyPrefixed(TextComponent.builder("") + .append(TextComponent.of("spark", TextColor.WHITE)) + .append(Component.space()) + .append(TextComponent.of("v" + getPlugin().getVersion(), TextColor.GRAY)) + .build() + ); + resp.replyPrefixed(TextComponent.builder("").color(TextColor.GRAY) + .append(TextComponent.of("Use ")) + .append(TextComponent.builder("/" + getPlugin().getLabel() + " help") + .color(TextColor.WHITE) + .decoration(TextDecoration.UNDERLINED, true) + .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, getPlugin().getLabel() + " help")) + .build() + ) + .append(TextComponent.of(" to view usage information.")) + .build() + ); return; } ArrayList<String> rawArgs = new ArrayList<>(Arrays.asList(args)); String alias = rawArgs.remove(0).toLowerCase(); - for (Command<S> command : this.commands) { + for (Command command : this.commands) { if (command.aliases().contains(alias)) { try { command.executor().execute(this, sender, resp, new Arguments(rawArgs)); @@ -129,7 +159,11 @@ public class SparkPlatform<S> { sendUsage(resp); } - public List<String> tabCompleteCommand(S sender, String[] args) { + public List<String> tabCompleteCommand(CommandSender sender, String[] args) { + if (!sender.hasPermission("spark")) { + return Collections.emptyList(); + } + List<String> arguments = new ArrayList<>(Arrays.asList(args)); if (args.length <= 1) { @@ -140,7 +174,7 @@ public class SparkPlatform<S> { } String alias = arguments.remove(0); - for (Command<S> command : this.commands) { + for (Command command : this.commands) { if (command.aliases().contains(alias)) { return command.tabCompleter().completions(this, sender, arguments); } @@ -149,18 +183,20 @@ public class SparkPlatform<S> { return Collections.emptyList(); } - private void sendUsage(CommandResponseHandler<S> sender) { - sender.replyPrefixed(TextComponent.builder() + private void sendUsage(CommandResponseHandler sender) { + sender.replyPrefixed(TextComponent.builder("") .append(TextComponent.of("spark", TextColor.WHITE)) .append(Component.space()) .append(TextComponent.of("v" + getPlugin().getVersion(), TextColor.GRAY)) .build() ); - for (Command<S> command : this.commands) { - sender.reply(TextComponent.builder() + for (Command command : this.commands) { + String usage = getPlugin().getLabel() + " " + command.aliases().get(0); + ClickEvent clickEvent = new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, usage); + sender.reply(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.GOLD).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) - .append(TextComponent.of("/" + getPlugin().getLabel() + " " + command.aliases().get(0), TextColor.GRAY)) + .append(TextComponent.builder("/" + usage).color(TextColor.GRAY).clickEvent(clickEvent).build()) .build() ); for (Command.ArgumentInfo arg : command.arguments()) { 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 19eb3d3..175fda5 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 @@ -22,12 +22,11 @@ package me.lucko.spark.common; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.TickCounter; -import net.kyori.text.Component; import java.nio.file.Path; import java.util.Set; -public interface SparkPlugin<S> { +public interface SparkPlugin { String getVersion(); @@ -35,9 +34,7 @@ public interface SparkPlugin<S> { String getLabel(); - Set<S> getSendersWithPermission(String permission); - - void sendMessage(S sender, Component message); + Set<CommandSender> getSendersWithPermission(String permission); void runAsync(Runnable r); 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 c9f6551..ab95cb6 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 @@ -21,24 +21,25 @@ package me.lucko.spark.common.command; import com.google.common.collect.ImmutableList; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import java.util.Collections; import java.util.List; import java.util.Objects; -public class Command<S> { +public class Command { - public static <S> Builder<S> builder() { - return new Builder<>(); + public static Builder builder() { + return new Builder(); } private final List<String> aliases; private final List<ArgumentInfo> arguments; - private final Executor<S> executor; - private final TabCompleter<S> tabCompleter; + private final Executor executor; + private final TabCompleter tabCompleter; - private Command(List<String> aliases, List<ArgumentInfo> arguments, Executor<S> executor, TabCompleter<S> tabCompleter) { + private Command(List<String> aliases, List<ArgumentInfo> arguments, Executor executor, TabCompleter tabCompleter) { this.aliases = aliases; this.arguments = arguments; this.executor = executor; @@ -53,45 +54,45 @@ public class Command<S> { return this.arguments; } - public Executor<S> executor() { + public Executor executor() { return this.executor; } - public TabCompleter<S> tabCompleter() { + public TabCompleter tabCompleter() { return this.tabCompleter; } - public static final class Builder<S> { + public static final class Builder { private final ImmutableList.Builder<String> aliases = ImmutableList.builder(); private final ImmutableList.Builder<ArgumentInfo> arguments = ImmutableList.builder(); - private Executor<S> executor = null; - private TabCompleter<S> tabCompleter = null; + private Executor executor = null; + private TabCompleter tabCompleter = null; Builder() { } - public Builder<S> aliases(String... aliases) { + public Builder aliases(String... aliases) { this.aliases.add(aliases); return this; } - public Builder<S> argumentUsage(String argumentName, String parameterDescription) { + public Builder argumentUsage(String argumentName, String parameterDescription) { this.arguments.add(new ArgumentInfo(argumentName, parameterDescription)); return this; } - public Builder<S> executor(Executor<S> executor) { + public Builder executor(Executor executor) { this.executor = Objects.requireNonNull(executor, "executor"); return this; } - public Builder<S> tabCompleter(TabCompleter<S> tabCompleter) { + public Builder tabCompleter(TabCompleter tabCompleter) { this.tabCompleter = Objects.requireNonNull(tabCompleter, "tabCompleter"); return this; } - public Command<S> build() { + public Command build() { List<String> aliases = this.aliases.build(); if (aliases.isEmpty()) { throw new IllegalStateException("No aliases defined"); @@ -102,22 +103,22 @@ public class Command<S> { 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); } } @FunctionalInterface - public interface Executor<S> { - void execute(SparkPlatform<S> platform, S sender, CommandResponseHandler resp, Arguments arguments); + public interface Executor { + void execute(SparkPlatform platform, CommandSender sender, CommandResponseHandler resp, Arguments arguments); } @FunctionalInterface - public interface TabCompleter<S> { - static <S> TabCompleter<S> empty() { + public interface TabCompleter { + static <S> TabCompleter empty() { return (platform, sender, arguments) -> Collections.emptyList(); } - List<String> completions(SparkPlatform<S> platform, S sender, List<String> arguments); + List<String> completions(SparkPlatform platform, CommandSender sender, List<String> arguments); } public static final class ArgumentInfo { diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/CommandModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/CommandModule.java index f195ef2..76c24b3 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/CommandModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/CommandModule.java @@ -22,8 +22,8 @@ package me.lucko.spark.common.command; import java.util.function.Consumer; -public interface CommandModule<S> { +public interface CommandModule { - void registerCommands(Consumer<Command<S>> consumer); + void registerCommands(Consumer<Command> consumer); } diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java index 94e1946..d31d1cf 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java @@ -20,50 +20,50 @@ package me.lucko.spark.common.command; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import net.kyori.text.Component; import net.kyori.text.TextComponent; -import net.kyori.text.event.ClickEvent; import net.kyori.text.format.TextColor; import net.kyori.text.format.TextDecoration; import java.util.Set; import java.util.function.Consumer; -public class CommandResponseHandler<S> { +public class CommandResponseHandler { /** The prefix used in all messages "&8[&e&l⚡&8] &7" */ - private static final TextComponent PREFIX = TextComponent.builder().color(TextColor.GRAY) + private static final TextComponent PREFIX = TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of("[", TextColor.DARK_GRAY)) .append(TextComponent.builder("⚡").color(TextColor.YELLOW).decoration(TextDecoration.BOLD, TextDecoration.State.TRUE).build()) .append(TextComponent.of("]", TextColor.DARK_GRAY)) .append(TextComponent.of(" ")) .build(); - private final SparkPlatform<S> platform; - private final S sender; + private final SparkPlatform platform; + private final CommandSender sender; - public CommandResponseHandler(SparkPlatform<S> platform, S sender) { + public CommandResponseHandler(SparkPlatform platform, CommandSender sender) { this.platform = platform; this.sender = sender; } - public S sender() { + public CommandSender sender() { return this.sender; } - public void allSenders(Consumer<? super S> action) { - Set<S> senders = this.platform.getPlugin().getSendersWithPermission("spark"); + public void allSenders(Consumer<? super CommandSender> action) { + Set<CommandSender> senders = this.platform.getPlugin().getSendersWithPermission("spark"); senders.add(this.sender); senders.forEach(action); } public void reply(Component message) { - this.platform.getPlugin().sendMessage(this.sender, message); + this.sender.sendMessage(message); } public void broadcast(Component message) { - allSenders(sender -> this.platform.getPlugin().sendMessage(sender, message)); + allSenders(sender -> sender.sendMessage(message)); } public void replyPrefixed(Component message) { diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java new file mode 100644 index 0000000..ad6b3e8 --- /dev/null +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java @@ -0,0 +1,120 @@ +/* + * This file is part of spark. + * + * Copyright (c) lucko (Luck) <luck@lucko.me> + * Copyright (c) contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package me.lucko.spark.common.command.modules; + +import me.lucko.spark.common.ActivityLog; +import me.lucko.spark.common.command.Command; +import me.lucko.spark.common.command.CommandModule; +import net.kyori.text.Component; +import net.kyori.text.TextComponent; +import net.kyori.text.event.ClickEvent; +import net.kyori.text.format.TextColor; +import net.kyori.text.format.TextDecoration; + +import java.util.List; +import java.util.function.Consumer; + +public class ActivityLogModule implements CommandModule { + + @Override + public void registerCommands(Consumer<Command> consumer) { + consumer.accept(Command.builder() + .aliases("activity", "activitylog", "log") + .executor((platform, sender, resp, arguments) -> { + List<ActivityLog.Activity> log = platform.getActivityLog().getLog(); + log.removeIf(ActivityLog.Activity::shouldExpire); + + if (log.isEmpty()) { + resp.replyPrefixed(TextComponent.of("There are no entries present in the log.")); + return; + } + + resp.replyPrefixed(TextComponent.of("Showing recent spark activity...", TextColor.GOLD)); + + int count = 0; + for (ActivityLog.Activity activity : log) { + count++; + + resp.replyPrefixed(TextComponent.builder("") + .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) + .append(Component.space()) + .append(TextComponent.of("#" + count, TextColor.WHITE)) + .append(TextComponent.of(" - ", TextColor.DARK_GRAY)) + .append(TextComponent.of(activity.getType(), TextColor.YELLOW)) + .append(TextComponent.of(" - ", TextColor.DARK_GRAY)) + .append(TextComponent.of(formatDateDiff(activity.getTime()), TextColor.GRAY)) + .build() + ); + resp.replyPrefixed(TextComponent.builder(" ") + .append(TextComponent.of("Created by: ", TextColor.GRAY)) + .append(TextComponent.of(activity.getUser(), TextColor.WHITE)) + .build() + ); + resp.replyPrefixed(TextComponent.builder(" ") + .append(TextComponent.of("Link: ", TextColor.GRAY)) + .append(TextComponent.builder(activity.getUrl()) + .color(TextColor.WHITE) + .decoration(TextDecoration.UNDERLINED, true) + .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, activity.getUrl())) + .build() + ) + .build() + ); + resp.reply(Component.space()); + } + }) + .tabCompleter(Command.TabCompleter.empty()) + .build() + ); + } + + private static String formatDateDiff(long time) { + long seconds = (System.currentTimeMillis() - time) / 1000; + + if (seconds <= 0) { + return "now"; + } + + long minute = seconds / 60; + seconds = seconds % 60; + long hour = minute / 60; + minute = minute % 60; + long day = hour / 24; + hour = hour % 24; + + StringBuilder sb = new StringBuilder(); + if (day != 0) { + sb.append(day).append("d "); + } + if (hour != 0) { + sb.append(hour).append("h "); + } + if (minute != 0) { + sb.append(minute).append("m "); + } + if (seconds != 0) { + sb.append(seconds).append("s"); + } + + return sb.toString().trim() + " ago"; + } + +} diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java index 097d264..83f9006 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java @@ -44,11 +44,11 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -public class HealthModule<S> implements CommandModule<S> { +public class HealthModule implements CommandModule { @Override - public void registerCommands(Consumer<Command<S>> consumer) { - consumer.accept(Command.<S>builder() + public void registerCommands(Consumer<Command> consumer) { + consumer.accept(Command.builder() .aliases("tps") .executor((platform, sender, resp, arguments) -> { TpsCalculator tpsCalculator = platform.getTpsCalculator(); @@ -63,7 +63,7 @@ public class HealthModule<S> implements CommandModule<S> { .build() ); - consumer.accept(Command.<S>builder() + consumer.accept(Command.builder() .aliases("healthreport", "health", "ht") .argumentUsage("memory", null) .executor((platform, sender, resp, arguments) -> { @@ -74,7 +74,7 @@ public class HealthModule<S> implements CommandModule<S> { TpsCalculator tpsCalculator = platform.getTpsCalculator(); if (tpsCalculator != null) { - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of("TPS from last 5s, 10s, 1m, 5m, 15m:", TextColor.GOLD)) @@ -86,7 +86,7 @@ public class HealthModule<S> implements CommandModule<S> { MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage(); - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of("Memory usage:", TextColor.GOLD)) @@ -109,7 +109,7 @@ public class HealthModule<S> implements CommandModule<S> { if (arguments.boolFlag("memory")) { MemoryUsage nonHeapUsage = memoryMXBean.getNonHeapMemoryUsage(); - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of("Non-heap memory usage:", TextColor.GOLD)) @@ -134,7 +134,7 @@ public class HealthModule<S> implements CommandModule<S> { usage = new MemoryUsage(usage.getInit(), usage.getUsed(), usage.getCommitted(), usage.getCommitted()); } - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of(memoryPool.getName() + " pool usage:", TextColor.GOLD)) @@ -172,7 +172,7 @@ public class HealthModule<S> implements CommandModule<S> { double processCpuLoad = CpuMonitor.getProcessCpuLoad(); if (systemCpuLoad >= 0 || processCpuLoad >= 0) { - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of("CPU usage:", TextColor.GOLD)) @@ -203,7 +203,7 @@ public class HealthModule<S> implements CommandModule<S> { FileStore fileStore = Files.getFileStore(Paths.get(".")); long totalSpace = fileStore.getTotalSpace(); long usedSpace = totalSpace - fileStore.getUsableSpace(); - report.add(TextComponent.builder() + report.add(TextComponent.builder("") .append(TextComponent.builder(">").color(TextColor.DARK_GRAY).decoration(TextDecoration.BOLD, true).build()) .append(Component.space()) .append(TextComponent.of("Disk usage:", TextColor.GOLD)) @@ -227,7 +227,7 @@ public class HealthModule<S> implements CommandModule<S> { e.printStackTrace(); } - TextComponent.Builder builder = TextComponent.builder(); + TextComponent.Builder builder = TextComponent.builder(""); report.forEach(line -> builder.append(line).append(Component.newline())); resp.reply(builder.build()); }); @@ -259,7 +259,7 @@ public class HealthModule<S> implements CommandModule<S> { line.append(TextComponent.of(Strings.repeat(" ", (length - committedChars)))); } - return TextComponent.builder() + return TextComponent.builder("") .append(TextComponent.of("[", TextColor.DARK_GRAY)) .append(line.build()) .append(TextComponent.of("]", TextColor.DARK_GRAY)) @@ -293,7 +293,7 @@ public class HealthModule<S> implements CommandModule<S> { line.append(TextComponent.of(Strings.repeat(" ", (length - committedChars)))); } - return TextComponent.builder() + return TextComponent.builder("") .append(TextComponent.of("[", TextColor.DARK_GRAY)) .append(line.build()) .append(TextComponent.of("]", TextColor.DARK_GRAY)) @@ -303,7 +303,7 @@ public class HealthModule<S> implements CommandModule<S> { private static TextComponent generateCpuUsageDiagram(double usage, int length) { int usedChars = (int) ((usage * length)); String line = Strings.repeat("/", usedChars) + Strings.repeat(" ", length - usedChars); - return TextComponent.builder() + return TextComponent.builder("") .append(TextComponent.of("[", TextColor.DARK_GRAY)) .append(TextComponent.of(line, TextColor.GRAY)) .append(TextComponent.of("]", TextColor.DARK_GRAY)) @@ -313,7 +313,7 @@ public class HealthModule<S> implements CommandModule<S> { private static TextComponent generateDiskUsageDiagram(double used, double max, int length) { int usedChars = (int) ((used * length) / max); String line = Strings.repeat("/", usedChars) + Strings.repeat(" ", length - usedChars); - return TextComponent.builder() + return TextComponent.builder("") .append(TextComponent.of("[", TextColor.DARK_GRAY)) .append(TextComponent.of(line, TextColor.GRAY)) .append(TextComponent.of("]", TextColor.DARK_GRAY)) diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java index e2d817d..b65db78 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java @@ -20,6 +20,7 @@ package me.lucko.spark.common.command.modules; +import me.lucko.spark.common.ActivityLog; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.Command; import me.lucko.spark.common.command.CommandModule; @@ -38,12 +39,12 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.function.Consumer; -public class MemoryModule<S> implements CommandModule<S> { +public class MemoryModule implements CommandModule { private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); @Override - public void registerCommands(Consumer<Command<S>> consumer) { - consumer.accept(Command.<S>builder() + public void registerCommands(Consumer<Command> consumer) { + consumer.accept(Command.builder() .aliases("heapsummary") .argumentUsage("run-gc-before", null) .executor((platform, sender, resp, arguments) -> { @@ -75,6 +76,8 @@ public class MemoryModule<S> implements CommandModule<S> { .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)) .build() ); + + platform.getActivityLog().addToLog(new ActivityLog.Activity(sender.getName(), System.currentTimeMillis(), "Heap dump summary", url)); } catch (IOException e) { resp.broadcastPrefixed(TextComponent.of("An error occurred whilst uploading the data.", TextColor.RED)); e.printStackTrace(); @@ -85,7 +88,7 @@ public class MemoryModule<S> implements CommandModule<S> { .build() ); - consumer.accept(Command.<S>builder() + consumer.accept(Command.builder() .aliases("heapdump") .argumentUsage("run-gc-before", null) .argumentUsage("include-non-live", null) diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java index e450b0b..fb1bc72 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java @@ -20,6 +20,7 @@ package me.lucko.spark.common.command.modules; +import me.lucko.spark.common.ActivityLog; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.command.Command; import me.lucko.spark.common.command.CommandModule; @@ -46,7 +47,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -public class SamplerModule<S> implements CommandModule<S> { +public class SamplerModule implements CommandModule { private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8"); /** Guards {@link #activeSampler} */ @@ -55,8 +56,8 @@ public class SamplerModule<S> implements CommandModule<S> { private Sampler activeSampler = null; @Override - public void registerCommands(Consumer<Command<S>> consumer) { - consumer.accept(Command.<S>builder() + public void registerCommands(Consumer<Command> consumer) { + consumer.accept(Command.builder() .aliases("sampler") .argumentUsage("info", null) .argumentUsage("stop", null) @@ -246,7 +247,7 @@ public class SamplerModule<S> implements CommandModule<S> { ); } - private void handleUpload(SparkPlatform<S> platform, CommandResponseHandler<S> resp, Sampler sampler) { + private void handleUpload(SparkPlatform platform, CommandResponseHandler resp, Sampler sampler) { platform.getPlugin().runAsync(() -> { byte[] output = sampler.formCompressedDataPayload(); try { @@ -259,6 +260,8 @@ public class SamplerModule<S> implements CommandModule<S> { .clickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)) .build() ); + + platform.getActivityLog().addToLog(new ActivityLog.Activity(resp.sender().getName(), System.currentTimeMillis(), "Sampler", url)); } catch (IOException e) { resp.broadcastPrefixed(TextComponent.of("An error occurred whilst uploading the results.", TextColor.RED)); e.printStackTrace(); diff --git a/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java b/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java index 3b26018..cbcf557 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java +++ b/spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java @@ -32,14 +32,14 @@ import net.kyori.text.format.TextColor; import java.util.function.Consumer; -public class TickMonitoringModule<S> implements CommandModule<S> { +public class TickMonitoringModule implements CommandModule { /** The tick monitor instance currently running, if any */ private ReportingTickMonitor activeTickMonitor = null; @Override - public void registerCommands(Consumer<Command<S>> consumer) { - consumer.accept(Command.<S>builder() + public void registerCommands(Consumer<Command> consumer) { + consumer.accept(Command.builder() .aliases("tickmonitoring") .argumentUsage("threshold", "percentage increase") .argumentUsage("without-gc", null) @@ -71,9 +71,9 @@ public class TickMonitoringModule<S> implements CommandModule<S> { } private class ReportingTickMonitor extends TickMonitor { - private final CommandResponseHandler<S> resp; + private final CommandResponseHandler resp; - ReportingTickMonitor(CommandResponseHandler<S> resp, TickCounter tickCounter, int percentageChangeThreshold, boolean monitorGc) { + ReportingTickMonitor(CommandResponseHandler resp, TickCounter tickCounter, int percentageChangeThreshold, boolean monitorGc) { super(tickCounter, percentageChangeThreshold, monitorGc); this.resp = resp; } diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java index 3d91368..5e1ec54 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java @@ -21,14 +21,12 @@ package me.lucko.spark.common.monitor.tick; import com.sun.management.GarbageCollectionNotificationInfo; -import com.sun.org.apache.regexp.internal.RE; import me.lucko.spark.common.monitor.memory.GarbageCollectionMonitor; import me.lucko.spark.common.sampler.TickCounter; import net.kyori.text.Component; import net.kyori.text.TextComponent; import net.kyori.text.format.TextColor; -import java.awt.*; import java.text.DecimalFormat; import java.util.DoubleSummaryStatistics; @@ -96,7 +94,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec // move onto the next state if (this.averageTickTime.getCount() >= 120) { sendMessage(TextComponent.of("Analysis is now complete.", TextColor.GOLD)); - sendMessage(TextComponent.builder().color(TextColor.GRAY) + sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of(">", TextColor.WHITE)) .append(Component.space()) .append(TextComponent.of("Max: ")) @@ -104,7 +102,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec .append(TextComponent.of("ms")) .build() ); - sendMessage(TextComponent.builder().color(TextColor.GRAY) + sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of(">", TextColor.WHITE)) .append(Component.space()) .append(TextComponent.of("Min: ")) @@ -112,7 +110,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec .append(TextComponent.of("ms")) .build() ); - sendMessage(TextComponent.builder().color(TextColor.GRAY) + sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of(">", TextColor.WHITE)) .append(Component.space()) .append(TextComponent.of("Avg: ")) @@ -136,7 +134,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec double percentageChange = (increase * 100d) / this.avg; if (percentageChange > this.percentageChangeThreshold) { - sendMessage(TextComponent.builder().color(TextColor.GRAY) + sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of("Tick ")) .append(TextComponent.of("#" + counter.getCurrentTick(), TextColor.DARK_GRAY)) .append(TextComponent.of(" lasted ")) @@ -166,7 +164,7 @@ public abstract class TickMonitor implements TickCounter.TickTask, GarbageCollec gcType = "Old Gen GC"; } - sendMessage(TextComponent.builder().color(TextColor.GRAY) + sendMessage(TextComponent.builder("").color(TextColor.GRAY) .append(TextComponent.of("Tick ")) .append(TextComponent.of("#" + this.tickCounter.getCurrentTick(), TextColor.DARK_GRAY)) .append(TextComponent.of(" included ")) diff --git a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java index 90a7b78..2278e0c 100644 --- a/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java +++ b/spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java @@ -103,7 +103,7 @@ public class TpsCalculator implements TickCounter.TickTask { } public TextComponent toFormattedComponent() { - return TextComponent.builder() + return TextComponent.builder("") .append(format(this.tps5S.getAverage())).append(TextComponent.of(", ")) .append(format(this.tps10S.getAverage())).append(TextComponent.of(", ")) .append(format(this.tps1M.getAverage())).append(TextComponent.of(", ")) |