Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ on:
types: [ opened, synchronize, reopened ]
workflow_dispatch:


permissions:
contents: read
packages: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: ${{ github.ref_name != 'main' }}

jobs:
build:
name: Build and Publish zEssentials
uses: GroupeZ-dev/actions/.github/workflows/build.yml@main
uses: GroupeZ-dev/actions/.github/workflows/build.yml@v1.0.0
with:
project-name: "zEssentials"
java-version: '25'
publish: true
project-to-publish: "API:publish"
discord-avatar-url: "https://groupez.dev/storage/images/325.png"
artifact-glob: "target/*.jar"
changelog-exclude-pattern: '^[a-f0-9]+\s+(build|chore)\(deps\)'
secrets:
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,87 @@
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MessageHelper {

private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

private static final Pattern COLOR_PATTERN = Pattern.compile(
"(<(?:\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'|[^<>\"'])*>|(?i:https?://)[^\\s<>]+)"
+ "|(§[xX](?:§[0-9a-fA-F]){6})"
+ "|(?<![</:])&?#([a-fA-F0-9]{6})"
+ "|[&§]([0-9a-fk-orA-FK-OR])");
private static final Map<Character, String> COLORS_MAPPINGS = Map.ofEntries(
Map.entry('0', "black"),
Map.entry('1', "dark_blue"),
Map.entry('2', "dark_green"),
Map.entry('3', "dark_aqua"),
Map.entry('4', "dark_red"),
Map.entry('5', "dark_purple"),
Map.entry('6', "gold"),
Map.entry('7', "gray"),
Map.entry('8', "dark_gray"),
Map.entry('9', "blue"),
Map.entry('a', "green"),
Map.entry('b', "aqua"),
Map.entry('c', "red"),
Map.entry('d', "light_purple"),
Map.entry('e', "yellow"),
Map.entry('f', "white"),
Map.entry('k', "obfuscated"),
Map.entry('l', "bold"),
Map.entry('m', "strikethrough"),
Map.entry('n', "underlined"),
Map.entry('o', "italic"),
Map.entry('r', "reset")
);

/**
* Converts legacy colors to MiniMessage tags so existing configurations remain readable.
* MiniMessage tags, click arguments and URLs are preserved; hover text is converted separately.
*
* @param message text containing MiniMessage, legacy colors or both
* @return text ready to deserialize with MiniMessage
*/
public static String colorMiniMessage(String message) {
Matcher matcher = COLOR_PATTERN.matcher(message);
StringBuilder result = new StringBuilder();
while (matcher.find()) {
String replacement = matcher.group();
if (matcher.group(1) != null) {
String prefix = "<hover:show_text:";
int start = matcher.start();
while (start > 0 && message.charAt(start - 1) == '\\') start--;
boolean escaped = (matcher.start() - start) % 2 != 0;
if (!escaped && replacement.regionMatches(true, 0, prefix, 0, prefix.length())) {
String text = replacement.substring(prefix.length(), replacement.length() - 1);
String converted = colorMiniMessage(text);
if (!text.equals(converted)) {
if (!text.startsWith("'") && !text.startsWith("\"")) {
converted = "\"" + converted.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
replacement = replacement.substring(0, prefix.length()) + converted + ">";
}
}
} else if (matcher.group(2) != null) {
replacement = "<#" + matcher.group(2).substring(2).replace("§", "") + ">";
} else if (matcher.group(3) != null) {
replacement = "<#" + matcher.group(3) + ">";
} else {
replacement = "<" + COLORS_MAPPINGS.get(Character.toLowerCase(matcher.group(4).charAt(0))) + ">";
}
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
}
matcher.appendTail(result);
return result.toString();
}

public static int getStringLength(String message) {
return getStringLength(MiniMessage.miniMessage().deserialize(message), false);
return getStringLength(MiniMessage.miniMessage().deserialize(colorMiniMessage(message)), false);
}

private static int getStringLength(Component component, boolean inheritedBold) {
Expand All @@ -32,11 +105,11 @@ private static int getStringLength(Component component, boolean inheritedBold) {
}

public static String getPlainText(String input) {
return PlainTextComponentSerializer.plainText().serialize(MiniMessage.miniMessage().deserialize(input));
return PlainTextComponentSerializer.plainText().serialize(MiniMessage.miniMessage().deserialize(colorMiniMessage(input)));
}

/**
* @deprecated Use {@link #getPlainText(String)}. MiniMessage formatting is stripped from the input.
* @deprecated Use {@link #getPlainText(String)}. MiniMessage and legacy formatting are stripped from the input.
*/
@Deprecated(forRemoval = false)
public static String removeColorCodes(String input) {
Expand Down
59 changes: 31 additions & 28 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
# Idées :

- Ajouter un broadcast de message centré avec le support des `\n` pour ajouter plusieurs lignes
- Ajouter une option pour désactiver la tabulation des joueurs hors ligne
- Ajouter un placeholder pour transformer les caractères en lettre spécial
- Ajouter une commande type `/oplist` pour afficher la liste des opérateurs du serveur (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/4)
- Améliorer la commande `/nightvision` pour qu'elle soit persistente lors d'un kill et de déconnexion/reconnexion du joueur (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/4)
- Ajouter la possibilité d'utiliser la commande `/invsee` sur les joueurs hors ligne
- Ajouter la commande `/nick` pour changer le pseudo d'un joueur (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/161)
- Rajouter une permission pour limiter l'utilisation des couleurs
- Rajouter une permission pour que le pseudo à l'output du /nick (raw) soit le même (sans prendre en compte les couleurs) que le pseudo initial
- Grosse refonte des hologrammes :
- Rajouter les hologrammes à double face (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/27)
- Rajouter les hologrammes "cliquables" avec une hitbox pour créer des interactions
- Rajouter les hologrammes avec de la pagination (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/40)
- Ajouter une API complète pour que les développeurs puissent se hook à zEssentials pour créer leurs hologrammes
- Rajouter la commande `/signedit` pour éditer une pancarte juste en la regardant
- Utiliser les dialogs là où c'est possible dans les versions récentes (entrer du texte...)
- Faire en sorte que si le NMS d'une version n'est pas encore ajouté, ça fallback vers la version précédente disponible
- Ajouter un module de TAB (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/147)
- Ajouter un module pour pour désactiver l'apparition des phantoms pour un joueur (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/147)
- Ajouter deux nouvelles permissions distinctes pour désactiver la perte d'inventaire et la perte d'expérience à la mort du joueur
- Ajouter un module de fly temporaire avec une commande pour give/take du temps
- Dans le module du chat, rajouter deux nouveaux placeholders pour montrer le contenu de son inventaire et le contenu de son coffre de l'ender (**ISSUE:** https://github.com/Maxlego08/zEssentials/issues/127)
- Dans les informations au démarrage, indiquer la liste des modules qui sont activés/désactivés
- Ajouter la commande `/zessentials modules` pour connaître la liste des modules qui sont activés/désactivés
- Ajouter pleinement le support de "VaultUnlocked: https://github.com/TheNewEconomy/VaultUnlocked"
- Lorsqu'un module est désactivé, faire en sorte qu'il soit complètement désactiver sur le serveur (commandes, status, checks...)
- Ajouter un broadcast centré, avec la prise en charge de `\n` pour afficher plusieurs lignes.
- Ajouter une option pour exclure les joueurs hors ligne de la tabulation.
- Ajouter un placeholder pour convertir le texte en lettres stylisées (smallcaps).
- Ajouter une commande `/oplist` pour consulter la liste des opérateurs du serveur ([issue #4](https://github.com/Maxlego08/zEssentials/issues/4)).
- Conserver l'effet de vision nocturne après la mort et la reconnexion du joueur ([issue #4](https://github.com/Maxlego08/zEssentials/issues/4)).
- Permettre à la commande `/invsee` et `/endersee` d'afficher l'inventaire des joueurs hors ligne.
- Ajouter une commande `/nick` pour modifier le pseudo d'un joueur ([issue #161](https://github.com/Maxlego08/zEssentials/issues/161)).
- Ajouter une permission pour limiter l'utilisation des couleurs dans les pseudos.
- Ajouter une permission pour afficher le pseudo d'origine sans tenir compte des couleurs.
- Revoir le système d'hologrammes :
- Ajouter l'affichage double face ([issue #27](https://github.com/Maxlego08/zEssentials/issues/27)).
- Ajouter des zones cliquables permettant de déclencher des interactions.
- Ajouter la pagination ([issue #40](https://github.com/Maxlego08/zEssentials/issues/40)).
- Fournir une API permettant aux développeurs de créer leurs propres hologrammes.
- Ajouter une commande `/signedit` pour modifier la pancarte visée.
- Utiliser les dialogs de zMenu lorsque c'est possible, notamment pour les champs de saisie et les curseurs.
- Prévoir un fallback vers le NMS de la version précédente lorsqu'aucun module n'existe pour la version du serveur.
- Ajouter un module de TAB configurable ([issue #147](https://github.com/Maxlego08/zEssentials/issues/147)).
- Ajouter une option permettant à chaque joueur de désactiver l'apparition des phantoms ([issue #147](https://github.com/Maxlego08/zEssentials/issues/147)).
- Ajouter des permissions distinctes pour conserver l'inventaire et l'expérience à la mort.
- Vérifier le fonctionnement du module de fly temporaire.
- Ajouter au chat des placeholders affichant l'inventaire et le coffre de l'Ender du joueur ([issue #127](https://github.com/Maxlego08/zEssentials/issues/127)).
- Ajouter `/zessentials modules` pour afficher les modules activés et désactivés.
- Afficher l'état des modules au démarrage du plugin.
- Désactiver complètement les modules désactivés, y compris leurs commandes et leurs vérifications.
- Compléter la prise en charge de [VaultUnlocked](https://github.com/TheNewEconomy/VaultUnlocked) en utilisant ses méthodes optimisées.
- Vérifier le fonctionnement de zEssentials sur Folia.
- Ajouter une commande pour créer des bossbars personnalisées et configurer leur couleur, leur style, leur texte et leur durée.
- Ajouter un système de récompenses fondé sur le temps de jeu, avec des gains périodiques ou uniques.

# Unreleased

Expand All @@ -35,7 +38,7 @@
- Unified item lookup and suggestions for give, mailbox and vault commands; feedback uses translated item names and preserves custom names
- Fixed custom item delivery to offline mail recipients and stale custom items after reloads
- Use the published zMenu **1.1.2.0** API for skulls and kit conversion/saving; the local JAR remains required for item persistence and sound helpers
- Migrated messages and configuration examples to native MiniMessage hex colors, including scoreboard animations; existing files must replace legacy `&`, `§` and bare hex color codes
- Migrated bundled messages and configuration examples to MiniMessage; legacy `&`, `§` and hex color codes remain supported in existing configurations and player messages
- Fixed MiniMessage text serialization, italic formatting and bold text alignment, and preserved click events with Adventure `5.x`
- Fixed feedback placeholders, duplicate Death Message registration, version comparisons and incomplete downloads; existing `command-enchant-error-enchant` messages must use `%enchant%`
- Updated Gradle to `9.8.2`, Shadow to `9.6.1` and paperweight to `2.0.0-beta.24`; centralized dependency versions and retained Java 21 for common code and Java 25 for 26.x NMS
Expand Down Expand Up @@ -746,4 +749,4 @@ economies:
# 1.0.0.1

- Added the command `/ess convert CMI`, allows converting the database from CMI to zEssentials.
Documentation: https://zessentials.groupez.dev/getting-started/convert#cmi
Documentation: https://zessentials.groupez.dev/getting-started/convert#cmi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import fr.maxlego08.essentials.api.ConfigurationFile;
import fr.maxlego08.essentials.api.messages.EssentialsMessage;
import fr.maxlego08.essentials.api.messages.Message;
import fr.maxlego08.essentials.api.messages.MessageHelper;
import fr.maxlego08.essentials.api.messages.MessageType;
import fr.maxlego08.essentials.api.messages.messages.BossBarMessage;
import fr.maxlego08.essentials.api.messages.messages.ClassicMessage;
Expand Down Expand Up @@ -229,7 +230,7 @@ private void loadMessages(YamlConfiguration configuration) {
}

private String replaceMessagesColors(String message) {
return this.plugin.getConfiguration().getMessageColors().stream().reduce(message, (msg, color) -> msg.replace(color.key(), "<c:" + color.color() + ">"), (msg1, msg2) -> msg1);
return this.plugin.getConfiguration().getMessageColors().stream().reduce(message, (msg, color) -> msg.replace(color.key(), MessageHelper.colorMiniMessage(color.color())), (msg1, msg2) -> msg1);
}

private List<String> getMessage(Map<?, ?> map) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.maxlego08.essentials.module.modules.kit;

import fr.maxlego08.essentials.api.kit.Kit;
import fr.maxlego08.essentials.api.messages.MessageHelper;
import fr.maxlego08.menu.api.MenuItemStack;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
Expand All @@ -18,7 +19,7 @@ public class KitInventoryHolder implements InventoryHolder {
public KitInventoryHolder(Player player, Kit kit) {
this.player = player;
this.kit = kit;
this.inventory = Bukkit.createInventory(this, 54, MiniMessage.miniMessage().deserialize("Kit Editor: " + kit.getName()));
this.inventory = Bukkit.createInventory(this, 54, MiniMessage.miniMessage().deserialize(MessageHelper.colorMiniMessage("Kit Editor: " + kit.getName())));

for (int index = 0; index != Math.min(54, kit.getMenuItemStacks().size()); index++) {
MenuItemStack menuItemStack = kit.getMenuItemStacks().get(index);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import fr.maxlego08.essentials.api.cache.SimpleCache;
import fr.maxlego08.essentials.api.commands.Permission;
import fr.maxlego08.essentials.api.messages.Message;
import fr.maxlego08.essentials.api.messages.MessageHelper;
import fr.maxlego08.essentials.api.messages.messages.BossBarMessage;
import fr.maxlego08.essentials.api.messages.messages.TitleMessage;
import fr.maxlego08.essentials.api.utils.TagPermission;
Expand Down Expand Up @@ -94,18 +95,18 @@ public void update(ItemMeta itemMeta, List<String> lore, Player offlinePlayer) {

@Override
public Inventory createInventory(String inventoryName, int size, InventoryHolder inventoryHolder) {
Component component = this.cache.get(inventoryName, () -> this.MINI_MESSAGE.deserialize(inventoryName));
Component component = this.cache.get(inventoryName, () -> this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(inventoryName)));
return Bukkit.createInventory(inventoryHolder, size, component);
}

@Override
public Component getComponent(String message) {
return this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(message));
return this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(message)));
}

@Override
public Component getComponent(String message, TagResolver tagResolver) {
return this.MINI_MESSAGE.deserialize(message, tagResolver);
return this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(message), tagResolver);
}

@Override
Expand All @@ -131,21 +132,21 @@ private TagResolver getTagResolver(Player player, TagResolver... tagResolvers) {

public Component translateText(Player player, String message, TagResolver... tagResolvers) {
var tagResolver = getTagResolver(player, tagResolvers);
return MiniMessage.builder().tags(tagResolver).build().deserialize(message);
return MiniMessage.builder().tags(tagResolver).build().deserialize(MessageHelper.colorMiniMessage(message));
}

@Override
public void sendActionBar(Player sender, String message) {
Component component = this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(message));
Component component = this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(message)));
sender.sendActionBar(component);
}

@Override
public void sendMessage(CommandSender sender, String message) {
if (sender instanceof Player player) {
sender.sendMessage(this.MINI_MESSAGE.deserialize(papi(message, player)));
sender.sendMessage(this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(papi(message, player))));
} else {
Component component = this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(message));
Component component = this.cache.get(message, () -> this.MINI_MESSAGE.deserialize(MessageHelper.colorMiniMessage(message)));
sender.sendMessage(component);
}
}
Expand Down Expand Up @@ -223,7 +224,7 @@ public void changeSignColor(SignChangeEvent event) {
if (line == null) continue;

var plainText = plainTextSerializer.serialize(line);
event.line(i, miniMessage.deserialize(plainText));
event.line(i, miniMessage.deserialize(MessageHelper.colorMiniMessage(plainText)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerLoginEvent;

Expand All @@ -33,6 +34,8 @@ public void disallow(PlayerLoginEvent event, PlayerLoginEvent.Result result, Mes
public void sendPrivateMessage(User user, PrivateMessage privateMessage, Message message, String content) {
PaperComponent paperComponent = (PaperComponent) this.componentMessage;
var messageAsString = papi(getMessage(message.getMessageAsString(), "%target%", privateMessage.username()), user.getPlayer());
user.getPlayer().sendMessage(paperComponent.getComponent(messageAsString, TagResolver.resolver("message", Tag.inserting(Component.text(content)))));
Player sender = message == Message.COMMAND_MESSAGE_OTHER ? Bukkit.getPlayer(privateMessage.uuid()) : user.getPlayer();
Component component = sender == null ? Component.text(content) : paperComponent.translateText(sender, content);
user.getPlayer().sendMessage(paperComponent.getComponent(messageAsString, TagResolver.resolver("message", Tag.inserting(component))));
}
}
Loading