diff --git a/src/main/java/de/shiewk/smoderation/SModeration.java b/src/main/java/de/shiewk/smoderation/SModeration.java index 0c1c21b..2d9d968 100644 --- a/src/main/java/de/shiewk/smoderation/SModeration.java +++ b/src/main/java/de/shiewk/smoderation/SModeration.java @@ -1,5 +1,7 @@ package de.shiewk.smoderation; +import de.shiewk.smoderation.command.BanCommand; +import de.shiewk.smoderation.command.KickCommand; import de.shiewk.smoderation.command.MuteCommand; import de.shiewk.smoderation.listener.PunishmentListener; import de.shiewk.smoderation.storage.PunishmentContainer; @@ -26,6 +28,16 @@ public final class SModeration extends JavaPlugin { assert mute != null; mute.setExecutor(new MuteCommand()); mute.setTabCompleter(new MuteCommand()); + + final PluginCommand ban = getCommand("ban"); + assert ban != null; + ban.setExecutor(new BanCommand()); + ban.setTabCompleter(new BanCommand()); + + final PluginCommand kick = getCommand("kick"); + assert kick != null; + kick.setExecutor(new KickCommand()); + kick.setTabCompleter(new KickCommand()); } @Override diff --git a/src/main/java/de/shiewk/smoderation/command/BanCommand.java b/src/main/java/de/shiewk/smoderation/command/BanCommand.java new file mode 100644 index 0000000..f3800cc --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/command/BanCommand.java @@ -0,0 +1,113 @@ +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 BanCommand 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 ban 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; + int p = 1; + 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){ + p = i; + break; + } else { + duration += parsedDuration; + } + if (i == args.length - 1){ p = args.length; } + } + if (duration == 0){ + sender.sendMessage(Component.text("Please provide a valid duration.").color(NamedTextColor.RED)); + return false; + } + StringBuilder reason = new StringBuilder(); + for (int i = p; i < args.length; i++) { + if (!reason.isEmpty()){ + reason.append(" "); + } + reason.append(args[i]); + } + final Punishment punishment = Punishment.ban(System.currentTimeMillis(), System.currentTimeMillis() + duration, senderUUID, uuid, reason.isEmpty() ? Punishment.DEFAULT_REASON : reason.toString()); + 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){ + try { + Long.parseLong(args[i]); + } catch (NumberFormatException ignored){ + if (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/command/KickCommand.java b/src/main/java/de/shiewk/smoderation/command/KickCommand.java new file mode 100644 index 0000000..d3e74ad --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/command/KickCommand.java @@ -0,0 +1,75 @@ +package de.shiewk.smoderation.command; + +import de.shiewk.smoderation.SModeration; +import de.shiewk.smoderation.punishments.Punishment; +import de.shiewk.smoderation.util.PlayerUtil; +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 KickCommand implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (args.length < 1){ + 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]; + Player player = Bukkit.getPlayer(playerName); + if (player == null) { + sender.sendMessage(Component.text("This player is not online.").color(NamedTextColor.RED)); + return true; + } + UUID uuid = player.getUniqueId(); + if (senderUUID.equals(uuid)) { + sender.sendMessage(Component.text("You can't kick yourself.").color(NamedTextColor.RED)); + return true; + } + StringBuilder reason = new StringBuilder(); + for (int i = 1; i < args.length; i++) { + if (!reason.isEmpty()){ + reason.append(" "); + } + reason.append(args[i]); + } + final Punishment punishment = Punishment.kick(System.currentTimeMillis(), senderUUID, uuid, reason.isEmpty() ? Punishment.DEFAULT_REASON : reason.toString()); + 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; + } + return List.of(); + } +} diff --git a/src/main/java/de/shiewk/smoderation/command/MuteCommand.java b/src/main/java/de/shiewk/smoderation/command/MuteCommand.java index 8184e18..41f449d 100644 --- a/src/main/java/de/shiewk/smoderation/command/MuteCommand.java +++ b/src/main/java/de/shiewk/smoderation/command/MuteCommand.java @@ -56,6 +56,7 @@ public class MuteCommand implements CommandExecutor, TabCompleter { } else { duration += parsedDuration; } + if (i == args.length - 1){ p = args.length; } } if (duration == 0){ sender.sendMessage(Component.text("Please provide a valid duration.").color(NamedTextColor.RED)); diff --git a/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java b/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java index a4c2f62..8ff9af1 100644 --- a/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java +++ b/src/main/java/de/shiewk/smoderation/util/PlayerUtil.java @@ -15,14 +15,14 @@ public abstract class PlayerUtil { public static @NotNull String offlinePlayerName(UUID uuid){ if (uuid.equals(UUID_CONSOLE)){ - return "UUID_CONSOLE"; + return "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) + 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 { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c304a5f..bdcdc9d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,26 +5,26 @@ api-version: '1.20' load: STARTUP commands: mute: - usage: "§cUsage: /mute " + usage: "§cUsage: /mute " aliases: - smodmute permission: smod.mute description: Mutes a player, either temporarily or permanently. ban: - usage: "§cUsage: /ban " + usage: "§cUsage: /ban " aliases: - smodban - tempban permission: smod.ban description: Bans a player, either temporarily or permanently. kick: - usage: "§cUsage: /kick " + usage: "§cUsage: /kick " aliases: - smodkick permission: smod.kick description: Kicks a player smod: - usage: /smod + usage: "§cUsage: /smod" aliases: - smodmenu - smoderation