diff options
31 files changed, 903 insertions, 265 deletions
diff --git a/spark-bukkit/build.gradle b/spark-bukkit/build.gradle index e8c6721..836e389 100644 --- a/spark-bukkit/build.gradle +++ b/spark-bukkit/build.gradle @@ -1,6 +1,8 @@ dependencies { compile project(':spark-common') - compile 'net.kyori:text-adapter-bukkit:1.0.3' + compile('net.kyori:text-adapter-bukkit:1.0.3') { + exclude(module: 'text') + } compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT' } diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitCommandSender.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitCommandSender.java new file mode 100644 index 0000000..dacea76 --- /dev/null +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/BukkitCommandSender.java @@ -0,0 +1,61 @@ +/* + * 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.bukkit; + +import me.lucko.spark.common.CommandSender; +import net.kyori.text.Component; +import net.kyori.text.adapter.bukkit.TextAdapter; + +public class BukkitCommandSender implements CommandSender { + private final org.bukkit.command.CommandSender sender; + + public BukkitCommandSender(org.bukkit.command.CommandSender sender) { + this.sender = sender; + } + + @Override + public String getName() { + return this.sender.getName(); + } + + @Override + public void sendMessage(Component message) { + TextAdapter.sendComponent(this.sender, message); + } + + @Override + public boolean hasPermission(String permission) { + return this.sender.hasPermission(permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BukkitCommandSender that = (BukkitCommandSender) o; + return this.sender.equals(that.sender); + } + + @Override + public int hashCode() { + return this.sender.hashCode(); + } +} diff --git a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/SparkBukkitPlugin.java b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/SparkBukkitPlugin.java index 15c725d..ee85c70 100644 --- a/spark-bukkit/src/main/java/me/lucko/spark/bukkit/SparkBukkitPlugin.java +++ b/spark-bukkit/src/main/java/me/lucko/spark/bukkit/SparkBukkitPlugin.java @@ -20,29 +20,27 @@ package me.lucko.spark.bukkit; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.command.CommandResponseHandler; import me.lucko.spark.common.monitor.tick.TpsCalculator; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.TickCounter; -import net.kyori.text.Component; import net.kyori.text.TextComponent; -import net.kyori.text.adapter.bukkit.TextAdapter; import org.bukkit.ChatColor; import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; -public class SparkBukkitPlugin extends JavaPlugin implements SparkPlugin<CommandSender> { +public class SparkBukkitPlugin extends JavaPlugin implements SparkPlugin { - private final SparkPlatform<CommandSender> platform = new SparkPlatform<>(this); + private final SparkPlatform platform = new SparkPlatform(this); @Override public void onEnable() { @@ -56,7 +54,7 @@ public class SparkBukkitPlugin extends JavaPlugin implements SparkPlugin<Command return true; } - CommandResponseHandler<CommandSender> resp = new CommandResponseHandler<>(this.platform, sender); + CommandResponseHandler resp = new CommandResponseHandler(this.platform, new BukkitCommandSender(sender)); TpsCalculator tpsCalculator = this.platform.getTpsCalculator(); resp.replyPrefixed(TextComponent.of("TPS from last 5s, 10s, 1m, 5m, 15m:")); resp.replyPrefixed(TextComponent.builder(" ").append(tpsCalculator.toFormattedComponent()).build()); @@ -71,22 +69,14 @@ public class SparkBukkitPlugin extends JavaPlugin implements SparkPlugin<Command } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (!sender.hasPermission("spark")) { - sender.sendMessage(ChatColor.RED + "You do not have permission to use this command."); - return true; - } - - this.platform.executeCommand(sender, args); + public boolean onCommand(org.bukkit.command.CommandSender sender, Command command, String label, String[] args) { + this.platform.executeCommand(new BukkitCommandSender(sender), args); return true; } @Override - public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - if (!sender.hasPermission("spark")) { - return Collections.emptyList(); - } - return this.platform.tabCompleteCommand(sender, args); + public List<String> onTabComplete(org.bukkit.command.CommandSender sender, Command command, String alias, String[] args) { + return this.platform.tabCompleteCommand(new BukkitCommandSender(sender), args); } @Override @@ -106,15 +96,10 @@ public class SparkBukkitPlugin extends JavaPlugin implements SparkPlugin<Command @Override public Set<CommandSender> getSendersWithPermission(String permission) { - Set<CommandSender> senders = new HashSet<>(getServer().getOnlinePlayers()); + List<org.bukkit.command.CommandSender> senders = new LinkedList<>(getServer().getOnlinePlayers()); senders.removeIf(sender -> !sender.hasPermission(permission)); senders.add(getServer().getConsoleSender()); - return senders; - } - - @Override - public void sendMessage(CommandSender sender, Component message) { - TextAdapter.sendComponent(sender, message); + return senders.stream().map(BukkitCommandSender::new).collect(Collectors.toSet()); } @Override diff --git a/spark-bungeecord/build.gradle b/spark-bungeecord/build.gradle index 55dcb4e..5ee50d7 100644 --- a/spark-bungeecord/build.gradle +++ b/spark-bungeecord/build.gradle @@ -1,6 +1,8 @@ dependencies { compile project(':spark-common') - compile 'net.kyori:text-adapter-bungeecord:1.0.3' + compile('net.kyori:text-adapter-bungeecord:1.0.3') { + exclude(module: 'text') + } compileOnly 'net.md-5:bungeecord-api:1.12-SNAPSHOT' } diff --git a/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/BungeeCordCommandSender.java b/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/BungeeCordCommandSender.java new file mode 100644 index 0000000..d3a831c --- /dev/null +++ b/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/BungeeCordCommandSender.java @@ -0,0 +1,61 @@ +/* + * 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.bungeecord; + +import me.lucko.spark.common.CommandSender; +import net.kyori.text.Component; +import net.kyori.text.adapter.bungeecord.TextAdapter; + +public class BungeeCordCommandSender implements CommandSender { + private final net.md_5.bungee.api.CommandSender sender; + + public BungeeCordCommandSender(net.md_5.bungee.api.CommandSender sender) { + this.sender = sender; + } + + @Override + public String getName() { + return this.sender.getName(); + } + + @Override + public void sendMessage(Component message) { + TextAdapter.sendComponent(this.sender, message); + } + + @Override + public boolean hasPermission(String permission) { + return this.sender.hasPermission(permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BungeeCordCommandSender that = (BungeeCordCommandSender) o; + return this.sender.equals(that.sender); + } + + @Override + public int hashCode() { + return this.sender.hashCode(); + } +} diff --git a/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/SparkBungeeCordPlugin.java b/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/SparkBungeeCordPlugin.java index ba2ee99..193ae06 100644 --- a/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/SparkBungeeCordPlugin.java +++ b/spark-bungeecord/src/main/java/me/lucko/spark/bungeecord/SparkBungeeCordPlugin.java @@ -20,27 +20,24 @@ package me.lucko.spark.bungeecord; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.TickCounter; -import net.kyori.text.Component; -import net.kyori.text.adapter.bungeecord.TextAdapter; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.plugin.Command; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.TabExecutor; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; -public class SparkBungeeCordPlugin extends Plugin implements SparkPlugin<CommandSender> { +public class SparkBungeeCordPlugin extends Plugin implements SparkPlugin { - private final SparkPlatform<CommandSender> platform = new SparkPlatform<>(this); + private final SparkPlatform platform = new SparkPlatform(this); @Override public void onEnable() { @@ -70,15 +67,10 @@ public class SparkBungeeCordPlugin extends Plugin implements SparkPlugin<Command @Override public Set<CommandSender> getSendersWithPermission(String permission) { - Set<CommandSender> senders = new HashSet<>(getProxy().getPlayers()); + List<net.md_5.bungee.api.CommandSender> senders = new LinkedList<>(getProxy().getPlayers()); senders.removeIf(sender -> !sender.hasPermission(permission)); senders.add(getProxy().getConsole()); - return senders; - } - - @Override - public void sendMessage(CommandSender sender, Component message) { - TextAdapter.sendComponent(sender, message); + return senders.stream().map(BungeeCordCommandSender::new).collect(Collectors.toSet()); } @Override @@ -105,23 +97,13 @@ public class SparkBungeeCordPlugin extends Plugin implements SparkPlugin<Command } @Override - public void execute(CommandSender sender, String[] args) { - if (!sender.hasPermission("spark")) { - TextComponent msg = new TextComponent("You do not have permission to use this command."); - msg.setColor(ChatColor.RED); - sender.sendMessage(msg); - return; - } - - this.plugin.platform.executeCommand(sender, args); + public void execute(net.md_5.bungee.api.CommandSender sender, String[] args) { + this.plugin.platform.executeCommand(new BungeeCordCommandSender(sender), args); } @Override - public Iterable<String> onTabComplete(CommandSender sender, String[] args) { - if (!sender.hasPermission("spark")) { - return Collections.emptyList(); - } - return this.plugin.platform.tabCompleteCommand(sender, args); + public Iterable<String> onTabComplete(net.md_5.bungee.api.CommandSender sender, String[] args) { + return this.plugin.platform.tabCompleteCommand(new BungeeCordCommandSender(sender), args); } } } diff --git a/spark-common/build.gradle b/spark-common/build.gradle index a8b95c6..8a9ee16 100644 --- a/spark-common/build.gradle +++ b/spark-common/build.gradle @@ -1,7 +1,16 @@ dependencies { compile 'com.squareup.okhttp3:okhttp:3.14.1' compile 'com.squareup.okio:okio:1.17.3' - compile 'net.kyori:text-api:2.0.0' + compile('net.kyori:text-api:2.0.0') { + exclude(module: 'checker-qual') + } + compile('net.kyori:text-serializer-gson:2.0.0') { + exclude(module: 'text-api') + exclude(module: 'gson') + } + compile('net.kyori:text-serializer-legacy:2.0.0') { + exclude(module: 'text-api') + } compileOnly 'com.google.code.gson:gson:2.7' compileOnly 'com.google.guava:guava:19.0' } 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(", ")) diff --git a/spark-forge/build.gradle b/spark-forge/build.gradle index a8c31cb..140612d 100644 --- a/spark-forge/build.gradle +++ b/spark-forge/build.gradle @@ -34,12 +34,6 @@ processResources { dependencies { compile project(':spark-common') - - compile 'net.kyori:text:1.11-1.6.4', { - exclude(module: 'checker-qual') - exclude(module: 'guava') - exclude(module: 'gson') - } } shadowJar { diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeClientSparkPlugin.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeClientSparkPlugin.java index b214308..ec6a0fc 100644 --- a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeClientSparkPlugin.java +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeClientSparkPlugin.java @@ -20,10 +20,10 @@ package me.lucko.spark.forge; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.sampler.TickCounter; import net.minecraft.client.Minecraft; import net.minecraft.command.ICommandSender; -import net.minecraft.server.MinecraftServer; import net.minecraftforge.client.ClientCommandHandler; import net.minecraftforge.fml.common.gameevent.TickEvent; @@ -43,8 +43,13 @@ public class ForgeClientSparkPlugin extends ForgeSparkPlugin { } @Override - public Set<ICommandSender> getSendersWithPermission(String permission) { - return new HashSet<>(Collections.singleton(Minecraft.getMinecraft().player)); + boolean hasPermission(ICommandSender sender, String permission) { + return true; + } + + @Override + public Set<CommandSender> getSendersWithPermission(String permission) { + return new HashSet<>(Collections.singleton(new ForgeCommandSender(Minecraft.getMinecraft().player, this))); } @Override @@ -66,9 +71,4 @@ public class ForgeClientSparkPlugin extends ForgeSparkPlugin { public List<String> getAliases() { return Collections.singletonList("sparkclient"); } - - @Override - public boolean checkPermission(MinecraftServer server, ICommandSender sender) { - return true; - } } diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeCommandSender.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeCommandSender.java new file mode 100644 index 0000000..4b71d86 --- /dev/null +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeCommandSender.java @@ -0,0 +1,66 @@ +/* + * 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.forge; + +import me.lucko.spark.common.CommandSender; +import net.kyori.text.Component; +import net.kyori.text.serializer.gson.GsonComponentSerializer; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.text.ITextComponent; + +public class ForgeCommandSender implements CommandSender { + private final ICommandSender sender; + private final ForgeSparkPlugin plugin; + + public ForgeCommandSender(ICommandSender sender, ForgeSparkPlugin plugin) { + this.sender = sender; + this.plugin = plugin; + } + + @Override + public String getName() { + return this.sender.getName(); + } + + @Override + public void sendMessage(Component message) { + ITextComponent component = ITextComponent.Serializer.jsonToComponent(GsonComponentSerializer.INSTANCE.serialize(message)); + this.sender.sendMessage(component); + } + + @Override + public boolean hasPermission(String permission) { + return this.plugin.hasPermission(this.sender, permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ForgeCommandSender that = (ForgeCommandSender) o; + return this.sender.equals(that.sender); + } + + @Override + public int hashCode() { + return this.sender.hashCode(); + } +} diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeServerSparkPlugin.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeServerSparkPlugin.java index 167aa94..ba56f44 100644 --- a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeServerSparkPlugin.java +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeServerSparkPlugin.java @@ -20,6 +20,7 @@ package me.lucko.spark.forge; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.sampler.TickCounter; import net.minecraft.command.ICommandSender; import net.minecraft.server.MinecraftServer; @@ -27,9 +28,10 @@ import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.common.gameevent.TickEvent; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; public class ForgeServerSparkPlugin extends ForgeSparkPlugin { public ForgeServerSparkPlugin(SparkForgeMod mod) { @@ -37,12 +39,17 @@ public class ForgeServerSparkPlugin extends ForgeSparkPlugin { } @Override - public Set<ICommandSender> getSendersWithPermission(String permission) { + boolean hasPermission(ICommandSender sender, String permission) { + return sender.canUseCommand(4, permission); + } + + @Override + public Set<CommandSender> getSendersWithPermission(String permission) { MinecraftServer mcServer = FMLCommonHandler.instance().getMinecraftServerInstance(); - Set<ICommandSender> senders = new HashSet<>(mcServer.getPlayerList().getPlayers()); + List<ICommandSender> senders = new LinkedList<>(mcServer.getPlayerList().getPlayers()); senders.removeIf(sender -> !sender.canUseCommand(4, permission)); senders.add(mcServer); - return senders; + return senders.stream().map(sender -> new ForgeCommandSender(sender, this)).collect(Collectors.toSet()); } @Override @@ -64,9 +71,4 @@ public class ForgeServerSparkPlugin extends ForgeSparkPlugin { public List<String> getAliases() { return Collections.emptyList(); } - - @Override - public boolean checkPermission(MinecraftServer server, ICommandSender sender) { - return sender.canUseCommand(4, "spark"); - } } diff --git a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeSparkPlugin.java b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeSparkPlugin.java index 7298793..039a7de 100644 --- a/spark-forge/src/main/java/me/lucko/spark/forge/ForgeSparkPlugin.java +++ b/spark-forge/src/main/java/me/lucko/spark/forge/ForgeSparkPlugin.java @@ -24,43 +24,36 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.sampler.ThreadDumper; -import net.kyori.text.Component; -import net.kyori.text.TextComponent; -import net.kyori.text.serializer.ComponentSerializers; import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; import net.minecraft.server.MinecraftServer; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.Style; -import net.minecraft.util.text.TextComponentString; -import net.minecraft.util.text.TextFormatting; -import net.minecraft.util.text.event.ClickEvent; import net.minecraftforge.fml.common.Mod; import javax.annotation.Nullable; import java.nio.file.Path; -import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @SuppressWarnings("NullableProblems") -public abstract class ForgeSparkPlugin implements SparkPlugin<ICommandSender>, ICommand { +public abstract class ForgeSparkPlugin implements SparkPlugin, ICommand { private final SparkForgeMod mod; private final ScheduledExecutorService scheduler; - private final SparkPlatform<ICommandSender> platform; + private final SparkPlatform platform; protected ForgeSparkPlugin(SparkForgeMod mod) { this.mod = mod; this.scheduler = Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder().setNameFormat("spark-forge-async-worker").build() ); - this.platform = new SparkPlatform<>(this); + this.platform = new SparkPlatform(this); this.platform.enable(); } + abstract boolean hasPermission(ICommandSender sender, String permission); + @Override public String getVersion() { return SparkForgeMod.class.getAnnotation(Mod.class).version(); @@ -72,12 +65,6 @@ public abstract class ForgeSparkPlugin implements SparkPlugin<ICommandSender>, I } @Override - public void sendMessage(ICommandSender sender, Component message) { - ITextComponent component = ITextComponent.Serializer.jsonToComponent(ComponentSerializers.JSON.serialize(message)); - sender.sendMessage(component); - } - - @Override public void runAsync(Runnable r) { this.scheduler.execute(r); } @@ -96,25 +83,17 @@ public abstract class ForgeSparkPlugin implements SparkPlugin<ICommandSender>, I @Override public void execute(MinecraftServer server, ICommandSender sender, String[] args) { - if (!checkPermission(server, sender)) { - TextComponentString msg = new TextComponentString("You do not have permission to use this command."); - Style style = msg.getStyle(); - style.setColor(TextFormatting.GRAY); - msg.setStyle(style); - - sender.sendMessage(msg); - return; - } - - this.platform.executeCommand(sender, args); + this.platform.executeCommand(new ForgeCommandSender(sender, this), args); } @Override public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, @Nullable BlockPos blockPos) { - if (!checkPermission(server, sender)) { - return Collections.emptyList(); - } - return this.platform.tabCompleteCommand(sender, args); + return this.platform.tabCompleteCommand(new ForgeCommandSender(sender, this), args); + } + + @Override + public boolean checkPermission(MinecraftServer minecraftServer, ICommandSender sender) { + return hasPermission(sender, "spark"); } @Override diff --git a/spark-sponge/build.gradle b/spark-sponge/build.gradle index dee1aac..3fd93e2 100644 --- a/spark-sponge/build.gradle +++ b/spark-sponge/build.gradle @@ -4,7 +4,9 @@ plugins { dependencies { compile project(':spark-common') - compile 'net.kyori:text-adapter-spongeapi:1.0.3' + compile('net.kyori:text-adapter-spongeapi:1.0.3') { + exclude(module: 'text') + } compileOnly 'org.spongepowered:spongeapi:7.1.0' annotationProcessor 'org.spongepowered:spongeapi:7.1.0' } diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SparkSpongePlugin.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SparkSpongePlugin.java index c0727ac..537bfea 100644 --- a/spark-sponge/src/main/java/me/lucko/spark/sponge/SparkSpongePlugin.java +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SparkSpongePlugin.java @@ -21,12 +21,11 @@ package me.lucko.spark.sponge; import com.google.inject.Inject; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.TickCounter; -import net.kyori.text.Component; -import net.kyori.text.adapter.spongeapi.TextAdapter; import org.spongepowered.api.Game; import org.spongepowered.api.command.CommandCallable; import org.spongepowered.api.command.CommandResult; @@ -40,21 +39,16 @@ import org.spongepowered.api.plugin.Plugin; import org.spongepowered.api.scheduler.AsynchronousExecutor; import org.spongepowered.api.scheduler.SpongeExecutorService; import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.action.TextActions; -import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.text.serializer.TextSerializers; import org.spongepowered.api.world.Location; import org.spongepowered.api.world.World; import javax.annotation.Nullable; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.file.Path; -import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; @Plugin( id = "spark", @@ -67,13 +61,13 @@ import java.util.Set; @Dependency(id = "spongeapi") } ) -public class SparkSpongePlugin implements SparkPlugin<CommandSource> { +public class SparkSpongePlugin implements SparkPlugin { private final Game game; private final Path configDirectory; private final SpongeExecutorService asyncExecutor; - private final SparkPlatform<CommandSource> platform = new SparkPlatform<>(this); + private final SparkPlatform platform = new SparkPlatform(this); @Inject public SparkSpongePlugin(Game game, @ConfigDir(sharedRoot = false) Path configDirectory, @AsynchronousExecutor SpongeExecutorService asyncExecutor) { @@ -109,16 +103,11 @@ public class SparkSpongePlugin implements SparkPlugin<CommandSource> { } @Override - public Set<CommandSource> getSendersWithPermission(String permission) { - Set<CommandSource> senders = new HashSet<>(this.game.getServer().getOnlinePlayers()); + public Set<CommandSender> getSendersWithPermission(String permission) { + List<CommandSource> senders = new LinkedList<>(this.game.getServer().getOnlinePlayers()); senders.removeIf(sender -> !sender.hasPermission(permission)); senders.add(this.game.getServer().getConsole()); - return senders; - } - - @Override - public void sendMessage(CommandSource sender, Component message) { - TextAdapter.sendComponent(sender, message); + return senders.stream().map(SpongeCommandSender::new).collect(Collectors.toSet()); } @Override @@ -145,18 +134,13 @@ public class SparkSpongePlugin implements SparkPlugin<CommandSource> { @Override public CommandResult process(CommandSource source, String arguments) { - if (!testPermission(source)) { - source.sendMessage(Text.builder("You do not have permission to use this command.").color(TextColors.RED).build()); - return CommandResult.empty(); - } - - this.plugin.platform.executeCommand(source, arguments.split(" ")); + this.plugin.platform.executeCommand(new SpongeCommandSender(source), arguments.split(" ")); return CommandResult.empty(); } @Override public List<String> getSuggestions(CommandSource source, String arguments, @Nullable Location<World> targetPosition) { - return Collections.emptyList(); + return this.plugin.platform.tabCompleteCommand(new SpongeCommandSender(source), arguments.split(" ")); } @Override diff --git a/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java new file mode 100644 index 0000000..5b434fc --- /dev/null +++ b/spark-sponge/src/main/java/me/lucko/spark/sponge/SpongeCommandSender.java @@ -0,0 +1,62 @@ +/* + * 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.sponge; + +import me.lucko.spark.common.CommandSender; +import net.kyori.text.Component; +import net.kyori.text.adapter.spongeapi.TextAdapter; +import org.spongepowered.api.command.CommandSource; + +public class SpongeCommandSender implements CommandSender { + private final CommandSource source; + + public SpongeCommandSender(CommandSource source) { + this.source = source; + } + + @Override + public String getName() { + return this.source.getName(); + } + + @Override + public void sendMessage(Component message) { + TextAdapter.sendComponent(this.source, message); + } + + @Override + public boolean hasPermission(String permission) { + return this.source.hasPermission(permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpongeCommandSender that = (SpongeCommandSender) o; + return this.source.equals(that.source); + } + + @Override + public int hashCode() { + return this.source.hashCode(); + } +} diff --git a/spark-velocity/src/main/java/me/lucko/spark/velocity/SparkVelocityPlugin.java b/spark-velocity/src/main/java/me/lucko/spark/velocity/SparkVelocityPlugin.java index c1c2f2d..4a5f500 100644 --- a/spark-velocity/src/main/java/me/lucko/spark/velocity/SparkVelocityPlugin.java +++ b/spark-velocity/src/main/java/me/lucko/spark/velocity/SparkVelocityPlugin.java @@ -30,21 +30,19 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; +import me.lucko.spark.common.CommandSender; import me.lucko.spark.common.SparkPlatform; import me.lucko.spark.common.SparkPlugin; import me.lucko.spark.common.sampler.ThreadDumper; import me.lucko.spark.common.sampler.TickCounter; -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.serializer.ComponentSerializers; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.optional.qual.MaybePresent; import java.nio.file.Path; -import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; @Plugin( id = "spark", @@ -53,9 +51,9 @@ import java.util.Set; description = "@desc@", authors = {"Luck", "sk89q"} ) -public class SparkVelocityPlugin implements SparkPlugin<CommandSource>, Command { +public class SparkVelocityPlugin implements SparkPlugin, Command { - private final SparkPlatform<CommandSource> platform = new SparkPlatform<>(this); + private final SparkPlatform platform = new SparkPlatform(this); private final ProxyServer proxy; private final Path configDirectory; @@ -79,13 +77,12 @@ public class SparkVelocityPlugin implements SparkPlugin<CommandSource>, Command @Override public void execute(@MaybePresent CommandSource sender, @NonNull @MaybePresent String[] args) { - if (!sender.hasPermission("spark")) { - TextComponent msg = TextComponent.builder("You do not have permission to use this command.").color(TextColor.RED).build(); - sender.sendMessage(msg); - return; - } + this.platform.executeCommand(new VelocityCommandSender(sender), args); + } - this.platform.executeCommand(sender, args); + @Override + public @MaybePresent List<String> suggest(@MaybePresent CommandSource sender, @NonNull @MaybePresent String[] currentArgs) { + return this.platform.tabCompleteCommand(new VelocityCommandSender(sender), currentArgs); } @Override @@ -104,16 +101,11 @@ public class SparkVelocityPlugin implements SparkPlugin<CommandSource>, Command } @Override - public Set<CommandSource> getSendersWithPermission(String permission) { - Set<CommandSource> senders = new HashSet<>(this.proxy.getAllPlayers()); + public Set<CommandSender> getSendersWithPermission(String permission) { + List<CommandSource> senders = new LinkedList<>(this.proxy.getAllPlayers()); senders.removeIf(sender -> !sender.hasPermission(permission)); senders.add(this.proxy.getConsoleCommandSource()); - return senders; - } - - @Override - public void sendMessage(CommandSource sender, Component message) { - sender.sendMessage(message); + return senders.stream().map(VelocityCommandSender::new).collect(Collectors.toSet()); } @Override diff --git a/spark-velocity/src/main/java/me/lucko/spark/velocity/VelocityCommandSender.java b/spark-velocity/src/main/java/me/lucko/spark/velocity/VelocityCommandSender.java new file mode 100644 index 0000000..1cad493 --- /dev/null +++ b/spark-velocity/src/main/java/me/lucko/spark/velocity/VelocityCommandSender.java @@ -0,0 +1,69 @@ +/* + * 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.velocity; + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.ConsoleCommandSource; +import com.velocitypowered.api.proxy.Player; +import me.lucko.spark.common.CommandSender; +import net.kyori.text.Component; + +public class VelocityCommandSender implements CommandSender { + private final CommandSource source; + + public VelocityCommandSender(CommandSource source) { + this.source = source; + } + + @Override + public String getName() { + if (this.source instanceof Player) { + return ((Player) this.source).getUsername(); + } else if (this.source instanceof ConsoleCommandSource) { + return "Console"; + } else { + return "unknown:" + this.source.getClass().getSimpleName(); + } + } + + @Override + public void sendMessage(Component message) { + this.source.sendMessage(message); + } + + @Override + public boolean hasPermission(String permission) { + return this.source.hasPermission(permission); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + VelocityCommandSender that = (VelocityCommandSender) o; + return this.source.equals(that.source); + } + + @Override + public int hashCode() { + return this.source.hashCode(); + } +} |