diff --git a/src/main/java/fr/communaywen/core/AywenCraftPlugin.java b/src/main/java/fr/communaywen/core/AywenCraftPlugin.java index 0bc41da0..2f33c070 100644 --- a/src/main/java/fr/communaywen/core/AywenCraftPlugin.java +++ b/src/main/java/fr/communaywen/core/AywenCraftPlugin.java @@ -7,6 +7,7 @@ import fr.communaywen.core.claim.ClaimListener; import fr.communaywen.core.claim.GamePlayer; import fr.communaywen.core.claim.RegionManager; +import fr.communaywen.core.clockinfos.tasks.CompassClockTask; import fr.communaywen.core.commands.credits.CreditCommand; import fr.communaywen.core.commands.credits.FeatureCommand; import fr.communaywen.core.commands.economy.AdminShopCommand; @@ -16,26 +17,27 @@ import fr.communaywen.core.commands.explosion.ExplodeRandomCommand; import fr.communaywen.core.commands.explosion.FBoomCommand; import fr.communaywen.core.commands.fun.*; -import fr.communaywen.core.commands.staff.ReportCommands; -import fr.communaywen.core.commands.utils.*; -import fr.communaywen.core.commands.teleport.RTPCommand; -import fr.communaywen.core.commands.teleport.SpawnCommand; import fr.communaywen.core.commands.link.LinkCommand; import fr.communaywen.core.commands.link.ManualLinkCommand; import fr.communaywen.core.commands.socials.DiscordCommand; import fr.communaywen.core.commands.socials.GithubCommand; +import fr.communaywen.core.commands.staff.ReportCommands; import fr.communaywen.core.commands.teams.TeamAdminCommand; import fr.communaywen.core.commands.teams.TeamCommand; +import fr.communaywen.core.commands.teleport.RTPCommand; +import fr.communaywen.core.commands.teleport.SpawnCommand; +import fr.communaywen.core.commands.utils.*; import fr.communaywen.core.customitems.commands.ShowCraftCommand; import fr.communaywen.core.customitems.listeners.CIBreakBlockListener; import fr.communaywen.core.customitems.listeners.CIEnchantListener; import fr.communaywen.core.customitems.listeners.CIPrepareAnvilListener; import fr.communaywen.core.fallblood.BandageRecipe; -import fr.communaywen.core.clockinfos.tasks.CompassClockTask; import fr.communaywen.core.friends.commands.FriendsCommand; import fr.communaywen.core.levels.LevelsCommand; import fr.communaywen.core.levels.LevelsListeners; import fr.communaywen.core.listeners.*; +import fr.communaywen.core.mailboxes.MailboxCommand; +import fr.communaywen.core.mailboxes.MailboxListener; import fr.communaywen.core.quests.PlayerQuests; import fr.communaywen.core.quests.QuestsListener; import fr.communaywen.core.quests.QuestsManager; @@ -50,7 +52,10 @@ import fr.communaywen.core.trade.TradeAcceptCommand; import fr.communaywen.core.trade.TradeCommand; import fr.communaywen.core.trade.TradeListener; -import fr.communaywen.core.utils.*; +import fr.communaywen.core.utils.DiscordWebhook; +import fr.communaywen.core.utils.LinkerAPI; +import fr.communaywen.core.utils.MOTDChanger; +import fr.communaywen.core.utils.PermissionCategory; import fr.communaywen.core.utils.command.InteractiveHelpMenu; import lombok.Getter; import lombok.SneakyThrows; @@ -59,6 +64,8 @@ import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.inventory.Inventory; @@ -68,46 +75,50 @@ import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; import revxrsal.commands.autocomplete.SuggestionProvider; import revxrsal.commands.bukkit.BukkitCommandHandler; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - +import java.io.File; import java.util.ArrayList; import java.util.List; -import java.io.File; public final class AywenCraftPlugin extends JavaPlugin { public static ArrayList frozenPlayers = new ArrayList<>(); public static ArrayList playerClaimsByPass = new ArrayList<>(); - + @Getter + private static AywenCraftPlugin instance; @Getter private final Managers managers = new Managers(); - public EventsManager eventsManager; // TODO: include to Managers.java - - @Getter - private static AywenCraftPlugin instance; public LuckPerms api; - + public List regions; + public MultiverseCore mvCore; @Getter private BukkitAudiences adventure; @Getter private InteractiveHelpMenu interactiveHelpMenu; - @Getter private BukkitCommandHandler handler; - @Getter private TabList tabList; - public List regions; - public MultiverseCore mvCore; + /** + * Format a permission with the permission prefix. + * + * @param category the permission category + * @param suffix the permission suffix + * @return The formatted permission. + * @see PermissionCategory #PERMISSION_PREFIX + */ + public static @NotNull String formatPermission(final @NotNull PermissionCategory category, + final @NotNull String suffix) { + return category.formatPermission(suffix); + } + @SneakyThrows @Override public void onEnable() { @@ -120,8 +131,7 @@ public void onEnable() { RegisteredServiceProvider provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class); if (provider != null) { api = provider.getProvider(); - } - else { + } else { getLogger().severe("LuckPerms not found !"); getServer().getPluginManager().disablePlugin(this); return; @@ -204,7 +214,8 @@ public void onEnable() { new DiscordCommand(this), new ShowCraftCommand(managers.getCustomItemsManager()), new ReportCommands(), - new ChatChannelCMD() + new ChatChannelCMD(), + new MailboxCommand() ); /* -------- */ @@ -251,11 +262,12 @@ public void run() { new CIBreakBlockListener(managers.getCustomItemsManager()), new CIEnchantListener(managers.getCustomItemsManager()), new CIPrepareAnvilListener(managers.getCustomItemsManager()), - new BabyFuzeListener() + new BabyFuzeListener(), + new MailboxListener() ); getServer().getPluginManager().registerEvents(eventsManager, this); // TODO: refactor - + /* --------- */ saveDefaultConfig(); @@ -277,8 +289,8 @@ public void run() { @SneakyThrows @Override public void onDisable() { - for(Player player : Bukkit.getOnlinePlayers()) { - for(QUESTS quests : QUESTS.values()) { + for (Player player : Bukkit.getOnlinePlayers()) { + for (QUESTS quests : QUESTS.values()) { PlayerQuests pq = QuestsManager.getPlayerQuests(player); // Load quest progress QuestsManager.savePlayerQuestProgress(player, quests, pq.getProgress(quests)); // Save quest progress player.closeInventory(); // Close inventory @@ -297,13 +309,13 @@ public ArrayList getFrozenPlayers() { return frozenPlayers; } + + // Farine pour fabriquer du pain + public int getBanDuration() { return getConfig().getInt("deco_freeze_nombre_de_jours_ban", 30); } - - // Farine pour fabriquer du pain - private void createFarineRecipe() { ItemStack farine = new ItemStack(Material.SUGAR); ItemMeta meta = farine.getItemMeta(); @@ -319,7 +331,7 @@ private void createFarineRecipe() { Bukkit.addRecipe(recipe); } - private void createCrazyPotion(){ + private void createCrazyPotion() { ItemStack crazyPotion = new ItemStack(Material.POTION); PotionMeta meta = (PotionMeta) crazyPotion.getItemMeta(); @@ -370,17 +382,4 @@ private FileConfiguration loadEventsManager() { } return YamlConfiguration.loadConfiguration(eventsFile); } - - /** - * Format a permission with the permission prefix. - * - * @param category the permission category - * @param suffix the permission suffix - * @return The formatted permission. - * @see PermissionCategory #PERMISSION_PREFIX - */ - public static @NotNull String formatPermission(final @NotNull PermissionCategory category, - final @NotNull String suffix) { - return category.formatPermission(suffix); - } } diff --git a/src/main/java/fr/communaywen/core/mailboxes/MailboxCommand.java b/src/main/java/fr/communaywen/core/mailboxes/MailboxCommand.java new file mode 100644 index 00000000..60b7e644 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/MailboxCommand.java @@ -0,0 +1,91 @@ +package fr.communaywen.core.mailboxes; + +import fr.communaywen.core.credit.Credit; +import fr.communaywen.core.mailboxes.letter.LetterHead; +import fr.communaywen.core.mailboxes.menu.HomeMailbox; +import fr.communaywen.core.mailboxes.menu.PendingMailbox; +import fr.communaywen.core.mailboxes.menu.PlayerMailbox; +import fr.communaywen.core.mailboxes.menu.letter.Letter; +import fr.communaywen.core.mailboxes.menu.letter.SendingLetter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import revxrsal.commands.annotation.*; +import revxrsal.commands.autocomplete.SuggestionProvider; +import revxrsal.commands.bukkit.annotation.CommandPermission; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.sendFailureMessage; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.sendWarningMessage; + +@Command({"mailbox", "mb", "letter", "mail", "lettre", "boite", "courrier"}) +@CommandPermission("ayw.command.mailbox") +@Credit("Gexary") +public class MailboxCommand { + + @Subcommand("home") + @Description("Ouvrir la page d'accueil de la boite aux lettres") + public void homeMailbox(Player player) { + HomeMailbox homeMailbox = new HomeMailbox(player); + homeMailbox.openInventory(); + } + + @Subcommand("send") + @Description("Envoyer une lettre à un joueur") + @AutoComplete("@players") + public void sendMailbox(Player player, @Named("player") String receiver) { + OfflinePlayer receiverPlayer = Bukkit.getPlayerExact(receiver); + if (receiverPlayer == null) receiverPlayer = Bukkit.getOfflinePlayerIfCached(receiver); + if (receiverPlayer == null || !(receiverPlayer.hasPlayedBefore() || receiverPlayer.isOnline())) { + Component message = Component.text("Le joueur ", NamedTextColor.DARK_RED) + .append(Component.text(receiver, NamedTextColor.RED)) + .append(Component.text(" n'existe pas ou ne s'est jamais connecté !", NamedTextColor.DARK_RED)); + sendFailureMessage(player, message); + } else if (receiverPlayer.getPlayer() == player) { + sendWarningMessage(player, "Vous ne pouvez pas vous envoyer à vous-même !"); + } else if (MailboxManager.canSend(player, receiverPlayer)) { + SendingLetter sendingLetter = new SendingLetter(player, receiverPlayer); + sendingLetter.openInventory(); + } else { + sendFailureMessage(player, "Vous n'avez pas les droits pour envoyer à cette personne !"); + } + } + + @Subcommand("pending") + @Description("Ouvrir les lettres en attente de réception") + public void pendingMailbox(Player player) { + PendingMailbox pendingMailbox = new PendingMailbox(player); + pendingMailbox.openInventory(); + } + + @SecretCommand + @Subcommand("open") + @Description("Ouvrir une lettre") + public void openMailbox(Player player, @Named("id") @Range(min = 1, max = Integer.MAX_VALUE) int id) { + LetterHead letterHead = Letter.getById(player, id); + if (letterHead == null) return; + Letter mailbox = new Letter(player, letterHead); + mailbox.openInventory(); + } + + @Subcommand("refuse") + @SecretCommand + @Description("Refuser une lettre") + public void refuseMailbox(Player player, @Named("id") @Range(min = 1, max = Integer.MAX_VALUE) int id) { + Letter.refuseLetter(player, id); + } + + @Subcommand("cancel") + @SecretCommand + @Description("Annuler une lettre") + public void cancelMailbox(Player player, @Named("id") @Range(min = 1, max = Integer.MAX_VALUE) int id) { + PendingMailbox.cancelLetter(player, id); + } + + @DefaultFor("~") + public void mailbox(Player player) { + PlayerMailbox playerMailbox = new PlayerMailbox(player); + playerMailbox.openInventory(); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/MailboxListener.java b/src/main/java/fr/communaywen/core/mailboxes/MailboxListener.java new file mode 100644 index 00000000..25ae2c83 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/MailboxListener.java @@ -0,0 +1,242 @@ +package fr.communaywen.core.mailboxes; + +import fr.communaywen.core.AywenCraftPlugin; +import fr.communaywen.core.mailboxes.letter.LetterHead; +import fr.communaywen.core.mailboxes.menu.HomeMailbox; +import fr.communaywen.core.mailboxes.menu.PendingMailbox; +import fr.communaywen.core.mailboxes.menu.PlayerMailbox; +import fr.communaywen.core.mailboxes.menu.PlayersList; +import fr.communaywen.core.mailboxes.menu.letter.Letter; +import fr.communaywen.core.mailboxes.menu.letter.SendingLetter; +import fr.communaywen.core.mailboxes.utils.MailboxInv; +import fr.communaywen.core.mailboxes.utils.PaginatedMailbox; +import fr.communaywen.core.utils.database.DatabaseConnector; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Set; +import java.util.UUID; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.*; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.*; + +public class MailboxListener extends DatabaseConnector implements Listener { + private final AywenCraftPlugin plugin = AywenCraftPlugin.getInstance(); + + /* + public MailboxListener() { + final int DELAY = 1; // in minutes + BukkitRunnable runnable = new BukkitRunnable() { + int i = 0; + + @Override + public void run() { + showBossBar(i++); + } + }; + runnable.runTaskTimer(plugin, DELAY, DELAY * 60L * 20L); + } + + public void showBossBar(int i) { + BossBar bossBar = BossBar.bossBar(getBossBarTitle(i), 1, BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_10); + for (Player player : Bukkit.getOnlinePlayers()) { + player.showBossBar(bossBar); + } + new BukkitRunnable() { + int j = 1; + + @Override + public void run() { + bossBar.progress(1.0F - j++ * 0.1F); + if (j > 10) { + for (Player player : Bukkit.getOnlinePlayers()) { + player.hideBossBar(bossBar); + } + cancel(); + } + } + }.runTaskTimer(plugin, 10L, 10L); + } + + public Component getBossBarTitle(int i) { + return Component.text("Envoi de lettre " + i, NamedTextColor.GOLD); + } + */ + + @EventHandler + public void onInventoryOpen(InventoryCloseEvent event) { + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof MailboxInv mailboxInv) mailboxInv.addInventory(); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + InventoryHolder holder = event.getInventory().getHolder(); + if (holder instanceof SendingLetter sendingLetter) sendingLetter.giveItems(); + if (holder instanceof MailboxInv mailboxInv) mailboxInv.removeInventory(); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + try (PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) AS affected_rows, sender_id, items_count, id FROM openmc_db.mailbox_items WHERE receiver_id = ? AND refused = false;")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet result = statement.executeQuery()) { + if (result.next()) { + Component message = null; + int affectedRows = result.getInt("affected_rows"); + if (affectedRows > 1) { + message = Component.text("Vous avez reçu ", NamedTextColor.DARK_GREEN) + .append(Component.text(affectedRows, NamedTextColor.GREEN)) + .append(Component.text(" lettres.", NamedTextColor.DARK_GREEN)) + .append(Component.text("\nCliquez-ici", NamedTextColor.YELLOW)) + .clickEvent(ClickEvent.runCommand("/mailbox")) + .hoverEvent(getHoverEvent("Ouvrir ma boîte aux lettres")) + .append(Component.text(" pour ouvrir les lettres", NamedTextColor.GOLD)); + } else if (affectedRows == 1) { + int itemsCount = result.getInt("items_count"); + int letterId = result.getInt("id"); + UUID senderUUID = UUID.fromString(result.getString("sender_id")); + OfflinePlayer sender = Bukkit.getOfflinePlayer(senderUUID); + String senderName = sender.getName(); + message = Component.text("Vous avez reçu ", NamedTextColor.DARK_GREEN) + .append(Component.text(itemsCount, NamedTextColor.GREEN)) + .append(Component.text(" " + getItemCount(itemsCount) + " de la part de ", NamedTextColor.DARK_GREEN)) + .append(Component.text(senderName == null ? "Unknown" : senderName, NamedTextColor.GREEN)) + .append(Component.text("\nCliquez-ici", NamedTextColor.YELLOW)) + .clickEvent(getRunCommand("open " + letterId)) + .hoverEvent(getHoverEvent("Ouvrir la lettre #" + letterId)) + .append(Component.text(" pour ouvrir la lettre", NamedTextColor.GOLD)); + } + if (message != null) sendSuccessMessage(player, message); + } + } + } catch (Exception e) { + e.printStackTrace(); + sendFailureMessage(event.getPlayer(), "Une erreur est survenue."); + } + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + Inventory inv = event.getView().getTopInventory(); + InventoryHolder holder = inv.getHolder(); + Set slots = event.getRawSlots(); + + if (holder instanceof SendingLetter) { + for (int slot : slots) { + if (slot >= 54) continue; + int row = slot / 9; + if (row < 1 || row > 3) { + event.setCancelled(true); + return; + } + } + } else if (holder instanceof MailboxInv) { + for (int slot : slots) { + if (slot >= holder.getInventory().getSize()) continue; + event.setCancelled(true); + } + } + } + + private void runTask(Runnable runnable) { + plugin.getServer().getScheduler().runTask(plugin, runnable); + } + + @EventHandler + public void onClick(InventoryClickEvent event) { + Inventory inv = event.getView().getTopInventory(); + InventoryHolder holder = inv.getHolder(); + ItemStack item = event.getCurrentItem(); + Player player = (Player) event.getWhoClicked(); + int slot = event.getRawSlot(); + int row = slot / 9; + + if (slot >= inv.getSize()) { + if (event.isShiftClick()) { + if (holder instanceof SendingLetter sendingLetter) { + if (sendingLetter.noSpace(item)) event.setCancelled(true); + } else if (holder instanceof MailboxInv) event.setCancelled(true); + } + return; + } + + if (holder instanceof SendingLetter) { + if (row < 1 || row > 3) { + event.setCancelled(true); + } + } else if (holder instanceof MailboxInv) event.setCancelled(true); + + //! Buttons actions + if (cancelBtn(item) && holder instanceof MailboxInv) { + runTask(player::closeInventory); + return; + } else if (item != null && item.getType() == Material.CHEST && slot == 45) { + runTask(() -> HomeMailbox.openHomeMailbox(player)); + return; + } else if (holder instanceof PaginatedMailbox menu) { + if (nextPageBtn(item)) { + runTask(menu::nextPage); + return; + } else if (previousPageBtn(item)) { + runTask(menu::previousPage); + return; + } + } + + if (holder instanceof SendingLetter sendingLetter) { + if (sendBtn(item)) runTask(sendingLetter::sendLetter); + } else if (holder instanceof PlayerMailbox playerMailbox) { + if (item != null && item.getType() == Material.PLAYER_HEAD) { + LetterHead letterHead = playerMailbox.getByIndex(slot); + if (letterHead == null) return; + runTask(() -> letterHead.openLetter(player)); + } + } else if (holder instanceof Letter letter) { + if (acceptBtn(item)) { + runTask(letter::accept); + } else if (refuseBtn(item)) { + runTask(letter::refuse); + } + } else if (holder instanceof HomeMailbox) { + if (slot == 3) { + runTask(() -> HomeMailbox.openPendingMailbox(player)); + } else if (slot == 4) { + runTask(() -> HomeMailbox.openPlayerMailbox(player)); + } else if (slot == 5) { + runTask(() -> HomeMailbox.openPlayersList(player)); + } else if (slot == 8) { + runTask(() -> HomeMailbox.openSettings(player)); + } + } else if (holder instanceof PendingMailbox pendingMailbox) { + if (item != null && item.getType() == Material.PLAYER_HEAD) { + runTask(() -> pendingMailbox.clickLetter(slot)); + } + } else if (holder instanceof PlayersList) { + if (item != null && item.getType() == Material.PLAYER_HEAD) { + SkullMeta meta = (SkullMeta) item.getItemMeta(); + OfflinePlayer receiver = meta.getOwningPlayer(); + if (receiver == null) return; + runTask(() -> HomeMailbox.openSendingMailbox(player, receiver)); + } + } + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/MailboxManager.java b/src/main/java/fr/communaywen/core/mailboxes/MailboxManager.java new file mode 100644 index 00000000..04350d97 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/MailboxManager.java @@ -0,0 +1,122 @@ +package fr.communaywen.core.mailboxes; + +import fr.communaywen.core.AywenCraftPlugin; +import fr.communaywen.core.mailboxes.letter.LetterHead; +import fr.communaywen.core.mailboxes.menu.PlayerMailbox; +import fr.communaywen.core.mailboxes.menu.letter.Letter; +import fr.communaywen.core.mailboxes.utils.MailboxInv; +import fr.communaywen.core.mailboxes.utils.MailboxMenuManager; +import fr.communaywen.core.utils.database.DatabaseConnector; +import fr.communaywen.core.utils.serializer.BukkitSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.title.Title; +import org.bukkit.OfflinePlayer; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.HashMap; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.*; + +public class MailboxManager extends DatabaseConnector { + private static final AywenCraftPlugin plugin = AywenCraftPlugin.getInstance(); + + public static boolean sendItems(Player sender, OfflinePlayer receiver, ItemStack[] items) { + String receiverUUID = receiver.getUniqueId().toString(); + if (!canSend(sender, receiver)) return false; + String receiverName = receiver.getName(); + int itemsCount = Arrays.stream(items).mapToInt(ItemStack::getAmount).sum(); + String senderUUID = sender.getUniqueId().toString(); + + try (PreparedStatement statement = connection.prepareStatement("INSERT INTO `mailbox_items` (sender_id, receiver_id, items, items_count) VALUES (?, ?, ?, ?);", PreparedStatement.RETURN_GENERATED_KEYS)) { + byte[] itemsBytes = BukkitSerializer.serializeItemStacks(items); + statement.setString(1, senderUUID); + statement.setString(2, receiverUUID); + statement.setBytes(3, itemsBytes); + statement.setInt(4, itemsCount); + if (statement.executeUpdate() == 0) return false; + try (ResultSet result = statement.getGeneratedKeys()) { + if (result.next()) { + int id = result.getInt(1); + Player receiverPlayer = receiver.getPlayer(); + if (receiverPlayer != null) { + if (MailboxMenuManager.playerInventories.get(receiverPlayer) instanceof PlayerMailbox receiverMailbox) { + LetterHead letterHead = new LetterHead(sender, id, itemsCount, result.getTimestamp(1).toLocalDateTime()); + receiverMailbox.addLetter(letterHead); + } else sendNotification(receiverPlayer, itemsCount, id, sender.getName()); + } + sendSuccessSendingMessage(sender, receiverName, itemsCount); + return true; + } + return false; + } + } catch (Exception ex) { + ex.printStackTrace(); + sendFailureSendingMessage(sender, receiverName); + return false; + } + } + + // todo + public static boolean canSend(Player sender, OfflinePlayer receiver) { + return true; + } + + private static void sendNotification(Player receiver, int itemsCount, int id, String name) { + Component message = Component.text("Vous avez reçu ", NamedTextColor.DARK_GREEN) + .append(Component.text(itemsCount, NamedTextColor.GREEN)) + .append(Component.text(" item" + (itemsCount > 1 ? "s" : "") + " de la part de ", NamedTextColor.DARK_GREEN)) + .append(Component.text(name, NamedTextColor.GREEN)) + .append(Component.text("\nCliquez-ici", NamedTextColor.YELLOW)) + .clickEvent(getRunCommand("open " + id)) + .hoverEvent(getHoverEvent("Ouvrir la lettre #" + id)) + .append(Component.text(" pour ouvrir la lettre", NamedTextColor.GOLD)); + sendSuccessMessage(receiver, message); + Title titleComponent = getTitle(itemsCount, name); + receiver.playSound(receiver.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, SoundCategory.MASTER, 1.0f, 1.0f); + receiver.showTitle(titleComponent); + } + + private static @NotNull Title getTitle(int itemsCount, String name) { + Component subtitle = Component.text(name, NamedTextColor.GOLD).append(Component.text(" vous a envoyé ", NamedTextColor.YELLOW)) + .append(Component.text(itemsCount, NamedTextColor.GOLD)) + .append(Component.text(" item" + (itemsCount > 1 ? "s" : ""), NamedTextColor.YELLOW)); + Component title = Component.text("Nouvelle lettre !", NamedTextColor.GREEN); + return Title.title(title, subtitle); + } + + private static void sendFailureSendingMessage(Player player, String receiverName) { + Component message = Component.text("Une erreur est apparue lors de l'envoie des items à ", NamedTextColor.DARK_RED) + .append(Component.text(receiverName, NamedTextColor.RED)); + sendFailureMessage(player, message); + } + + private static void sendSuccessSendingMessage(Player player, String receiverName, int itemsCount) { + Component message = Component.text(itemsCount, NamedTextColor.GREEN) + .append(Component.text(" " + getItemCount(itemsCount) + " envoyé" + (itemsCount > 1 ? "s" : "") + " à ", NamedTextColor.DARK_GREEN)) + .append(Component.text(receiverName, NamedTextColor.GREEN)); + sendSuccessMessage(player, message); + } + + public static void givePlayerItems(Player player, ItemStack[] items) { + HashMap remainingItems = player.getInventory().addItem(items); + for (ItemStack item : remainingItems.values()) + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + + public static void cancelLetter(Player player, int id) { + MailboxInv inv = MailboxMenuManager.playerInventories.get(player); + if (inv instanceof PlayerMailbox playerMailbox) { + playerMailbox.removeLetter(id); + } else if (inv instanceof Letter letter) { + letter.cancel(); + } + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/letter/LetterHead.java b/src/main/java/fr/communaywen/core/mailboxes/letter/LetterHead.java new file mode 100644 index 00000000..27b708c8 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/letter/LetterHead.java @@ -0,0 +1,64 @@ +package fr.communaywen.core.mailboxes.letter; + +import fr.communaywen.core.mailboxes.menu.letter.Letter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.getPlayerName; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.nonItalic; +import static fr.communaywen.core.utils.StringDateFormatter.formatRelativeDate; + +public class LetterHead extends ItemStack { + private final int id; + private final int itemsCount; + private final ItemStack[] items; + + public LetterHead(OfflinePlayer player, int id, int itemsCount, LocalDateTime sentAt, ItemStack[] items) { + super(Material.PLAYER_HEAD, 1); + this.id = id; + this.itemsCount = itemsCount; + this.items = items; + SkullMeta skullMeta = (SkullMeta) this.getItemMeta(); + skullMeta.setOwningPlayer(player); + skullMeta.displayName(getPlayerName(player)); + ArrayList lore = new ArrayList<>(); + Component firstLine = Component.text(formatRelativeDate(sentAt), NamedTextColor.DARK_GRAY); + Component secondLine = Component.text("➤ Contient ", NamedTextColor.DARK_GREEN) + .append(Component.text(itemsCount, NamedTextColor.GREEN, TextDecoration.BOLD)) + .append(Component.text(" item" + (itemsCount > 1 ? "s" : ""), NamedTextColor.DARK_GREEN)); + lore.add(nonItalic(firstLine)); + lore.add(nonItalic(secondLine)); + skullMeta.lore(lore); + this.setItemMeta(skullMeta); + } + + public LetterHead(OfflinePlayer player, int id, int itemsCount, LocalDateTime sentAt) { + this(player, id, itemsCount, sentAt, null); + } + + public ItemStack[] getItems() { + return items; + } + + public void openLetter(Player player) { + Letter letter = new Letter(player, this); + letter.openInventory(); + } + + public int getItemsCount() { + return itemsCount; + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/letter/SenderLetter.java b/src/main/java/fr/communaywen/core/mailboxes/letter/SenderLetter.java new file mode 100644 index 00000000..07bb584e --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/letter/SenderLetter.java @@ -0,0 +1,45 @@ +package fr.communaywen.core.mailboxes.letter; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.SkullMeta; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.*; +import static fr.communaywen.core.utils.StringDateFormatter.formatRelativeDate; + +public class SenderLetter extends ItemStack { + private final int id; + + public SenderLetter(OfflinePlayer player, int id, int itemsCount, LocalDateTime sentAt, boolean refused) { + super(Material.PLAYER_HEAD, 1); + this.id = id; + SkullMeta skullMeta = (SkullMeta) this.getItemMeta(); + skullMeta.setOwningPlayer(player); + skullMeta.displayName(getStatus(refused)); + ArrayList lore = new ArrayList<>(); + lore.add(colorText("➡ Cliquez pour annuler", NamedTextColor.YELLOW, true)); + lore.add(getPlayerName(player)); + lore.add(colorText(formatRelativeDate(sentAt) + ", " + itemsCount + " " + getItemCount(itemsCount), NamedTextColor.DARK_GRAY, true)); + skullMeta.lore(lore); + this.setItemMeta(skullMeta); + } + + public static Component getStatus(boolean refused) { + NamedTextColor color = refused ? NamedTextColor.DARK_RED : NamedTextColor.DARK_AQUA; + Component status = Component.text("[", NamedTextColor.DARK_GRAY) + .append(Component.text(refused ? "❌" : "⌚", color)) + .append(Component.text("] ", NamedTextColor.DARK_GRAY)) + .append(Component.text(refused ? "Refusée" : "En attente", color)); + return nonItalic(status); + } + + public int getId() { + return id; + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/HomeMailbox.java b/src/main/java/fr/communaywen/core/mailboxes/menu/HomeMailbox.java new file mode 100644 index 00000000..8933b42b --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/HomeMailbox.java @@ -0,0 +1,62 @@ +package fr.communaywen.core.mailboxes.menu; + +import fr.communaywen.core.mailboxes.utils.MailboxMenuManager; +import fr.communaywen.core.mailboxes.menu.letter.SendingLetter; +import fr.communaywen.core.mailboxes.utils.MailboxInv; +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.OfflinePlayer; +import org.bukkit.entity.Player; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.getCustomItem; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.getHead; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.getItem; + +public class HomeMailbox extends MailboxInv { + private static final String INV_NAME = "\uF990\uE004"; + + public HomeMailbox(Player player) { + super(player); + this.inventory = Bukkit.createInventory(this, 9, MailboxMenuManager.getInvTitle(INV_NAME)); + inventory.setItem(3, getCustomItem(Component.text("En attente", NamedTextColor.DARK_AQUA, TextDecoration.BOLD), 2006)); + inventory.setItem(4, getHead(player, Component.text("Ma boite aux lettres", NamedTextColor.GOLD, TextDecoration.BOLD))); + inventory.setItem(5, getCustomItem(Component.text("Envoyer", NamedTextColor.DARK_AQUA, TextDecoration.BOLD), 2007)); + inventory.setItem(8, getItem(Component.text("Paramètres", NamedTextColor.DARK_AQUA, TextDecoration.BOLD), Material.COMPARATOR)); + } + + public static void openPlayersList(Player player) { + PlayersList playersList = new PlayersList(player); + playersList.openInventory(); + } + + public static void openSendingMailbox(Player player, OfflinePlayer receiver) { + SendingLetter sendingLetter = new SendingLetter(player, receiver); + sendingLetter.openInventory(); + } + + public static void openPlayerMailbox(Player player) { + PlayerMailbox playerMailbox = new PlayerMailbox(player); + playerMailbox.openInventory(); + } + + public static void openPendingMailbox(Player player) { + PendingMailbox pendingMailbox = new PendingMailbox(player); + pendingMailbox.openInventory(); + } + + public static void openSettings(Player player) { + } + + public static void openHomeMailbox(Player player) { + HomeMailbox homeMailbox = new HomeMailbox(player); + homeMailbox.openInventory(); + } + + @Override + public void openInventory() { + player.openInventory(this.inventory); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/PendingMailbox.java b/src/main/java/fr/communaywen/core/mailboxes/menu/PendingMailbox.java new file mode 100644 index 00000000..bd227a0a --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/PendingMailbox.java @@ -0,0 +1,99 @@ +package fr.communaywen.core.mailboxes.menu; + +import fr.communaywen.core.mailboxes.MailboxManager; +import fr.communaywen.core.mailboxes.letter.SenderLetter; +import fr.communaywen.core.mailboxes.utils.PaginatedMailbox; +import fr.communaywen.core.utils.serializer.BukkitSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.UUID; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.*; + +public class PendingMailbox extends PaginatedMailbox { + static { + invErrorMessage = "Erreur lors de la récupération de vos lettres."; + } + + public PendingMailbox(Player player) { + super(player); + if (fetchMailbox()) initInventory(); + } + + public static void cancelLetter(Player player, int id) { + try (PreparedStatement statement = connection.prepareStatement("SELECT receiver_id, items, items_count FROM openmc_db.mailbox_items WHERE id = ? AND sender_id = ?;")) { + statement.setInt(1, id); + statement.setString(2, player.getUniqueId().toString()); + try (ResultSet result = statement.executeQuery()) { + if (result.next()) { + int itemsCount = result.getInt("items_count"); + ItemStack[] items = BukkitSerializer.deserializeItemStacks(result.getBytes("items")); + Player receiver = Bukkit.getOfflinePlayer(UUID.fromString(result.getString("receiver_id"))).getPlayer(); + if (deleteLetter(id)) { + if (receiver != null) MailboxManager.cancelLetter(receiver, id); + MailboxManager.givePlayerItems(player, items); + Component message = Component.text("Vous avez annulé la lettre et reçu ", NamedTextColor.DARK_GREEN) + .append(Component.text(itemsCount, NamedTextColor.GREEN)) + .append(Component.text(" " + getItemCount(itemsCount), NamedTextColor.DARK_GREEN)); + sendSuccessMessage(player, message); + } + } else { + Component message = Component.text("La lettre avec l'id ", NamedTextColor.DARK_RED) + .append(Component.text(id, NamedTextColor.RED)) + .append(Component.text(" n'a pas été trouvée.", NamedTextColor.DARK_RED)); + sendFailureMessage(player, message); + } + } + } catch (Exception e) { + e.printStackTrace(); + sendFailureMessage(player, "Une erreur est survenue."); + } + } + + public static boolean deleteLetter(int id) throws SQLException { + try (PreparedStatement statement = connection.prepareStatement("DELETE FROM openmc_db.mailbox_items WHERE id = ?;")) { + statement.setInt(1, id); + return statement.executeUpdate() == 1; + } + } + + public boolean fetchMailbox() { + try (PreparedStatement statement = connection.prepareStatement("SELECT id, receiver_id, sent_at, items_count, refused FROM openmc_db.mailbox_items WHERE sender_id = ? ORDER BY sent_at DESC;")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet result = statement.executeQuery()) { + while (result.next()) { + UUID senderUUID = UUID.fromString(result.getString("receiver_id")); + int id = result.getInt("id"); + int itemsCount = result.getInt("items_count"); + LocalDateTime sentAt = result.getTimestamp("sent_at").toLocalDateTime(); + boolean refused = result.getBoolean("refused"); + pageItems.add(new SenderLetter(Bukkit.getOfflinePlayer(senderUUID), id, itemsCount, sentAt, refused)); + } + return true; + } + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public void clickLetter(int slot) { + SenderLetter senderLetter = getByIndex(slot); + if (senderLetter == null) return; + int id = senderLetter.getId(); + Component message = Component.text("Cliquez-ici", NamedTextColor.YELLOW) + .clickEvent(getRunCommand("cancel " + id)) + .hoverEvent(getHoverEvent("Annuler la lettre #" + id)) + .append(Component.text(" si vous êtes sur de vouloir annuler la lettre.", NamedTextColor.GOLD)); + sendWarningMessage(player, message); + player.closeInventory(); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/PlayerMailbox.java b/src/main/java/fr/communaywen/core/mailboxes/menu/PlayerMailbox.java new file mode 100644 index 00000000..2176d7d1 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/PlayerMailbox.java @@ -0,0 +1,65 @@ +package fr.communaywen.core.mailboxes.menu; + +import fr.communaywen.core.mailboxes.letter.LetterHead; +import fr.communaywen.core.mailboxes.utils.PaginatedMailbox; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.UUID; + +public class PlayerMailbox extends PaginatedMailbox { + + static { + invErrorMessage = "Erreur lors de la récupération de votre boite aux lettres."; + } + + public PlayerMailbox(Player player) { + super(player); + if (fetchMailbox()) initInventory(); + } + + public void addLetter(LetterHead letterHead) { + pageItems.add(letterHead); + int size = pageItems.size(); + if (size - 1 / maxIndex == page) updateInventory(false, size - 1 % maxIndex); + } + + public void removeLetter(int id) { + for (int i = 0; i < pageItems.size(); i++) { + if (pageItems.get(i).getId() == id) { + pageItems.remove(i); + int currentPage = i / maxIndex; + + if (currentPage == page) { + updateInventory(false, i % maxIndex); + } else if (currentPage < page) { + updateInventory(true); + } + break; + } + } + } + + public boolean fetchMailbox() { + try (PreparedStatement statement = connection.prepareStatement("SELECT id, sender_id, sent_at, items_count FROM openmc_db.mailbox_items WHERE receiver_id = ? AND refused = false ORDER BY sent_at DESC;")) { + statement.setString(1, player.getUniqueId().toString()); + try (ResultSet result = statement.executeQuery()) { + while (result.next()) { + int id = result.getInt("id"); + UUID senderUUID = UUID.fromString(result.getString("sender_id")); + int itemsCount = result.getInt("items_count"); + LocalDateTime sentAt = result.getTimestamp("sent_at").toLocalDateTime(); + pageItems.add(new LetterHead(Bukkit.getOfflinePlayer(senderUUID), id, itemsCount, sentAt)); + } + return true; + } + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/PlayersList.java b/src/main/java/fr/communaywen/core/mailboxes/menu/PlayersList.java new file mode 100644 index 00000000..a89b22f9 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/PlayersList.java @@ -0,0 +1,24 @@ +package fr.communaywen.core.mailboxes.menu; + +import fr.communaywen.core.mailboxes.utils.PaginatedMailbox; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.getPlayerHead; + +public class PlayersList extends PaginatedMailbox { + public PlayersList(Player player) { + super(player); + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { + if (onlinePlayer == player) continue; + pageItems.add(getPlayerHead(onlinePlayer)); + } + initInventory(); + } + + @Override + public void openInventory() { + player.openInventory(this.inventory); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/letter/Letter.java b/src/main/java/fr/communaywen/core/mailboxes/menu/letter/Letter.java new file mode 100644 index 00000000..e7df93fb --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/letter/Letter.java @@ -0,0 +1,146 @@ +package fr.communaywen.core.mailboxes.menu.letter; + +import fr.communaywen.core.mailboxes.letter.LetterHead; +import fr.communaywen.core.mailboxes.utils.MailboxInv; +import fr.communaywen.core.mailboxes.utils.MailboxMenuManager; +import fr.communaywen.core.utils.serializer.BukkitSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.UUID; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.*; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.*; + +public class Letter extends MailboxInv { + private final static String INV_NAME = "\uF990\uE003"; + + static { + invErrorMessage = "Erreur lors de la récupération de votre boite aux lettres."; + } + + private final int id; + private final int itemsCount; + private ItemStack[] items; + + public Letter(Player player, LetterHead letterHead) { + super(player); + this.id = letterHead.getId(); + this.itemsCount = letterHead.getItemsCount(); + this.items = letterHead.getItems(); + if (items != null || getMailboxById()) { + inventory = Bukkit.createInventory(this, 54, MailboxMenuManager.getInvTitle(INV_NAME)); + inventory.setItem(45, homeBtn()); + inventory.setItem(48, acceptBtn()); + inventory.setItem(49, letterHead); + inventory.setItem(50, refuseBtn()); + inventory.setItem(53, cancelBtn()); + + for (int i = 0; i < items.length; i++) inventory.setItem(i + 9, items[i]); + } + } + + public static LetterHead getById(Player player, int id) { + try (PreparedStatement statement = connection.prepareStatement("SELECT items_count, sent_at, sender_id, items FROM openmc_db.mailbox_items WHERE id = ? AND refused = false AND receiver_id = ?;")) { + statement.setInt(1, id); + statement.setString(2, player.getUniqueId().toString()); + try (ResultSet result = statement.executeQuery()) { + if (result.next()) { + int itemsCount = result.getInt("items_count"); + LocalDateTime sentAt = result.getTimestamp("sent_at").toLocalDateTime(); + OfflinePlayer sender = Bukkit.getOfflinePlayer(UUID.fromString(result.getString("sender_id"))); + ItemStack[] items = BukkitSerializer.deserializeItemStacks(result.getBytes("items")); + return new LetterHead(sender, id, itemsCount, sentAt, items); + } + sendFailureMessage(player, "La lettre n'a pas été trouvée."); + return null; + } + } catch (Exception e) { + e.printStackTrace(); + sendFailureMessage(player, "Une erreur est survenue."); + return null; + } + } + + public static void refuseLetter(Player player, int id) { + try (PreparedStatement statement = connection.prepareStatement("UPDATE openmc_db.mailbox_items SET refused = true WHERE id = ? AND receiver_id = ?;")) { + statement.setInt(1, id); + statement.setString(2, player.getUniqueId().toString()); + if (statement.executeUpdate() == 1) { + sendSuccessMessage(player, "La lettre a été refusée."); + } else { + Component message = Component.text("La lettre avec l'id ", NamedTextColor.DARK_RED) + .append(Component.text(id, NamedTextColor.RED)) + .append(Component.text(" n'existe pas.", NamedTextColor.DARK_RED)); + sendFailureMessage(player, message); + } + } catch (SQLException e) { + sendFailureMessage(player, "Une erreur est survenue."); + e.printStackTrace(); + } + } + + private boolean getMailboxById() { + try (PreparedStatement statement = connection.prepareStatement("SELECT items FROM openmc_db.mailbox_items WHERE id = ? AND refused = false;")) { + statement.setInt(1, id); + try (ResultSet result = statement.executeQuery()) { + if (result.next()) { + items = BukkitSerializer.deserializeItemStacks(result.getBytes("items")); + return true; + } + return false; + } + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + + public void accept() { + try (PreparedStatement statement = connection.prepareStatement("DELETE FROM openmc_db.mailbox_items WHERE id = ? AND refused = false;")) { + statement.setInt(1, id); + if (statement.executeUpdate() == 1) { + Component message = Component.text("Vous avez reçu ", NamedTextColor.DARK_GREEN) + .append(Component.text(itemsCount, NamedTextColor.GREEN)) + .append(Component.text(" " + getItemCount(itemsCount), NamedTextColor.DARK_GREEN)); + sendSuccessMessage(player, message); + HashMap remainingItems = player.getInventory().addItem(items); + for (ItemStack item : remainingItems.values()) { + player.getWorld().dropItemNaturally(player.getLocation(), item); + } + } else { + Component message = Component.text("La lettre avec l'id ", NamedTextColor.DARK_RED) + .append(Component.text(id, NamedTextColor.RED)) + .append(Component.text(" n'existe pas.", NamedTextColor.DARK_RED)); + sendFailureMessage(player, message); + } + } catch (SQLException e) { + e.printStackTrace(); + sendFailureMessage(player, "Une erreur est survenue."); + } + player.closeInventory(); + } + + public void refuse() { + Component message = Component.text("Cliquez-ici", NamedTextColor.YELLOW) + .clickEvent(getRunCommand("refuse " + id)) + .hoverEvent(getHoverEvent("Refuser la lettre #" + id)) + .append(Component.text(" si vous êtes sur de vouloir refuser la lettre.", NamedTextColor.GOLD)); + sendWarningMessage(player, message); + player.closeInventory(); + } + + public void cancel() { + player.closeInventory(); + sendFailureMessage(player, "La lettre a été annulée."); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/menu/letter/SendingLetter.java b/src/main/java/fr/communaywen/core/mailboxes/menu/letter/SendingLetter.java new file mode 100644 index 00000000..83e02193 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/menu/letter/SendingLetter.java @@ -0,0 +1,72 @@ +package fr.communaywen.core.mailboxes.menu.letter; + +import fr.communaywen.core.mailboxes.MailboxManager; +import fr.communaywen.core.mailboxes.utils.MailboxInv; +import fr.communaywen.core.mailboxes.utils.MailboxMenuManager; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.*; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.getHead; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.sendFailureMessage; + +public class SendingLetter extends MailboxInv { + private final static String INV_NAME = "\uF990\uE003"; + private final OfflinePlayer receiver; + + public SendingLetter(Player player, OfflinePlayer receiver) { + super(player); + this.receiver = receiver; + inventory = Bukkit.createInventory(this, 54, MailboxMenuManager.getInvTitle(INV_NAME)); + inventory.setItem(49, getHead(receiver)); + inventory.setItem(45, homeBtn()); + inventory.setItem(48, sendBtn()); + inventory.setItem(50, cancelBtn()); + + for (int i = 0; i < 9; i++) inventory.setItem(i, transparentItem()); + } + + @Override + public void openInventory() { + player.openInventory(this.inventory); + } + + public ItemStack[] getItems() { + List itemsList = new ArrayList<>(27); + for (int slot = 9; slot < 36; slot++) { + ItemStack item = inventory.getItem(slot); + if (item != null && !item.getType().isAir()) itemsList.add(item); + } + return itemsList.toArray(new ItemStack[0]); + } + + public void sendLetter() { + ItemStack[] items = getItems(); + inventory.clear(); + player.closeInventory(); + if (items.length == 0) { + sendFailureMessage(player, "Vous ne pouvez pas envoyer de lettre vide"); + } else if (!MailboxManager.sendItems(player, receiver, items)) MailboxManager.givePlayerItems(player, items); + } + + public void giveItems() { + MailboxManager.givePlayerItems(player, getItems()); + } + + public boolean noSpace(ItemStack item) { + if (item == null || item.getType().isAir()) return false; + int size = item.getAmount(); + for (int slot = 9; slot < 36; slot++) { + ItemStack targetItem = inventory.getItem(slot); + if (targetItem == null || targetItem.getType().isAir()) return false; + if (targetItem.isSimilar(item)) size -= targetItem.getMaxStackSize() - targetItem.getAmount(); + if (size <= 0) return false; + } + return true; + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxInv.java b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxInv.java new file mode 100644 index 00000000..68a5222c --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxInv.java @@ -0,0 +1,48 @@ +package fr.communaywen.core.mailboxes.utils; + +import fr.communaywen.core.AywenCraftPlugin; +import fr.communaywen.core.utils.database.DatabaseConnector; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.playerInventories; +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.sendFailureMessage; + +public abstract class MailboxInv extends DatabaseConnector implements InventoryHolder { + protected static String invErrorMessage; + protected final Player player; + protected final AywenCraftPlugin plugin = AywenCraftPlugin.getInstance(); + protected Inventory inventory; + + public MailboxInv(Player player) { + this.player = player; + } + + public void addInventory() { + playerInventories.put(player, this); + } + + public void removeInventory() { + playerInventories.remove(player); + } + + @Override + public @NotNull Inventory getInventory() { + return this.inventory; + } + + public void openInventory() { + if (inventory == null) { + sendInvErrorMessage(player); + return; + } + player.openInventory(this.inventory); + } + + protected void sendInvErrorMessage(Player player) { + if (invErrorMessage == null) return; + sendFailureMessage(player, invErrorMessage); + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxMenuManager.java b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxMenuManager.java new file mode 100644 index 00000000..6bbeec6c --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxMenuManager.java @@ -0,0 +1,141 @@ +package fr.communaywen.core.mailboxes.utils; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.HashMap; +import java.util.List; + +import static fr.communaywen.core.mailboxes.utils.MailboxUtils.nonItalic; + +public class MailboxMenuManager { + public static final Key openmcKey = Key.key("openmc", "menu"); + public static final HashMap playerInventories = new HashMap<>(); + private static final Material customMaterial = Material.BARRIER; + + public static Component getInvTitle(String title) { + return Component.text(title, NamedTextColor.WHITE).font(openmcKey); + } + + public static ItemStack getCustomItem(Component name, int data) { + return getCustomItem(name, data, null); + } + + public static ItemStack getCustomItem(Component name, int data, List lore) { + ItemStack item = new ItemStack(customMaterial); + ItemMeta meta = item.getItemMeta(); + meta.setCustomModelData(data); + meta.displayName(nonItalic(name)); + if (lore != null) meta.lore(lore); + meta.setMaxStackSize(1); + item.setItemMeta(meta); + return item; + } + + public static ItemStack transparentItem() { + ItemStack item = new ItemStack(customMaterial); + ItemMeta meta = item.getItemMeta(); + meta.setHideTooltip(true); + meta.setCustomModelData(2005); + meta.displayName(Component.text("")); + meta.setMaxStackSize(1); + item.setItemMeta(meta); + return item; + } + + public static ItemStack getBtn(String symbol, String name, int data, NamedTextColor color) { + return getBtn(symbol, name, data, color, false); + } + + public static ItemStack getBtn(String symbol, String name, int data, NamedTextColor color, boolean bold) { + Component itemName = Component.text("[", NamedTextColor.DARK_GRAY).append(Component.text(symbol, color)).append(Component.text("]", NamedTextColor.DARK_GRAY)).append(Component.text(" " + name, color)); + return getCustomItem(bold ? itemName.decorate(TextDecoration.BOLD) : itemName, data); + } + + public static ItemStack cancelBtn() { + return getBtn("❌", "Cancel", 2000, NamedTextColor.DARK_RED, true); + } + + public static ItemStack nextPageBtn() { + Component name = Component.text("Next page ➡", NamedTextColor.GOLD, TextDecoration.BOLD); + return getCustomItem(name, 2003); + } + + public static ItemStack previousPageBtn() { + Component name = Component.text("⬅ Previous page", NamedTextColor.GOLD, TextDecoration.BOLD); + return getCustomItem(name, 2004); + } + + public static ItemStack acceptBtn() { + return getBtn("✔", "Accepter", 2001, NamedTextColor.DARK_GREEN); + } + + public static ItemStack sendBtn() { + return getBtn("✉", "Envoyer", 2007, NamedTextColor.DARK_AQUA); + } + + public static ItemStack refuseBtn() { + return getBtn("❌", "Refuser", 2002, NamedTextColor.DARK_RED); + } + + public static ItemStack homeBtn() { + ItemStack item = new ItemStack(Material.CHEST); + ItemMeta meta = item.getItemMeta(); + meta.displayName(Component.text("⬅ Home", NamedTextColor.GOLD, TextDecoration.BOLD).decoration(TextDecoration.ITALIC, false)); + meta.setMaxStackSize(1); + item.setItemMeta(meta); + return item; + } + + public static ItemStack getPlayerHead(Player player) { + return getPlayerHead(player, Component.text(player.getName(), NamedTextColor.GOLD, TextDecoration.BOLD)); + } + + public static ItemStack getPlayerHead(Player player, Component name) { + ItemStack item = new ItemStack(Material.PLAYER_HEAD, 1); + SkullMeta meta = (SkullMeta) item.getItemMeta(); + meta.setOwningPlayer(player); + meta.displayName(nonItalic(name)); + item.setItemMeta(meta); + return item; + } + + public static boolean isNotNull(ItemStack item) { + return item != null && item.getType() != Material.AIR && item.getItemMeta() != null; + } + + public static boolean isBtn(ItemStack item, int data) { + return isNotNull(item) && item.getType() == customMaterial && item.getItemMeta().hasCustomModelData() && item.getItemMeta().getCustomModelData() == data; + } + + public static boolean cancelBtn(ItemStack item) { + return isBtn(item, 2000); + } + + public static boolean nextPageBtn(ItemStack item) { + return isBtn(item, 2003); + } + + public static boolean previousPageBtn(ItemStack item) { + return isBtn(item, 2004); + } + + public static boolean acceptBtn(ItemStack item) { + return isBtn(item, 2001); + } + + public static boolean sendBtn(ItemStack item) { + return isBtn(item, 2007); + } + + public static boolean refuseBtn(ItemStack item) { + return isBtn(item, 2002); + } +} \ No newline at end of file diff --git a/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxUtils.java b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxUtils.java new file mode 100644 index 00000000..7265d7d1 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/utils/MailboxUtils.java @@ -0,0 +1,97 @@ +package fr.communaywen.core.mailboxes.utils; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.NotNull; + +public class MailboxUtils { + public static Component getPlayerName(OfflinePlayer player) { + String pName = player.getName(); + return Component.text("⬤ ", player.isConnected() ? NamedTextColor.DARK_GREEN : NamedTextColor.DARK_RED) + .append(Component.text(pName == null ? "Unknown" : pName, NamedTextColor.GOLD, TextDecoration.BOLD)) + .decoration(TextDecoration.ITALIC, false); + } + + public static String getItemCount(int itemsCount) { + return "item" + (itemsCount > 1 ? "s" : ""); + } + + public static ItemStack getHead(OfflinePlayer player, Component name) { + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) head.getItemMeta(); + meta.setOwningPlayer(player); + meta.displayName(nonItalic(name)); + head.setItemMeta(meta); + return head; + } + + public static Component colorText(String text, NamedTextColor color, boolean nonItalic) { + Component component = Component.text(text, color); + return nonItalic ? nonItalic(component) : component; + } + + public static Component nonItalic(Component name) { + return name.decoration(TextDecoration.ITALIC, false); + } + + public static void sendWarningMessage(Player player, Component message) { + Component success = Component.text("[", NamedTextColor.DARK_GRAY).append(Component.text("⚠", NamedTextColor.GOLD)).append(Component.text("] ", NamedTextColor.DARK_GRAY)).append(message); + player.sendMessage(success); + } + + public static void sendWarningMessage(Player player, String message) { + sendWarningMessage(player, Component.text(message, NamedTextColor.GOLD)); + } + + public static void sendSuccessMessage(Player player, String message) { + sendSuccessMessage(player, Component.text(message, NamedTextColor.DARK_GREEN)); + } + + public static void sendSuccessMessage(Player player, Component message) { + Component success = Component.text("[", NamedTextColor.DARK_GRAY).append(Component.text("✔", NamedTextColor.DARK_GREEN)).append(Component.text("] ", NamedTextColor.DARK_GRAY)).append(message); + player.sendMessage(success); + } + + public static void sendFailureMessage(Player player, String message) { + sendFailureMessage(player, Component.text(message, NamedTextColor.DARK_RED)); + } + + public static void sendFailureMessage(Player player, Component message) { + Component success = Component.text("[", NamedTextColor.DARK_GRAY).append(Component.text("❌", NamedTextColor.DARK_RED)).append(Component.text("] ", NamedTextColor.DARK_GRAY)).append(message); + player.sendMessage(success); + } + + public static void sendHelp(Player sender, String message) { + Component finalMessage = Component.text("[", NamedTextColor.DARK_GRAY).append(Component.text("help", NamedTextColor.DARK_AQUA)).append(Component.text("] ", NamedTextColor.DARK_GRAY)).append(Component.text(message, NamedTextColor.GOLD)); + sender.sendMessage(finalMessage); + } + + public static @NotNull HoverEvent getHoverEvent(String message) { + return HoverEvent.showText(Component.text(message, NamedTextColor.GRAY)); + } + + public static @NotNull ClickEvent getRunCommand(String command) { + return ClickEvent.runCommand("/mailbox " + command); + } + + public static ItemStack getHead(OfflinePlayer player) { + return getHead(player, getPlayerName(player)); + } + + public static ItemStack getItem(Component name, Material material) { + ItemStack item = new ItemStack(material, 1); + ItemMeta meta = item.getItemMeta(); + meta.displayName(nonItalic(name)); + item.setItemMeta(meta); + return item; + } +} diff --git a/src/main/java/fr/communaywen/core/mailboxes/utils/PaginatedMailbox.java b/src/main/java/fr/communaywen/core/mailboxes/utils/PaginatedMailbox.java new file mode 100644 index 00000000..f827a835 --- /dev/null +++ b/src/main/java/fr/communaywen/core/mailboxes/utils/PaginatedMailbox.java @@ -0,0 +1,80 @@ +package fr.communaywen.core.mailboxes.utils; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; + +import static fr.communaywen.core.mailboxes.utils.MailboxMenuManager.*; + +public abstract class PaginatedMailbox extends MailboxInv { + protected final int maxIndex; + protected final String invName; + protected final ArrayList pageItems = new ArrayList<>(); + protected int page = 0; + + public PaginatedMailbox(Player player, int maxIndex, String invName) { + super(player); + this.maxIndex = maxIndex; + this.invName = invName; + } + + public PaginatedMailbox(Player player, String invName) { + this(player, 45, invName); + } + + public PaginatedMailbox(Player player) { + this(player, "\uF990\uE002"); + } + + protected void initInventory() { + inventory = Bukkit.createInventory(this, 54, MailboxMenuManager.getInvTitle(invName)); + inventory.setItem(45, homeBtn()); + inventory.setItem(48, previousPageBtn()); + inventory.setItem(49, cancelBtn()); + inventory.setItem(50, nextPageBtn()); + updateInventory(false); + } + + public T getByIndex(int index) { + int currentIndex = page * maxIndex + index; + return currentIndex >= pageItems.size() ? null : pageItems.get(currentIndex); + } + + private void clearFrom(int index) { + for (int i = index; i < maxIndex; i++) inventory.clear(i); + } + + public void nextPage() { + updatePage(++page); + } + + public void previousPage() { + updatePage(--page); + } + + private void updatePage(int page) { + int maxPage = pageItems.size() / maxIndex; + this.page = Math.max(0, Math.min(page, maxPage)); + updateInventory(true); + } + + protected void updateInventory(boolean clear, int index) { + for (int i = index; i < maxIndex; i++) { + ItemStack item = getByIndex(i); + if (item == null) { + if (i == 0 && page > 0) { + previousPage(); + return; + } else if (clear) clearFrom(i); + break; + } + inventory.setItem(i, item); + } + } + + protected void updateInventory(boolean clear) { + updateInventory(clear, 0); + } +} diff --git a/src/main/java/fr/communaywen/core/utils/StringDateFormatter.java b/src/main/java/fr/communaywen/core/utils/StringDateFormatter.java index 60421a00..298ff551 100644 --- a/src/main/java/fr/communaywen/core/utils/StringDateFormatter.java +++ b/src/main/java/fr/communaywen/core/utils/StringDateFormatter.java @@ -8,11 +8,11 @@ public class StringDateFormatter { public static String formatRelativeDate(LocalDateTime dateTime) { LocalDateTime now = LocalDateTime.now(); Duration duration = Duration.between(dateTime, now); + long minutes = duration.toMinutes(); - if (duration.toMinutes() < 1) { + if (minutes < 1) { return "À l'instant"; - } else if (duration.toMinutes() < 60) { - long minutes = duration.toMinutes(); + } else if (minutes < 60) { return "Il y a " + minutes + " minute" + (minutes > 1 ? "s" : ""); } else if (duration.toHours() < 24) { long hours = duration.toHours(); @@ -21,7 +21,7 @@ public static String formatRelativeDate(LocalDateTime dateTime) { long days = duration.toDays(); return "Il y a " + days + " jour" + (days > 1 ? "s" : ""); } else { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("Le dd/MM/yyyy à HH:mm"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("'Le' dd/MM/yyyy 'à' HH:mm"); return dateTime.format(formatter); } } diff --git a/src/main/java/fr/communaywen/core/utils/database/DatabaseManager.java b/src/main/java/fr/communaywen/core/utils/database/DatabaseManager.java index bf66a2d9..3a2f85ab 100644 --- a/src/main/java/fr/communaywen/core/utils/database/DatabaseManager.java +++ b/src/main/java/fr/communaywen/core/utils/database/DatabaseManager.java @@ -42,7 +42,15 @@ public void init() throws SQLException { "secondPlayer_uuid VARCHAR(36) NOT NULL," + "friendDate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP" + ")").executeUpdate(); - + this.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS mailbox_items (" + + "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + + "sender_id UUID NOT NULL," + + "receiver_id UUID NOT NULL," + + "items BLOB NOT NULL," + + "items_count INT NOT NULL," + + "sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP," + + "refused BOOLEAN NOT NULL DEFAULT FALSE" + + ")").executeUpdate(); this.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS blacklists (Owner VARCHAR(36), Blocked VARCHAR(36))").executeUpdate(); this.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS link (discord_id VARCHAR(100) NOT NULL, minecraft_uuid VARCHAR(36))").executeUpdate(); this.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS link_verif (minecraft_uuid VARCHAR(36) NOT NULL, code int(11) NOT NULL)").executeUpdate(); diff --git a/src/main/java/fr/communaywen/core/utils/serializer/BukkitSerializer.java b/src/main/java/fr/communaywen/core/utils/serializer/BukkitSerializer.java index 5fa3d173..022e71bd 100644 --- a/src/main/java/fr/communaywen/core/utils/serializer/BukkitSerializer.java +++ b/src/main/java/fr/communaywen/core/utils/serializer/BukkitSerializer.java @@ -10,28 +10,16 @@ import java.io.IOException; public class BukkitSerializer { - public byte[] serializeItemStacks(ItemStack[] inv) throws Exception { - try { - @Cleanup - ByteArrayOutputStream os = new ByteArrayOutputStream(); - @Cleanup - BukkitObjectOutputStream bos = new BukkitObjectOutputStream(os); - bos.writeObject(inv); - return os.toByteArray(); - } catch (IOException ex) { - throw ex; - } + public static byte[] serializeItemStacks(ItemStack[] inv) throws IOException { + @Cleanup ByteArrayOutputStream os = new ByteArrayOutputStream(); + @Cleanup BukkitObjectOutputStream bos = new BukkitObjectOutputStream(os); + bos.writeObject(inv); + return os.toByteArray(); } - public ItemStack[] deserializeItemStacks(byte[] b) throws Exception { - try { - @Cleanup - ByteArrayInputStream bais = new ByteArrayInputStream(b); - @Cleanup - BukkitObjectInputStream bois = new BukkitObjectInputStream(bais); - return (ItemStack[]) bois.readObject(); - } catch (IOException ex) { - throw ex; - } + public static ItemStack[] deserializeItemStacks(byte[] b) throws Exception { + @Cleanup ByteArrayInputStream bais = new ByteArrayInputStream(b); + @Cleanup BukkitObjectInputStream bois = new BukkitObjectInputStream(bais); + return (ItemStack[]) bois.readObject(); } } \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d813fe81..922eb7fc 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -57,3 +57,6 @@ permissions: description: 'Donne un bandage' ayw.command.report: description: 'Permet de signaler un joueur' + ayw.command.mailbox: + description: 'Utilise la boite aux lettres' + default: true