From fe4e87d9b42e9482920dc9f13eaec5146cbd001c Mon Sep 17 00:00:00 2001 From: Shiewk Date: Sat, 8 Jun 2024 11:03:56 +0200 Subject: [PATCH] Add mute command, command usages and smaller improvements --- .../de/shiewk/smoderation/SModeration.java | 7 ++ .../smoderation/command/MuteCommand.java | 97 +++++++++++++++++++ .../event/PunishmentIssueEvent.java | 14 ++- .../listener/PunishmentListener.java | 2 +- .../smoderation/punishments/Punishment.java | 7 +- .../shiewk/smoderation/util/PlayerUtil.java | 18 +++- .../de/shiewk/smoderation/util/TimeUtil.java | 30 ++++++ src/main/resources/plugin.yml | 4 + 8 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/shiewk/smoderation/command/MuteCommand.java diff --git a/src/main/java/de/shiewk/smoderation/SModeration.java b/src/main/java/de/shiewk/smoderation/SModeration.java index ff79d1d..0c1c21b 100644 --- a/src/main/java/de/shiewk/smoderation/SModeration.java +++ b/src/main/java/de/shiewk/smoderation/SModeration.java @@ -1,7 +1,9 @@ package de.shiewk.smoderation; +import de.shiewk.smoderation.command.MuteCommand; import de.shiewk.smoderation.listener.PunishmentListener; import de.shiewk.smoderation.storage.PunishmentContainer; +import org.bukkit.command.PluginCommand; import org.bukkit.plugin.java.JavaPlugin; import static org.bukkit.Bukkit.getPluginManager; @@ -19,6 +21,11 @@ public final class SModeration extends JavaPlugin { @Override public void onEnable() { getPluginManager().registerEvents(new PunishmentListener(), this); + + final PluginCommand mute = getCommand("mute"); + assert mute != null; + mute.setExecutor(new MuteCommand()); + mute.setTabCompleter(new MuteCommand()); } @Override diff --git a/src/main/java/de/shiewk/smoderation/command/MuteCommand.java b/src/main/java/de/shiewk/smoderation/command/MuteCommand.java new file mode 100644 index 0000000..9a8644a --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/command/MuteCommand.java @@ -0,0 +1,97 @@ +package de.shiewk.smoderation.command; + +import de.shiewk.smoderation.SModeration; +import de.shiewk.smoderation.punishments.Punishment; +import de.shiewk.smoderation.util.PlayerUtil; +import de.shiewk.smoderation.util.TimeUtil; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.command.*; +import org.bukkit.entity.Player; +import org.bukkit.util.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class MuteCommand implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 2){ + return false; + } else { + UUID senderUUID; + if (sender instanceof ConsoleCommandSender){ + senderUUID = PlayerUtil.UUID_CONSOLE; + } else if (sender instanceof Player pl){ + senderUUID = pl.getUniqueId(); + } else if (sender instanceof BlockCommandSender){ + sender.sendMessage(Component.text("Blocks can't execute this command.").color(NamedTextColor.RED)); + return true; + } else { + sender.sendMessage(Component.text("Your command sender type is unknown (%s).".formatted(sender.getClass().getName())).color(NamedTextColor.RED)); + return true; + } + String playerName = args[0]; + UUID uuid = PlayerUtil.offlinePlayerUUIDByName(playerName); + if (senderUUID.equals(uuid)) { + sender.sendMessage(Component.text("You can't mute yourself.").color(NamedTextColor.RED)); + return true; + } + if (uuid == null) { + sender.sendMessage(Component.text("This player is either offline or was never on this server.").color(NamedTextColor.RED)); + return true; + } + long duration = 0; + for (int i = 1 /* start with index 1 to avoid player name */; i < args.length; i++) { + String arg = args[i]; + long parsedDuration = TimeUtil.parseDurationMillisSafely(arg); + if (parsedDuration == -1){ + break; + } else { + duration += parsedDuration; + } + } + if (duration == 0){ + sender.sendMessage(Component.text("Please provide a valid duration.").color(NamedTextColor.RED)); + return false; + } + final Punishment punishment = Punishment.mute(System.currentTimeMillis(), System.currentTimeMillis() + duration, senderUUID, uuid); + Punishment.issue(punishment, SModeration.container); + return true; + } + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 2){ + String toComplete = args.length > 0 ? args[0] : ""; + ArrayList names = new ArrayList<>(); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + names.add(onlinePlayer.getName()); + } + ArrayList completions = new ArrayList<>(); + StringUtil.copyPartialMatches(toComplete, names, completions); + return completions; + } else { + for (int i = 1; i < args.length; i++) { + if (TimeUtil.parseDurationMillisSafely(args[i]) == -1){ + return List.of(); + } + } + return List.of( + "100ms", + "15s", // some sample completions for duration + "30min", // you can input your own ones as well + "6h", + "1d", + "2w", + "3mo", + "1y" + ); + } + } +} diff --git a/src/main/java/de/shiewk/smoderation/event/PunishmentIssueEvent.java b/src/main/java/de/shiewk/smoderation/event/PunishmentIssueEvent.java index b5a249e..53112ab 100644 --- a/src/main/java/de/shiewk/smoderation/event/PunishmentIssueEvent.java +++ b/src/main/java/de/shiewk/smoderation/event/PunishmentIssueEvent.java @@ -2,15 +2,17 @@ package de.shiewk.smoderation.event; import de.shiewk.smoderation.punishments.Punishment; import de.shiewk.smoderation.storage.PunishmentContainer; +import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; -public class PunishmentIssueEvent extends Event { +public class PunishmentIssueEvent extends Event implements Cancellable { private static final HandlerList handlerList = new HandlerList(); private final Punishment punishment; private final PunishmentContainer container; + private boolean cancelled; public PunishmentIssueEvent(Punishment punishment, PunishmentContainer container) { this.punishment = punishment; @@ -33,4 +35,14 @@ public class PunishmentIssueEvent extends Event { public static HandlerList getHandlerList() { return handlerList; } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } } diff --git a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java index b2fdc8f..fb5c949 100644 --- a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java +++ b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java @@ -38,7 +38,7 @@ public class PunishmentListener implements Listener { } } - @EventHandler(priority = EventPriority.LOW) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onPunishmentIssue(PunishmentIssueEvent event){ final Punishment punishment = event.getPunishment(); switch (punishment.type){ diff --git a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java index 1971ae3..f9c7ea8 100644 --- a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java +++ b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java @@ -87,8 +87,11 @@ public class Punishment { } public static void issue(Punishment punishment, PunishmentContainer container){ - container.add(punishment); - Bukkit.getPluginManager().callEvent(new PunishmentIssueEvent(punishment, container)); + final PunishmentIssueEvent event = new PunishmentIssueEvent(punishment, container); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()){ + container.add(punishment); + } } public Component playerMessage(){ diff --git a/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java b/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java index 43bb84a..8888e9f 100644 --- a/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java +++ b/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java @@ -2,15 +2,31 @@ package de.shiewk.smoderation.util; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.UUID; public abstract class PlayerUtil { private PlayerUtil(){} - public static String offlinePlayerName(UUID uuid){ + public static final UUID UUID_CONSOLE = new UUID(0, 0); + + public static @NotNull String offlinePlayerName(UUID uuid){ + if (uuid.equals(UUID_CONSOLE)){ + return "UUID_CONSOLE"; + } OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); return player.getName() == null ? uuid.toString() : player.getName(); } + public static @Nullable UUID offlinePlayerUUIDByName(String name){ + final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayerIfCached(name);// getOfflinePlayerIfCached(String) is safer (I have experience with getOfflinePlayer(String) returning wrong UUIDs) + if (offlinePlayer != null) { + return offlinePlayer.getUniqueId(); + } else { + return null; + } + } + } diff --git a/src/main/java/de/shiewk/smoderation/util/TimeUtil.java b/src/main/java/de/shiewk/smoderation/util/TimeUtil.java index 71cd116..9031baa 100644 --- a/src/main/java/de/shiewk/smoderation/util/TimeUtil.java +++ b/src/main/java/de/shiewk/smoderation/util/TimeUtil.java @@ -77,4 +77,34 @@ public abstract class TimeUtil { return builder.toString(); } + + public static long parseDurationMillisSafely(String in){ + try { + return parseDurationMillis(in); + } catch (Throwable e){ + return -1; + } + } + + public static long parseDurationMillis(String in){ + if (in.endsWith("ms")){ + return Long.parseLong(in.substring(0, in.length()-2)); + } else if (in.endsWith("s")){ + return Long.parseLong(in.substring(0, in.length()-1)) * 1000L; + } else if (in.endsWith("min")){ + return Long.parseLong(in.substring(0, in.length()-3)) * 60000L; + } else if (in.endsWith("h")){ + return Long.parseLong(in.substring(0, in.length()-1)) * 3600000L; + } else if (in.endsWith("d")){ + return Long.parseLong(in.substring(0, in.length()-1)) * 86400000L; + } else if (in.endsWith("w")){ + return Long.parseLong(in.substring(0, in.length()-1)) * 604800000L; + } else if (in.endsWith("mo")){ + return Long.parseLong(in.substring(0, in.length()-2)) * 2592000000L; + } else if (in.endsWith("y")){ + return Long.parseLong(in.substring(0, in.length()-1)) * 31536000000L; + } else { + return -1; + } + } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index b93a094..415887f 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,22 +5,26 @@ api-version: '1.20' load: STARTUP commands: mute: + usage: /mute aliases: - smodmute permission: smod.mute description: Mutes a player, either temporarily or permanently. ban: + usage: /ban aliases: - smodban - tempban permission: smod.ban description: Bans a player, either temporarily or permanently. kick: + usage: /kick aliases: - smodkick permission: smod.kick description: Kicks a player smod: + usage: /smod aliases: - smodmenu - smoderation