From 964f3758419b1716b41ab378c764c17e0b641f53 Mon Sep 17 00:00:00 2001 From: SarTron-NorthBlue Date: Wed, 15 Jul 2026 13:26:53 +0400 Subject: [PATCH] Ajoute le GUI VoteParty avec liens de vote cliquables (v1.0.1) Nouvelle commande /votegui : affiche la progression du VoteParty et un item par site de vote configure. Un clic ferme le GUI et envoie un message chat cliquable vers l'URL du site (Bukkit ne permet pas d'ouvrir un navigateur directement depuis un clic d'inventaire). Co-Authored-By: Claude Sonnet 5 --- pom.xml | 2 +- spigot/pom.xml | 2 +- .../fr/votenetwork/spigot/VoteConfig.java | 56 +++++++++++++++ .../votenetwork/spigot/VoteNetworkSpigot.java | 4 ++ .../spigot/command/VoteGuiCommand.java | 31 ++++++++ .../votenetwork/spigot/gui/VotePartyGui.java | 72 +++++++++++++++++++ .../spigot/gui/VotePartyGuiHolder.java | 23 ++++++ .../spigot/gui/VotePartyGuiListener.java | 53 ++++++++++++++ .../fr/votenetwork/spigot/gui/VoteSite.java | 40 +++++++++++ spigot/src/main/resources/config.yml | 24 +++++++ spigot/src/main/resources/plugin.yml | 3 + velocity/pom.xml | 2 +- .../velocity/VoteNetworkVelocity.java | 2 +- 13 files changed, 310 insertions(+), 4 deletions(-) create mode 100644 spigot/src/main/java/fr/votenetwork/spigot/command/VoteGuiCommand.java create mode 100644 spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGui.java create mode 100644 spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiHolder.java create mode 100644 spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiListener.java create mode 100644 spigot/src/main/java/fr/votenetwork/spigot/gui/VoteSite.java diff --git a/pom.xml b/pom.xml index 839051c..ca6490a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ fr.votenetwork votenetwork-parent - 1.0.0 + 1.0.1 pom diff --git a/spigot/pom.xml b/spigot/pom.xml index 0a84672..f7e5135 100644 --- a/spigot/pom.xml +++ b/spigot/pom.xml @@ -7,7 +7,7 @@ fr.votenetwork votenetwork-parent - 1.0.0 + 1.0.1 votenetwork-spigot diff --git a/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java b/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java index 0914bd1..39e1d83 100644 --- a/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java +++ b/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java @@ -1,9 +1,14 @@ package fr.votenetwork.spigot; +import fr.votenetwork.spigot.gui.VoteSite; import org.bukkit.ChatColor; +import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; +import java.util.ArrayList; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.stream.Collectors; public class VoteConfig { @@ -141,6 +146,57 @@ public class VoteConfig { "&cVous n'avez pas la permission d'utiliser cette commande.")); } + public String getGuiTitle() { + return colorize(cfg().getString("gui.title", "&8VoteNetwork")); + } + + public String getGuiProgressItemMaterial() { + return cfg().getString("gui.progress-item.material", "NETHER_STAR"); + } + + public String getGuiProgressItemName() { + return colorize(cfg().getString("gui.progress-item.name", "&b&lVoteParty")); + } + + public List getGuiProgressItemLore() { + return colorizeList(cfg().getStringList("gui.progress-item.lore")); + } + + public String getGuiClickMessage() { + return colorize(cfg().getString("gui.click-message", "&a[Vote] Clique sur le lien pour voter :")); + } + + /** + * Parses gui.vote-sites (a YAML list of maps: name/material/url/lore) into VoteSite + * objects, one item per configured vote site. + */ + public List getVoteSites() { + List sites = new ArrayList<>(); + for (Map raw : cfg().getMapList("gui.vote-sites")) { + String name = raw.containsKey("name") ? String.valueOf(raw.get("name")) : "&aVoter"; + String materialName = raw.containsKey("material") ? String.valueOf(raw.get("material")) : "PAPER"; + String url = raw.containsKey("url") ? String.valueOf(raw.get("url")) : ""; + + List lore = new ArrayList<>(); + Object loreObj = raw.get("lore"); + if (loreObj instanceof List loreList) { + for (Object line : loreList) { + lore.add(String.valueOf(line)); + } + } + + Material material; + try { + material = Material.valueOf(materialName.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + material = Material.PAPER; + } + + sites.add(new VoteSite(colorize(name), material, url, colorizeList(lore))); + } + return sites; + } + private String colorize(String input) { return input == null ? "" : ChatColor.translateAlternateColorCodes('&', input); } diff --git a/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java b/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java index f24e99b..1b76606 100644 --- a/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java +++ b/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java @@ -5,6 +5,8 @@ import fr.votenetwork.spigot.api.VoteNetworkAPIHolder; import fr.votenetwork.spigot.api.VoteNetworkAPIImpl; import fr.votenetwork.spigot.command.ClaimCommand; import fr.votenetwork.spigot.command.VoteAdminCommand; +import fr.votenetwork.spigot.command.VoteGuiCommand; +import fr.votenetwork.spigot.gui.VotePartyGuiListener; import fr.votenetwork.spigot.listener.VoteMessageListener; import fr.votenetwork.spigot.placeholder.VoteNetworkPlaceholders; import fr.votenetwork.spigot.storage.MySqlPendingVoteStore; @@ -55,6 +57,8 @@ public class VoteNetworkSpigot extends JavaPlugin { getCommand("claim").setExecutor(new ClaimCommand(this, voteConfig, pendingVoteStore, voteParty, maintenanceState)); getCommand("vote").setExecutor(new VoteAdminCommand(this, voteConfig, pendingVoteStore, maintenanceState)); + getCommand("votegui").setExecutor(new VoteGuiCommand(voteConfig, voteParty)); + getServer().getPluginManager().registerEvents(new VotePartyGuiListener(voteConfig), this); VoteNetworkAPI api = new VoteNetworkAPIImpl(this, voteConfig, pendingVoteStore, voteParty, maintenanceState); VoteNetworkAPIHolder.set(api); diff --git a/spigot/src/main/java/fr/votenetwork/spigot/command/VoteGuiCommand.java b/spigot/src/main/java/fr/votenetwork/spigot/command/VoteGuiCommand.java new file mode 100644 index 0000000..854c796 --- /dev/null +++ b/spigot/src/main/java/fr/votenetwork/spigot/command/VoteGuiCommand.java @@ -0,0 +1,31 @@ +package fr.votenetwork.spigot.command; + +import fr.votenetwork.spigot.VoteConfig; +import fr.votenetwork.spigot.gui.VotePartyGui; +import fr.votenetwork.spigot.voteparty.VoteParty; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class VoteGuiCommand implements CommandExecutor { + + private final VoteConfig config; + private final VoteParty voteParty; + + public VoteGuiCommand(VoteConfig config, VoteParty voteParty) { + this.config = config; + this.voteParty = voteParty; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(ChatColor.RED + "Commande reservee aux joueurs."); + return true; + } + VotePartyGui.open(player, config, voteParty); + return true; + } +} diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGui.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGui.java new file mode 100644 index 0000000..680de71 --- /dev/null +++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGui.java @@ -0,0 +1,72 @@ +package fr.votenetwork.spigot.gui; + +import fr.votenetwork.spigot.VoteConfig; +import fr.votenetwork.spigot.voteparty.VoteParty; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.List; + +/** + * Builds and opens the VoteParty progress GUI: a progress item (slot 4) plus one item per + * configured vote site (starting at slot 9), each opening its URL when clicked. + */ +public final class VotePartyGui { + + public static final int PROGRESS_SLOT = 4; + public static final int SITES_START_SLOT = 9; + + private VotePartyGui() { + } + + public static void open(Player player, VoteConfig config, VoteParty voteParty) { + List sites = config.getVoteSites(); + int size = ((SITES_START_SLOT + sites.size() + 8) / 9) * 9; + size = Math.max(size, 18); + + VotePartyGuiHolder holder = new VotePartyGuiHolder(); + Inventory inventory = Bukkit.createInventory(holder, size, config.getGuiTitle()); + holder.setInventory(inventory); + + int required = config.getVotePartyVotesRequired(); + int current = Math.min(voteParty.getCurrentVotes(), required); + + ItemStack progressItem = new ItemStack(materialOrFallback(config.getGuiProgressItemMaterial())); + ItemMeta progressMeta = progressItem.getItemMeta(); + if (progressMeta != null) { + progressMeta.setDisplayName(config.getGuiProgressItemName()); + progressMeta.setLore(config.getGuiProgressItemLore().stream() + .map(line -> line.replace("%current%", String.valueOf(current)) + .replace("%required%", String.valueOf(required))) + .toList()); + progressItem.setItemMeta(progressMeta); + } + inventory.setItem(PROGRESS_SLOT, progressItem); + + int slot = SITES_START_SLOT; + for (VoteSite site : sites) { + ItemStack item = new ItemStack(site.getMaterial()); + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + meta.setDisplayName(site.getName()); + meta.setLore(site.getLore()); + item.setItemMeta(meta); + } + inventory.setItem(slot, item); + slot++; + } + + player.openInventory(inventory); + } + + private static org.bukkit.Material materialOrFallback(String name) { + try { + return org.bukkit.Material.valueOf(name.toUpperCase(java.util.Locale.ROOT)); + } catch (IllegalArgumentException e) { + return org.bukkit.Material.NETHER_STAR; + } + } +} diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiHolder.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiHolder.java new file mode 100644 index 0000000..3109961 --- /dev/null +++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiHolder.java @@ -0,0 +1,23 @@ +package fr.votenetwork.spigot.gui; + +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +/** + * Marker holder used to recognize our own GUI inventory in InventoryClickEvent, so the + * listener never touches clicks happening in unrelated inventories (chests, other plugins' + * GUIs, etc.). + */ +public class VotePartyGuiHolder implements InventoryHolder { + + private Inventory inventory; + + @Override + public Inventory getInventory() { + return inventory; + } + + public void setInventory(Inventory inventory) { + this.inventory = inventory; + } +} diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiListener.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiListener.java new file mode 100644 index 0000000..c7d5bef --- /dev/null +++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/VotePartyGuiListener.java @@ -0,0 +1,53 @@ +package fr.votenetwork.spigot.gui; + +import fr.votenetwork.spigot.VoteConfig; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.List; + +/** + * Bukkit cannot open a browser directly from an inventory click, so a click on a vote-site + * item closes the GUI and sends a clickable chat message (ClickEvent OPEN_URL) instead. + */ +public class VotePartyGuiListener implements Listener { + + private final VoteConfig config; + + public VotePartyGuiListener(VoteConfig config) { + this.config = config; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getInventory().getHolder() instanceof VotePartyGuiHolder)) { + return; + } + event.setCancelled(true); + + int slot = event.getRawSlot(); + if (slot < VotePartyGui.SITES_START_SLOT || !(event.getWhoClicked() instanceof Player player)) { + return; + } + + List sites = config.getVoteSites(); + int index = slot - VotePartyGui.SITES_START_SLOT; + if (index < 0 || index >= sites.size()) { + return; + } + + VoteSite site = sites.get(index); + if (site.getUrl() == null || site.getUrl().isEmpty()) { + return; + } + + player.closeInventory(); + TextComponent message = new TextComponent(config.getGuiClickMessage() + " " + site.getUrl()); + message.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, site.getUrl())); + player.spigot().sendMessage(message); + } +} diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteSite.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteSite.java new file mode 100644 index 0000000..d1fa954 --- /dev/null +++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteSite.java @@ -0,0 +1,40 @@ +package fr.votenetwork.spigot.gui; + +import org.bukkit.Material; + +import java.util.List; + +/** + * A single vote-site entry configured in config.yml (gui.vote-sites): the display item and + * the URL opened (via a clickable chat message) when the player clicks it in the GUI. + */ +public class VoteSite { + + private final String name; + private final Material material; + private final String url; + private final List lore; + + public VoteSite(String name, Material material, String url, List lore) { + this.name = name; + this.material = material; + this.url = url; + this.lore = lore; + } + + public String getName() { + return name; + } + + public Material getMaterial() { + return material; + } + + public String getUrl() { + return url; + } + + public List getLore() { + return lore; + } +} diff --git a/spigot/src/main/resources/config.yml b/spigot/src/main/resources/config.yml index c600160..38ee5d1 100644 --- a/spigot/src/main/resources/config.yml +++ b/spigot/src/main/resources/config.yml @@ -47,6 +47,30 @@ voteparty: progress-message: "&b[Vote] &f%current%&7/&f%required% &fvotes avant le prochain VoteParty !" triggered-message: "&a[Vote] &fVoteParty declenche ! Profitez des recompenses !" +# GUI /votegui : affiche la progression du VoteParty et des items cliquables vers les sites +# de vote (ouvre le lien via un message chat cliquable, Bukkit ne peut pas ouvrir un navigateur +# directement depuis un clic d'inventaire). +gui: + title: "&8VoteNetwork" + progress-item: + material: NETHER_STAR + name: "&b&lVoteParty" + lore: + - "&7Progression actuelle :" + - "&f%current%&7/&f%required% &7votes" + click-message: "&a[Vote] Clique sur le lien pour voter :" + vote-sites: + - name: "&aVoter sur le site 1" + material: PAPER + url: "https://example.com/vote/site1" + lore: + - "&7Clique pour voter !" + - name: "&aVoter sur le site 2" + material: BOOK + url: "https://example.com/vote/site2" + lore: + - "&7Clique pour voter !" + messages: direct-vote: "&a[Vote] &fMerci pour ton vote !" claim-success: "&a[Vote] &fVous avez recupere &e%amount% &fvote(s) en attente !" diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index 9031b9a..aaa65ab 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -14,6 +14,9 @@ commands: description: Administration du systeme de vote (maintenance, test du stockage). usage: /vote permission: votenetwork.admin + votegui: + description: Ouvre le GUI de progression du VoteParty avec les liens de vote. + usage: /votegui permissions: votenetwork.admin: diff --git a/velocity/pom.xml b/velocity/pom.xml index 0c50857..c858217 100644 --- a/velocity/pom.xml +++ b/velocity/pom.xml @@ -7,7 +7,7 @@ fr.votenetwork votenetwork-parent - 1.0.0 + 1.0.1 votenetwork-velocity diff --git a/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java b/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java index d507155..a2dc281 100644 --- a/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java +++ b/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java @@ -14,7 +14,7 @@ import java.nio.file.Path; @Plugin( id = "votenetwork", name = "VoteNetwork", - version = "1.0.0", + version = "1.0.1", description = "Systeme de vote reseau Velocity <-> Spigot", authors = {"Sar_Tron"} )