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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ run/

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle/wrapper/gradle-wrapper.jar
!**/libs/*.jar
CLAUDE.md
.claude
updates.md
Expand Down
1 change: 0 additions & 1 deletion API/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ rootProject.extra.properties["sha"]?.let { sha ->

dependencies {
compileOnly(libs.paper.api)
compileOnly(files("../libs/zMenu-1.1.2.0.jar"))

implementation(libs.sarah)
implementation(libs.folialib)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,16 @@ public interface EssentialsPlugin extends Plugin {
ModuleManager getModuleManager();

/**
* Gets the inventory manager for managing inventories.
*
* @return The inventory manager.
* Gets the inventory manager provided by the required zMenu plugin.
* <p>
* In addition to inventories, zMenu 1.1.2.1 and later expose item serialization and
* sound creation through this service. Its item serializer preserves the format
* used by existing zEssentials mailbox, vault and item hologram data.
*
* @return The registered zMenu inventory manager.
* @see InventoryManager#serializeItemStack(org.bukkit.inventory.ItemStack)
* @see InventoryManager#deserializeItemStack(String)
* @see InventoryManager#createSoundOption(String, String, float, float)
*/
InventoryManager getInventoryManager();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package fr.maxlego08.essentials.api.mailbox;

import fr.maxlego08.essentials.api.dto.MailBoxDTO;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.menu.api.InventoryManager;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;

import java.util.Date;
Expand Down Expand Up @@ -31,14 +32,21 @@ public MailBoxItem(UUID uuid, ItemStack itemStack, Date expiredAt) {
}

/**
* Constructs a new MailBoxItem from a MailBoxDTO.
* Restores a mailbox item from its stored data using zMenu's item serializer.
* <p>
* Requires zMenu 1.1.2.1 or later to be enabled and its {@link InventoryManager}
* service to be registered. The stored item format remains compatible with
* mailbox entries saved before the API migration.
*
* @param mailBoxDTO the data transfer object containing mailbox item data
* @throws NullPointerException if the DTO is null or the inventory manager service is unavailable
* @see InventoryManager#deserializeItemStack(String)
*/
public MailBoxItem(MailBoxDTO mailBoxDTO) {
this.id = mailBoxDTO.id();
this.uuid = mailBoxDTO.unique_id();
this.itemStack = ItemStackUtils.deserializeItemStack(mailBoxDTO.itemstack());
InventoryManager inventoryManager = Bukkit.getServicesManager().load(InventoryManager.class);
this.itemStack = inventoryManager.deserializeItemStack(mailBoxDTO.itemstack());
this.expiredAt = mailBoxDTO.expired_at();
}

