diff --git a/src/main/java/de/shiewk/smoderation/SModeration.java b/src/main/java/de/shiewk/smoderation/SModeration.java index 2e01c60..3811db2 100644 --- a/src/main/java/de/shiewk/smoderation/SModeration.java +++ b/src/main/java/de/shiewk/smoderation/SModeration.java @@ -3,6 +3,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.command.SModCommand; import de.shiewk.smoderation.event.CustomInventoryEvents; import de.shiewk.smoderation.listener.PunishmentListener; import de.shiewk.smoderation.storage.PunishmentContainer; @@ -40,6 +41,11 @@ public final class SModeration extends JavaPlugin { assert kick != null; kick.setExecutor(new KickCommand()); kick.setTabCompleter(new KickCommand()); + + final PluginCommand smod = getCommand("smod"); + assert smod != null; + smod.setExecutor(new SModCommand()); + smod.setTabCompleter(new SModCommand()); } @Override diff --git a/src/main/java/de/shiewk/smoderation/command/SModCommand.java b/src/main/java/de/shiewk/smoderation/command/SModCommand.java new file mode 100644 index 0000000..d2ab3ce --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/command/SModCommand.java @@ -0,0 +1,28 @@ +package de.shiewk.smoderation.command; + +import de.shiewk.smoderation.SModeration; +import de.shiewk.smoderation.inventory.SModMenu; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class SModCommand implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { + if (commandSender instanceof Player player){ + new SModMenu(player, SModeration.container).open(); + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { + return List.of(); + } +} diff --git a/src/main/java/de/shiewk/smoderation/inventory/SModMenu.java b/src/main/java/de/shiewk/smoderation/inventory/SModMenu.java new file mode 100644 index 0000000..f0948d8 --- /dev/null +++ b/src/main/java/de/shiewk/smoderation/inventory/SModMenu.java @@ -0,0 +1,103 @@ +package de.shiewk.smoderation.inventory; + +import de.shiewk.smoderation.punishments.Punishment; +import de.shiewk.smoderation.punishments.PunishmentType; +import de.shiewk.smoderation.storage.PunishmentContainer; +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 net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; + +public class SModMenu extends PageableCustomInventory { + + private final Inventory inventory; + private final Player player; + private final ArrayList punishments; + + public SModMenu(Player player, PunishmentContainer container) { + this.player = player; + this.inventory = Bukkit.createInventory(this, 54, Component.text("SMod Menu")); + punishments = container.copy(); + } + + @Override + public int lastPage() { + return Math.max((punishments.size() - 1) / 45, 0); + } + + @Override + public void switchPage() { + player.playSound(player, Sound.BLOCK_STONE_HIT, 0.75f, 1f); + } + + private ItemStack createPunishmentItem(Punishment punishment){ + final NamedTextColor PRIMARY_COLOR = NamedTextColor.AQUA; + final NamedTextColor SECONDARY_COLOR = NamedTextColor.GREEN; + ItemStack stack = new ItemStack(Material.PLAYER_HEAD); + stack.editMeta(meta -> { + if (meta instanceof SkullMeta skullMeta){ + skullMeta.setOwningPlayer(Bukkit.getOfflinePlayer(punishment.to)); + } + meta.displayName(applyFormatting(Component.text(punishment.type.name).color(NamedTextColor.RED).decorate(TextDecoration.BOLD))); + ArrayList lore = new ArrayList<>(); + lore.add(applyFormatting(Component.text("Player: ").color(SECONDARY_COLOR).append(Component.text(PlayerUtil.offlinePlayerName(punishment.to)).color(PRIMARY_COLOR)))); + lore.add(applyFormatting(Component.text("Punished by: ").color(SECONDARY_COLOR).append(Component.text(PlayerUtil.offlinePlayerName(punishment.by)).color(PRIMARY_COLOR)))); + lore.add(applyFormatting(Component.text("Timestamp: ").color(SECONDARY_COLOR).append(Component.text(TimeUtil.calendarTimestamp(punishment.time)).color(PRIMARY_COLOR)))); + if (punishment.type != PunishmentType.KICK){ + lore.add(applyFormatting(Component.text("Duration: ").color(SECONDARY_COLOR).append(Component.text(TimeUtil.formatTimeLong(punishment.until - punishment.time)).color(PRIMARY_COLOR)))); + long remainingTime = punishment.until - System.currentTimeMillis(); + final String expires; + if (remainingTime > 0){ + expires = "in " + TimeUtil.formatTimeLong(remainingTime); + } else { + remainingTime *= -1; + expires = TimeUtil.formatTimeLong(remainingTime) + " ago"; + } + lore.add(applyFormatting(Component.text("Expires: ").color(SECONDARY_COLOR).append(Component.text(expires).color(PRIMARY_COLOR)))); + } + lore.add(applyFormatting(Component.text("Reason: ").color(SECONDARY_COLOR).append(Component.text(punishment.reason).color(PRIMARY_COLOR)))); + meta.lore(lore); + }); + return stack; + } + + @Override + public void refresh() { + for (int i = 45; i < 54; i++) { + inventory.setItem(i, createEmptyStack()); + } + inventory.setItem(45, createPreviousPageStack()); + inventory.setItem(53, createNextPageStack()); + + for (int i = 0; i < 45; i++) { + int ci = i + (getPage() * 45); + if (punishments.size() > ci){ + inventory.setItem(i, createPunishmentItem(punishments.get(ci))); + } else { + inventory.setItem(i, new ItemStack(Material.AIR)); + } + } + } + + @Override + public void open() { + refresh(); + player.openInventory(this.inventory); + } + + @Override + public @NotNull Inventory getInventory() { + return inventory; + } +} diff --git a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java index 31fd43b..0ca1f2c 100644 --- a/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java +++ b/src/main/java/de/shiewk/smoderation/listener/PunishmentListener.java @@ -46,6 +46,7 @@ public class PunishmentListener implements Listener { final Punishment duplicate = container.find(p -> p.to.equals(punishment.to) && p.type == punishment.type && p.until >= punishment.time); if (duplicate != null){ container.remove(duplicate); + container.add(new Punishment(duplicate.type, duplicate.time, System.currentTimeMillis(), duplicate.by, duplicate.to, duplicate.reason)); } switch (punishment.type){ case KICK, BAN -> { diff --git a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java index 263a17e..9516c85 100644 --- a/src/main/java/de/shiewk/smoderation/punishments/Punishment.java +++ b/src/main/java/de/shiewk/smoderation/punishments/Punishment.java @@ -26,7 +26,7 @@ public class Punishment { public final UUID to; public final String reason; - protected Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason) { + public Punishment(PunishmentType type, long time, long until, UUID by, UUID to, String reason) { this.type = type; this.time = time; this.until = until; diff --git a/src/main/java/de/shiewk/smoderation/punishments/PunishmentType.java b/src/main/java/de/shiewk/smoderation/punishments/PunishmentType.java index 3197474..cc48074 100644 --- a/src/main/java/de/shiewk/smoderation/punishments/PunishmentType.java +++ b/src/main/java/de/shiewk/smoderation/punishments/PunishmentType.java @@ -1,7 +1,13 @@ package de.shiewk.smoderation.punishments; public enum PunishmentType { - MUTE, - KICK, - BAN + MUTE("Mute"), + KICK("Kick"), + BAN("Ban"); + + public final String name; + + PunishmentType(String name) { + this.name = name; + } } diff --git a/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java b/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java index d8a7748..9147810 100644 --- a/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java +++ b/src/main/java/de/shiewk/smoderation/storage/PunishmentContainer.java @@ -53,4 +53,8 @@ public class PunishmentContainer { public @Nullable Punishment findByTimestamp(long timestamp){ return find(punishment -> punishment.time == timestamp); } + + public ArrayList copy() { + return new ArrayList<>(punishments); + } } diff --git a/src/main/java/de/shiewk/smoderation/util/TimeUtil.java b/src/main/java/de/shiewk/smoderation/util/TimeUtil.java index c7fb06a..c40a29b 100644 --- a/src/main/java/de/shiewk/smoderation/util/TimeUtil.java +++ b/src/main/java/de/shiewk/smoderation/util/TimeUtil.java @@ -1,5 +1,11 @@ package de.shiewk.smoderation.util; +import org.jetbrains.annotations.Range; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + public abstract class TimeUtil { private TimeUtil(){} @@ -112,4 +118,84 @@ public abstract class TimeUtil { return -1; } } + + public static String calendarTimestamp(long time){ + Date date = new Date(time); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + TimeZone zone = calendar.getTimeZone(); + int second = calendar.get(Calendar.SECOND); + int minute = calendar.get(Calendar.MINUTE); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + int year = calendar.get(Calendar.YEAR); + + String day = numberWithSuffix(calendar.get(Calendar.DAY_OF_MONTH)); + String month = monthName(calendar.get(Calendar.MONTH)); + return "%s %s %s, %s:%s:%s %s".formatted( + month, + day, + year, + hour < 10 ? "0" + hour : hour, + minute < 10 ? "0" + minute : minute, + second < 10 ? "0" + second : second, + zone.getDisplayName(false, TimeZone.SHORT) + ); + } + + private static String numberWithSuffix(int i){ + return i + numberSuffix(i); + } + + private static String numberSuffix(int i){ + if ((i % 10) == 1 && i != 11){ + return "st"; + } else if ((i % 10) == 2 && i != 12){ + return "nd"; + } else if ((i % 10) == 3 && i != 13){ + return "rd"; + } + return "th"; + } + + public static String monthName(@Range(from = 0, to = 11) int m){ + switch (m){ + case 0 -> { + return "January"; + } + case 1 -> { + return "February"; + } + case 2 -> { + return "March"; + } + case 3 -> { + return "April"; + } + case 4 -> { + return "May"; + } + case 5 -> { + return "June"; + } + case 6 -> { + return "July"; + } + case 7 -> { + return "August"; + } + case 8 -> { + return "September"; + } + case 9 -> { + return "October"; + } + case 10 -> { + return "November"; + } + case 11 -> { + return "December"; + } + } + return "Unknown Month"; + } }