aboutsummaryrefslogtreecommitdiff
path: root/spark-common/src/main/java/me/lucko/spark
diff options
context:
space:
mode:
authorLuck <git@lucko.me>2019-04-28 15:37:32 +0100
committerLuck <git@lucko.me>2019-05-04 23:22:32 +0100
commitc3ae37e88f967a21522af7d0cb79a571326cd7e9 (patch)
tree7ce46c61407dacdff3cabaeb1ffc8eb5e7cd614a /spark-common/src/main/java/me/lucko/spark
parent51fa2b3e64f021c3c0535f9f931d3fae27ca7adc (diff)
downloadspark-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')
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/ActivityLog.java195
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/CommandSender.java33
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlatform.java84
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/SparkPlugin.java7
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/Command.java45
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/CommandModule.java4
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/CommandResponseHandler.java22
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/ActivityLogModule.java120
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/HealthModule.java30
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/MemoryModule.java11
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/SamplerModule.java11
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/command/modules/TickMonitoringModule.java10
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TickMonitor.java12
-rw-r--r--spark-common/src/main/java/me/lucko/spark/common/monitor/tick/TpsCalculator.java2
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(", "))