Expand Down Expand Up @@ -72,7 +80,7 @@ public UUID getUniqueId() {
/**
* Gets the item stack stored in the mailbox.
*
* @return the item stack
* @return the stored item, or null if no item was stored or deserialization failed
*/
public ItemStack getItemStack() {
return itemStack;
Expand Down
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ allprojects {

dependencies {
compileOnly(rootLibs.zmenu.api)
// Still needed for item persistence and sound helpers not exposed by the zMenu API.
compileOnly(files("libs/zMenu-1.1.2.0.jar"))

compileOnly(rootLibs.sarah)
compileOnly(rootLibs.folialib)
Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
- `/enchant` now uses server registry identifiers, including datapack and plugin enchantments; legacy API methods remain available and deprecated
- 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
- Require zMenu **1.1.2.1** and use its published API for items and sounds; removed the local zMenu JAR while preserving stored items and custom resource-pack sounds
- Migrated messages and configuration examples to native MiniMessage hex colors, including scoreboard animations; existing files must replace legacy `&`, `§` and bare hex color codes
- 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%`
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ mysql-connector = "26.10.0"
mariadb-connector = "3.5.6"

# Integration Hooks
zmenu-api = "1.1.2.0"
zmenu-api = "1.1.2.1"
placeholderapi = "2.12.3"
blocktracker = "1.0.6"
mythicmobs = "5.7.2"
Expand Down
Binary file removed libs/zMenu-1.1.2.0.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import fr.maxlego08.essentials.api.messages.Message;
import fr.maxlego08.essentials.module.modules.vault.VaultModule;
import fr.maxlego08.essentials.zutils.utils.commands.VCommand;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
Expand Down Expand Up @@ -51,7 +50,7 @@ protected CommandResultType perform(EssentialsPlugin plugin) {
}

VaultItemDTO vaultItemDTO = optional.get();
ItemStack itemStack = ItemStackUtils.deserializeItemStack(vaultItemDTO.item());
ItemStack itemStack = plugin.getInventoryManager().deserializeItemStack(vaultItemDTO.item());
String itemName = itemStack == null ? "unknown" : getItemName(itemStack);

message(this.sender, Message.COMMAND_VAULT_GET_SLOT_SUCCESS, "%player%", playerName, "%vault%", vaultId, "%slot%", slot, "%amount%", String.valueOf(vaultItemDTO.quantity()), "%item%", itemName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
import fr.maxlego08.essentials.api.teleportation.WorldOverride;
import fr.maxlego08.essentials.api.user.User;
import fr.maxlego08.essentials.module.ZModule;
import fr.maxlego08.essentials.zutils.utils.CustomSoundOption;
import fr.maxlego08.menu.api.sound.SoundOption;
import fr.maxlego08.menu.hooks.xseries.XSound;
import fr.maxlego08.menu.sound.ZSoundOption;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
Expand All @@ -24,7 +23,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Random;
import java.util.UUID;
Expand Down Expand Up @@ -96,15 +94,22 @@ private SoundOption loadSoundOption(YamlConfiguration configuration, String path

float volume = (float) section.getDouble("volume", 1.0);
float pitch = (float) section.getDouble("pitch", 1.0);
Optional<XSound> xSound = XSound.of(soundName);

return new ZSoundOption(xSound.orElse(null), "MASTER", soundName, pitch, volume, xSound.isEmpty());
SoundOption soundOption = this.plugin.getInventoryManager().createSoundOption(soundName, "MASTER", pitch, volume);
return soundOption == null ? new CustomSoundOption(soundName, pitch, volume) : soundOption;
}

/**
* @return the configured teleportation countdown sound, including custom resource-pack sounds,
* or null when this sound is disabled or unconfigured
*/
public SoundOption getCountdownSound() {
return countdownSound;
}

/**
* @return the configured teleportation completion sound, including custom resource-pack sounds,
* or null when this sound is disabled or unconfigured
*/
public SoundOption getCompleteSound() {
return completeSound;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@
import fr.maxlego08.essentials.api.utils.DynamicCooldown;
import fr.maxlego08.essentials.module.ZModule;
import fr.maxlego08.essentials.storage.ConfigStorage;
import fr.maxlego08.essentials.zutils.utils.CustomSoundOption;
import fr.maxlego08.essentials.zutils.utils.TimerBuilder;
import fr.maxlego08.essentials.zutils.utils.paper.PaperComponent;
import fr.maxlego08.menu.api.engine.Pagination;
import fr.maxlego08.menu.api.sound.SoundOption;
import fr.maxlego08.menu.hooks.xseries.XSound;
import fr.maxlego08.menu.sound.ZSoundOption;
import io.papermc.paper.event.player.AsyncChatEvent;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
Expand Down Expand Up @@ -114,11 +113,11 @@ public void loadConfiguration() {
this.chatDisplays.clear();
if (this.enablePing) {
SoundOption pingSoundOption = null;
// Resolve the sound cross-version via XSound. Since Paper 1.21.3+ org.bukkit.Sound is an interface
// (no longer an enum), the reflection config loader can no longer map it, so we resolve it manually.
if (this.enablePlayerPingSound && this.playerPingSound != null && !this.playerPingSound.isEmpty()) {
Optional<XSound> xSound = XSound.of(this.playerPingSound);
pingSoundOption = new ZSoundOption(xSound.orElse(null), "MASTER", this.playerPingSound, this.playerPingSoundPitch, this.playerPingSoundVolume, xSound.isEmpty());
pingSoundOption = this.plugin.getInventoryManager().createSoundOption(this.playerPingSound, "MASTER", this.playerPingSoundPitch, this.playerPingSoundVolume);
if (pingSoundOption == null) {
pingSoundOption = new CustomSoundOption(this.playerPingSound, this.playerPingSoundPitch, this.playerPingSoundVolume);
}
}
this.pingDisplay = new PlayerPingDisplay(this.plugin, this.playerPingColor, this.playerPingColorOther, pingSoundOption);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import fr.maxlego08.essentials.zutils.utils.ZUtils;
import fr.maxlego08.menu.api.exceptions.InventoryException;
import fr.maxlego08.menu.api.utils.Loader;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Material;
Expand Down Expand Up @@ -90,7 +89,7 @@ private void loadConfiguration(YamlConfiguration configuration, HologramConfigur
}

private void loadItemConfiguration(YamlConfiguration configuration, ItemHologramConfiguration itemHologramConfiguration) {
itemHologramConfiguration.setItemStack(ItemStackUtils.deserializeItemStack(configuration.getString("itemstack", "null")));
itemHologramConfiguration.setItemStack(this.plugin.getInventoryManager().deserializeItemStack(configuration.getString("itemstack", "null")));
}

private void loadBlockConfiguration(YamlConfiguration configuration, BlockHologramConfiguration blockHologramConfiguration) {
Expand Down Expand Up @@ -164,7 +163,7 @@ public void save(Hologram hologram, YamlConfiguration configuration, String path
configuration.set("block-material", blockHologramConfiguration.getMaterial().name());
} else if (hologramConfiguration instanceof ItemHologramConfiguration itemHologramConfiguration) {

configuration.set("itemstack", ItemStackUtils.serializeItemStack(itemHologramConfiguration.getItemStack()));
configuration.set("itemstack", this.plugin.getInventoryManager().serializeItemStack(itemHologramConfiguration.getItemStack()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import fr.maxlego08.essentials.api.vault.VaultResult;
import fr.maxlego08.essentials.api.vault.VaultSlotType;
import fr.maxlego08.essentials.module.ZModule;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
Expand Down Expand Up @@ -229,7 +228,7 @@ public VaultResult addNewItemToVault(Vault vault, UUID uniqueId, ItemStack curre
VaultItem newVaultItem = new ZVaultItem(slot, currentItem, quantity);
vault.getVaultItems().put(slot, newVaultItem);
int finalSlot = slot;
getStorage().createVaultItem(uniqueId, vault.getVaultId(), finalSlot, newVaultItem.getQuantity(), ItemStackUtils.serializeItemStack(currentItem));
getStorage().createVaultItem(uniqueId, vault.getVaultId(), finalSlot, newVaultItem.getQuantity(), this.plugin.getInventoryManager().serializeItemStack(currentItem));
return new VaultResult(vault, slot);
}

Expand Down Expand Up @@ -446,7 +445,7 @@ public boolean addItem(UUID uniqueId, ItemStack itemStack, long amount) {
}
VaultItem newVaultItem = new ZVaultItem(nextSlot, itemStack, amount);
vault.getVaultItems().put(nextSlot, newVaultItem);
storage.createVaultItem(uniqueId, vault.getVaultId(), nextSlot, newVaultItem.getQuantity(), ItemStackUtils.serializeItemStack(itemStack));
storage.createVaultItem(uniqueId, vault.getVaultId(), nextSlot, newVaultItem.getQuantity(), this.plugin.getInventoryManager().serializeItemStack(itemStack));
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import fr.maxlego08.essentials.api.vault.Vault;
import fr.maxlego08.essentials.api.vault.VaultManager;
import fr.maxlego08.essentials.zutils.utils.ZUtils;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.menu.api.InventoryManager;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;

import java.util.HashMap;
Expand Down Expand Up @@ -39,7 +40,8 @@ public Map<Integer, Vault> getVaults() {

@Override
public void createVault(VaultDTO vaultDTO) {
this.vaults.put(vaultDTO.vault_id(), new ZVault(vaultDTO.unique_id(), vaultDTO.vault_id(), vaultDTO.name(), ItemStackUtils.deserializeItemStack(vaultDTO.icon())));
InventoryManager inventoryManager = Bukkit.getServicesManager().load(InventoryManager.class);
this.vaults.put(vaultDTO.vault_id(), new ZVault(vaultDTO.unique_id(), vaultDTO.vault_id(), vaultDTO.name(), inventoryManager.deserializeItemStack(vaultDTO.icon())));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import fr.maxlego08.essentials.api.vault.Vault;
import fr.maxlego08.essentials.api.vault.VaultItem;
import fr.maxlego08.essentials.zutils.utils.ZUtils;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.menu.api.InventoryManager;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;

Expand Down Expand Up @@ -70,7 +71,8 @@ public void setIconItemStack(ItemStack iconItemStack) {

@Override
public void createItem(VaultItemDTO vaultItemDTO) {
this.vaultItems.put(vaultItemDTO.slot(), new ZVaultItem(vaultItemDTO.slot(), ItemStackUtils.deserializeItemStack(vaultItemDTO.item()), vaultItemDTO.quantity()));
InventoryManager inventoryManager = Bukkit.getServicesManager().load(InventoryManager.class);
this.vaultItems.put(vaultItemDTO.slot(), new ZVaultItem(vaultItemDTO.slot(), inventoryManager.deserializeItemStack(vaultItemDTO.item()), vaultItemDTO.quantity()));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import fr.maxlego08.essentials.api.dto.MailBoxDTO;
import fr.maxlego08.essentials.api.mailbox.MailBoxItem;
import fr.maxlego08.essentials.storage.database.Repository;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.sarah.DatabaseConnection;

import java.util.Date;
Expand All @@ -23,7 +22,7 @@ public List<MailBoxDTO> select(UUID uuid) {
public void insert(MailBoxItem mailBoxItem) {
this.insert(table -> {
table.uuid("unique_id", mailBoxItem.getUniqueId());
table.string("itemstack", ItemStackUtils.serializeItemStack(mailBoxItem.getItemStack()));
table.string("itemstack", this.plugin.getInventoryManager().serializeItemStack(mailBoxItem.getItemStack()));
table.object("expired_at", mailBoxItem.getExpiredAt());
}, mailBoxItem::setId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import fr.maxlego08.essentials.user.ZUser;
import fr.maxlego08.essentials.zutils.utils.StorageHelper;
import fr.maxlego08.essentials.zutils.utils.TypeSafeCache;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.sarah.*;
import fr.maxlego08.sarah.database.DatabaseType;
import fr.maxlego08.sarah.logger.JULogger;
Expand Down Expand Up @@ -821,7 +820,7 @@ public List<PlayerSlotDTO> getPlayerVaultSlots() {

@Override
public void updateVault(UUID uniqueId, Vault vault) {
async(uniqueId, () -> with(VaultRepository.class).update(uniqueId, vault.getVaultId(), vault.getName(), vault.getIconItemStack() == null ? null : ItemStackUtils.serializeItemStack(vault.getIconItemStack())));
async(uniqueId, () -> with(VaultRepository.class).update(uniqueId, vault.getVaultId(), vault.getName(), vault.getIconItemStack() == null ? null : this.plugin.getInventoryManager().serializeItemStack(vault.getIconItemStack())));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fr.maxlego08.essentials.zutils.utils;

import fr.maxlego08.menu.api.sound.SoundOption;
import fr.maxlego08.menu.hooks.xseries.XSound;
import org.bukkit.Location;
import org.bukkit.SoundCategory;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* A zMenu sound option for resource-pack keys that its sound factory cannot resolve.
* <p>
* Playback uses the configured key directly through Paper. Player sounds use the
* {@code MASTER} category; sounds played at another entity are heard in its world.
* No vanilla {@link XSound} is associated with this option.
*
* @param soundName the resource-pack sound key, for example {@code mypack:teleport}
* @param pitch the playback pitch
* @param volume the playback volume
* @see fr.maxlego08.menu.api.InventoryManager#createSoundOption(String, String, float, float)
*/
public record CustomSoundOption(String soundName, float pitch, float volume) implements SoundOption {

/**
* @return null because this option plays a custom key rather than a vanilla sound
*/
@Override
public @Nullable XSound getSound() {
return null;
}

@Override
public float getPitch() {
return this.pitch;
}

@Override
public float getVolume() {
return this.volume;
}

@Override
public boolean isCustom() {
return true;
}

@Override
public @NotNull String getSoundName() {
return this.soundName;
}

@Override
public void play(@NotNull Entity entity) {
Location location = entity.getLocation();
if (entity instanceof Player player) {
player.playSound(location, this.soundName, SoundCategory.MASTER, this.volume, this.pitch);
} else {
location.getWorld().playSound(location, this.soundName, this.volume, this.pitch);
}
}
}
Loading