) msg);
+ if (out == null) promise.setSuccess();
+ else ctx.write(out, promise);
+ }
+ }
+
+ private @Nullable > P filterOutgoing(P packet) {
+ return discardRules.contains(packet.getClass()) ? null : packet;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (!(msg instanceof Packet> packet)) {
+ super.channelRead(ctx, msg);
+ return;
+ }
+
+ @SuppressWarnings("unchecked")
+ var p = (Packet super ServerGamePacketListener>) packet;
+
+ var listener = listeners.get(packet.getClass());
+ if (listener != null) listener.offer(p);
+
+ var redirect = redirections.get(packet.getClass());
+ if (redirect != null) redirect.offer(p);
+ else super.channelRead(ctx, packet);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/network/PacketQueue.java b/Common/src/main/java/fr/maxlego08/menu/common/network/PacketQueue.java
new file mode 100644
index 000000000..35ff8127d
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/common/network/PacketQueue.java
@@ -0,0 +1,238 @@
+package fr.maxlego08.menu.common.network;
+
+import com.tcoded.folialib.wrapper.task.WrappedTask;
+import fr.maxlego08.menu.api.MenuPlugin;
+import net.minecraft.network.protocol.Packet;
+import org.bukkit.entity.Player;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class PacketQueue> {
+
+ private final Queue queue;
+ private final Map, Consumer super T>> handlers;
+ private final Consumer super T> fallback;
+ private final boolean discard;
+ private final boolean directDispatch;
+ private WrappedTask task;
+
+ private PacketQueue(Builder builder) {
+ this.queue = builder.concurrent ? new ConcurrentLinkedQueue<>() : new LinkedList<>();
+ this.handlers = Map.copyOf(builder.handlers);
+ this.fallback = builder.fallback;
+ this.discard = builder.discard;
+ this.directDispatch = builder.directDispatch;
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Queue access
+ // ──────────────────────────────────────────────────────────────
+
+ /** Raw queue — hand this to MenuPacketListener if needed. */
+ public Queue queue() {
+ return queue;
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Offer
+ // ──────────────────────────────────────────────────────────────
+
+ /**
+ * Called by MenuPacketListener on the Netty I/O thread.
+ *
+ * If {@code discard} is enabled the packet is silently dropped.
+ * If {@code directDispatch} is enabled the handler fires immediately
+ * on the Netty thread — only safe for thread-safe, Bukkit-API-free handlers.
+ * Otherwise the packet is queued for later {@link #dispatch()} on the main thread.
+ */
+ public void offer(T packet) {
+ if (discard) return;
+ if (directDispatch) {
+ dispatchSingle(packet);
+ return;
+ }
+ queue.offer(packet);
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Dispatch
+ // ──────────────────────────────────────────────────────────────
+
+ /**
+ * Drain every queued packet and fire the matching consumer.
+ * Must be called on the correct thread (main / entity thread).
+ * No-op when {@code directDispatch} is enabled since packets never queue.
+ */
+ public void dispatch() {
+ T packet;
+ while ((packet = queue.poll()) != null) dispatchSingle(packet);
+ }
+
+ /**
+ * Drain exactly one queued packet.
+ * Returns {@code true} if a packet was dispatched.
+ */
+ public boolean dispatchOne() {
+ T packet = queue.poll();
+ if (packet == null) return false;
+ dispatchSingle(packet);
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void dispatchSingle(T packet) {
+ Consumer super T> handler = handlers.get(packet.getClass());
+ if (handler != null) handler.accept(packet);
+ else if (fallback != null) fallback.accept(packet);
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Scheduling
+ // ──────────────────────────────────────────────────────────────
+
+ /**
+ * Schedules a repeating task that calls {@link #dispatch()} every tick.
+ * The queue owns the task lifecycle — call {@link #cancel()} to stop it.
+ *
+ * Accepts a scheduler function to keep PacketQueue decoupled from FoliaLib:
+ *
{@code
+ * queue.schedule(r -> plugin.getScheduler().runAtEntityTimer(player, r, 1L, 1L));
+ * }
+ */
+ public PacketQueue schedule(Function scheduler) {
+ this.task = scheduler.apply(this::dispatch);
+ return this;
+ }
+
+ /**
+ * Convenience overload when you don't need a custom interval.
+ * Schedules at 1-tick period on the player's entity thread.
+ */
+ public PacketQueue schedule(MenuPlugin plugin, Player player) {
+ return schedule(r -> plugin.getScheduler().runAtEntityTimer(player, r, 1L, 1L));
+ }
+
+ /** Cancels the scheduled dispatch task if one was started via {@link #schedule}. */
+ public void cancel() {
+ if (task != null) {
+ task.cancel();
+ task = null;
+ }
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Utility
+ // ──────────────────────────────────────────────────────────────
+
+ public boolean isEmpty() { return queue.isEmpty(); }
+ public int size() { return queue.size(); }
+ public void clear() { queue.clear(); }
+
+ // ──────────────────────────────────────────────────────────────
+ // Static factories
+ // ──────────────────────────────────────────────────────────────
+
+ /**
+ * Silently discards every packet — useful to block a packet type
+ * without processing it.
+ */
+ public static > PacketQueue empty() {
+ return new PacketQueue<>(new Builder().discard(true).concurrent(false));
+ }
+
+ /**
+ * Packets are queued but never dispatched automatically.
+ * Drain manually via {@link #dispatch()} or {@link #dispatchOne()}.
+ */
+ public static > PacketQueue unhandled() {
+ return PacketQueue.builder().build();
+ }
+
+ /**
+ * Queue with a single catch-all handler for every packet type.
+ */
+ public static > PacketQueue of(Consumer super T> handler) {
+ return PacketQueue.builder().orElse(handler).build();
+ }
+
+ /**
+ * Queue with a single typed handler.
+ */
+ public static > PacketQueue
of(Class
type, Consumer super P> handler) {
+ return PacketQueue.
builder().on(type, handler).build();
+ }
+
+ public static > Builder builder() {
+ return new Builder<>();
+ }
+
+ // ──────────────────────────────────────────────────────────────
+ // Builder
+ // ──────────────────────────────────────────────────────────────
+
+ public static final class Builder> {
+
+ private boolean concurrent = true;
+ private boolean discard = false;
+ private boolean directDispatch = false;
+ private final Map, Consumer super T>> handlers = new LinkedHashMap<>();
+ private Consumer super T> fallback;
+
+ public Builder concurrent(boolean concurrent) {
+ this.concurrent = concurrent;
+ return this;
+ }
+
+ /**
+ * When {@code true}, every incoming packet is silently dropped.
+ * No handlers are called and nothing is queued.
+ */
+ public Builder discard(boolean discard) {
+ this.discard = discard;
+ return this;
+ }
+
+ /**
+ * When {@code true}, handlers fire immediately on the Netty I/O thread
+ * instead of being queued for the main thread.
+ *
+ * Only enable this if your handlers are thread-safe and do not touch
+ * the Bukkit API. Using this with {@link org.bukkit.entity.Player}
+ * methods or any scheduler will cause corruption or crashes.
+ */
+ public Builder directDispatch(boolean directDispatch) {
+ this.directDispatch = directDispatch;
+ return this;
+ }
+
+ /**
+ * Register a typed handler for a specific packet subclass.
+ * {@code
+ * .on(ServerboundRenameItemPacket.class, p -> handleRename(p.getName()))
+ * }
+ */
+ @SuppressWarnings("unchecked")
+ public Builder on(Class type, Consumer super P> handler) {
+ handlers.put(type, (Consumer super T>) handler);
+ return this;
+ }
+
+ /**
+ * Catch-all — fires for any packet whose class has no specific handler.
+ */
+ public Builder orElse(Consumer super T> fallback) {
+ this.fallback = fallback;
+ return this;
+ }
+
+ public PacketQueue build() {
+ return new PacketQueue<>(this);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/MessageUtils.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/MessageUtils.java
index ce9596bff..0e1b9ef88 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/MessageUtils.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/MessageUtils.java
@@ -3,7 +3,7 @@
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.utils.IMessage;
import fr.maxlego08.menu.api.utils.Message;
-import fr.maxlego08.menu.common.utils.nms.NmsVersion;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
@@ -130,7 +130,7 @@ protected final Class> getNMSClass(String name) {
protected void title(Player player, String title, String subtitle, int fadeInTime, int showTime, int fadeOutTime) {
- if (NmsVersion.nmsVersion.isNewMaterial()) {
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.13"))) {
player.sendTitle(title, subtitle, fadeInTime, showTime, fadeOutTime);
return;
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/TranslationHelper.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/TranslationHelper.java
index fae8dfa37..e31066cc7 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/TranslationHelper.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/TranslationHelper.java
@@ -2,6 +2,8 @@
import org.bukkit.inventory.ItemStack;
+import java.util.Locale;
+
public abstract class TranslationHelper {
protected String getItemName(ItemStack itemStack) {
@@ -14,8 +16,8 @@ protected String getItemName(ItemStack itemStack) {
return itemStack.getItemMeta().getDisplayName();
}
- String name = itemStack.serialize().get("type").toString().replace("_", " ").toLowerCase();
- return name.substring(0, 1).toUpperCase() + name.substring(1);
+ String name = itemStack.serialize().get("type").toString().replace("_", " ").toLowerCase(Locale.ROOT);
+ return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
}
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/ZUtils.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/ZUtils.java
index 6ac42804b..f1bebfd36 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/ZUtils.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/ZUtils.java
@@ -3,9 +3,9 @@
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.utils.EnumInventory;
import fr.maxlego08.menu.api.utils.Message;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import fr.maxlego08.menu.common.enums.Permission;
import fr.maxlego08.menu.common.utils.nms.NMSUtils;
-import fr.maxlego08.menu.common.utils.nms.NmsVersion;
import fr.maxlego08.menu.zcore.logger.Logger;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
@@ -45,7 +45,7 @@ public abstract class ZUtils extends MessageUtils {
private static Material[] byId;
static {
- if (!NmsVersion.nmsVersion.isNewMaterial()) {
+ if (!MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.13"))) {
byId = new Material[0];
for (Material material : Material.values()) {
if (byId.length <= material.getId()) {
@@ -57,7 +57,7 @@ public abstract class ZUtils extends MessageUtils {
}
protected String findPlayerLocale(Player player) {
- if (NmsVersion.getCurrentVersion().getVersion() >= NmsVersion.V_1_13.getVersion()) {
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.13"))) {
try {
return player != null ? player.getLocale() : null;
} catch (Exception exception) {
@@ -213,8 +213,8 @@ protected String format(double decimal, String format) {
* @return the formatted name
*/
protected String name(String string) {
- String name = string.replace("_", " ").toLowerCase();
- return name.substring(0, 1).toUpperCase() + name.substring(1);
+ String name = string.replace("_", " ").toLowerCase(Locale.ROOT);
+ return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
}
/**
@@ -224,8 +224,8 @@ protected String name(String string) {
* @return the formatted name
*/
protected String name(Material string) {
- String name = string.name().replace("_", " ").toLowerCase();
- return name.substring(0, 1).toUpperCase() + name.substring(1);
+ String name = string.name().replace("_", " ").toLowerCase(Locale.ROOT);
+ return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1);
}
/**
@@ -452,7 +452,7 @@ private void applyTextureUrl(ItemStack itemStack, String url) {
protected Object getPrivateField(Object object, String field) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Class> clazz = object.getClass();
- Field objectField = field.equals("commandMap") ? clazz.getDeclaredField(field) : field.equals("knownCommands") ? NmsVersion.nmsVersion.isNewMaterial() ? clazz.getSuperclass().getDeclaredField(field) : clazz.getDeclaredField(field) : null;
+ Field objectField = field.equals("commandMap") ? clazz.getDeclaredField(field) : field.equals("knownCommands") ? MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.13")) ? clazz.getSuperclass().getDeclaredField(field) : clazz.getDeclaredField(field) : null;
objectField.setAccessible(true);
Object result = objectField.get(object);
objectField.setAccessible(false);
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/itemstack/MenuItemStackFromItemStack.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/itemstack/MenuItemStackFromItemStack.java
index 5edc1a389..48cb890f6 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/itemstack/MenuItemStackFromItemStack.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/itemstack/MenuItemStackFromItemStack.java
@@ -4,8 +4,8 @@
import fr.maxlego08.menu.api.InventoryManager;
import fr.maxlego08.menu.api.itemstack.Firework;
import fr.maxlego08.menu.api.itemstack.Potion;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
-import fr.maxlego08.menu.common.utils.nms.NmsVersion;
import org.bukkit.FireworkEffect;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.*;
@@ -24,7 +24,7 @@ public static ZMenuItemStack fromItemStack(InventoryManager manager, ItemStack i
menuItemStack.setMaterial(itemStack.getType().name());
int amount = itemStack.getAmount();
if (amount > 1) menuItemStack.setAmount(String.valueOf(itemStack.getAmount()));
- if (NmsVersion.getCurrentVersion().isItemLegacy()) {
+ if (MinecraftVersion.getCurrentVersion().isBefore(MinecraftVersion.parse("1.13"))) {
int durability = itemStack.getDurability();
if (durability > 0) menuItemStack.setDurability(durability);
int data = itemStack.getData().getData();
@@ -45,7 +45,7 @@ public static ZMenuItemStack fromItemStack(InventoryManager manager, ItemStack i
menuItemStack.setFlags(new ArrayList<>(itemMeta.getItemFlags()));
menuItemStack.setEnchantments(itemMeta.getEnchants());
- if (NmsVersion.getCurrentVersion().isCustomModelData() && itemMeta.hasCustomModelData()) {
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.14")) && itemMeta.hasCustomModelData()) {
menuItemStack.setModelID(itemMeta.getCustomModelData());
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackCompound.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackCompound.java
index c6e796eb8..9c9fbabb1 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackCompound.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackCompound.java
@@ -1,5 +1,7 @@
package fr.maxlego08.menu.common.utils.nms;
+import fr.maxlego08.menu.nms.NMSHandler;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils.EnumReflectionItemStack;
import org.bukkit.inventory.ItemStack;
@@ -12,14 +14,14 @@ public class ItemStackCompound {
// Static block to initialize the itemStackCompound based on the NmsVersion
static {
- fr.maxlego08.menu.common.utils.nms.NmsVersion nmsVersion = fr.maxlego08.menu.common.utils.nms.NmsVersion.nmsVersion;
- if (nmsVersion == fr.maxlego08.menu.common.utils.nms.NmsVersion.V_1_18_2) {
+ MinecraftVersion currentVersion = MinecraftVersion.getCurrentVersion();
+ if (currentVersion.equals(MinecraftVersion.parse("1.18.2"))) {
itemStackCompound = new ItemStackCompound(EnumReflectionCompound.V1_18_2);
- } else if (nmsVersion.getVersion() >= 1200) {
+ } else if (currentVersion.isAtLeast(MinecraftVersion.parse("1.12"))) {
itemStackCompound = new ItemStackCompound(EnumReflectionCompound.V1_12);
- } else if (nmsVersion.getVersion() >= 1190) {
+ } else if (currentVersion.isAtLeast(MinecraftVersion.parse("1.19"))) {
itemStackCompound = new ItemStackCompound(EnumReflectionCompound.V1_19);
- } else if (nmsVersion.getVersion() >= 1170) {
+ } else if (currentVersion.isAtLeast(MinecraftVersion.parse("1.17"))) {
itemStackCompound = new ItemStackCompound(EnumReflectionCompound.V1_17);
} else itemStackCompound = new ItemStackCompound(EnumReflectionCompound.V1_8_8);
}
@@ -29,7 +31,7 @@ public class ItemStackCompound {
/**
* Constructs an ItemStackCompound instance based on the given EnumReflectionCompound.
*
- * @param reflection The EnumReflectionCompound representing the NBT tag reflection version.
+ * @param reflection The EnumReflectionCompound representing the NBT tag reflection value.
*/
public ItemStackCompound(EnumReflectionCompound reflection) {
super();
@@ -77,6 +79,8 @@ public ItemStack applyCompound(ItemStack itemStack, Object compoundObject) throw
* @return The modified ItemStack.
*/
public ItemStack setString(ItemStack itemStack, String key, String value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setString(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
if (compoundObject == null) return null;
@@ -97,6 +101,8 @@ public ItemStack setString(ItemStack itemStack, String key, String value) {
* @return The string value.
*/
public String getString(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getString(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
if (compoundObject == null) return null;
@@ -117,6 +123,8 @@ public String getString(ItemStack itemStack, String key) {
* @return The double value.
*/
public double getDouble(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getDouble(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
if (compoundObject == null) return 0;
@@ -136,6 +144,8 @@ public double getDouble(ItemStack itemStack, String key) {
* @return The long value.
*/
public long getLong(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getLong(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
if (compoundObject == null) return 0;
@@ -154,6 +164,8 @@ public long getLong(ItemStack itemStack, String key) {
* @return The integer value.
*/
public int getInt(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getInt(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
if (compoundObject == null) return 0;
@@ -173,6 +185,8 @@ public int getInt(ItemStack itemStack, String key) {
* @return The float value.
*/
public float getFloat(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getFloat(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
return (float) compoundObject.getClass().getMethod(this.reflection.getMethodGetFloat(), String.class).invoke(compoundObject, new Object[]{key});
@@ -191,6 +205,8 @@ public float getFloat(ItemStack itemStack, String key) {
* @return The boolean value.
*/
public boolean getBoolean(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.getBoolean(itemStack, key);
try {
Object compoundObject = this.getCompound(itemStack);
return (boolean) compoundObject.getClass().getMethod(this.reflection.getMethodGetBoolean(), String.class).invoke(compoundObject, new Object[]{key});
@@ -210,6 +226,8 @@ public boolean getBoolean(ItemStack itemStack, String key) {
* @return The modified ItemStack.
*/
public ItemStack setInt(ItemStack itemStack, String key, int value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setInt(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
compoundObject.getClass().getMethod(this.reflection.getMethodSetInt(), String.class, int.class).invoke(compoundObject, key, value);
@@ -230,6 +248,8 @@ public ItemStack setInt(ItemStack itemStack, String key, int value) {
* @return The modified ItemStack.
*/
public ItemStack setLong(ItemStack itemStack, String key, long value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setLong(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
compoundObject.getClass().getMethod(this.reflection.getMethodSetLong(), String.class, long.class).invoke(compoundObject, key, value);
@@ -250,6 +270,8 @@ public ItemStack setLong(ItemStack itemStack, String key, long value) {
* @return The modified ItemStack.
*/
public ItemStack setFloat(ItemStack itemStack, String key, float value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setFloat(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
compoundObject.getClass().getMethod(this.reflection.getMethodSetFloat(), String.class, float.class).invoke(compoundObject, key, value);
@@ -270,6 +292,8 @@ public ItemStack setFloat(ItemStack itemStack, String key, float value) {
* @return The modified ItemStack.
*/
public ItemStack setBoolean(ItemStack itemStack, String key, boolean value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setBoolean(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
@@ -291,6 +315,8 @@ public ItemStack setBoolean(ItemStack itemStack, String key, boolean value) {
* @return The modified ItemStack.
*/
public ItemStack setDouble(ItemStack itemStack, String key, double value) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.setDouble(itemStack, key, value);
try {
Object compoundObject = this.getCompound(itemStack);
compoundObject.getClass().getMethod(this.reflection.getMethodSetDouble(), String.class, double.class).invoke(compoundObject, key, value);
@@ -310,6 +336,8 @@ public ItemStack setDouble(ItemStack itemStack, String key, double value) {
* @return True if the key is present, false otherwise.
*/
public boolean isKey(ItemStack itemStack, String key) {
+ NMSHandler handler = NMSProvider.getHandler();
+ if (handler != null) return handler.hasKey(itemStack, key);
try {
Object nbttagCompound = this.getCompound(itemStack);
if (nbttagCompound == null) return false;
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackUtils.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackUtils.java
index cdd70f663..ad48e2956 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackUtils.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/ItemStackUtils.java
@@ -1,6 +1,7 @@
package fr.maxlego08.menu.common.utils.nms;
import fr.maxlego08.menu.api.configuration.Configuration;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import fr.maxlego08.menu.common.utils.Base64;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
@@ -13,8 +14,9 @@
public class ItemStackUtils {
- private static final fr.maxlego08.menu.common.utils.nms.NmsVersion NMS_VERSION = fr.maxlego08.menu.common.utils.nms.NmsVersion.nmsVersion;
+ private static final NmsVersion NMS_VERSION = NmsVersion.nmsVersion;
private static final Map itemStackSerialized = new HashMap<>();
+ private static final MinecraftVersion MINECRAFT_VERSION = MinecraftVersion.getCurrentVersion();
public static String serializeItemStack(ItemStack paramItemStack) {
@@ -26,7 +28,7 @@ public static String serializeItemStack(ItemStack paramItemStack) {
return itemStackSerialized.get(paramItemStack);
}
- if (fr.maxlego08.menu.common.utils.nms.NmsVersion.getCurrentVersion().isAttributItemStack()) {
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.20.5"))) {
return Base64ItemStack.encode(paramItemStack);
}
@@ -67,7 +69,7 @@ public static ItemStack deserializeItemStack(String paramString) {
return null;
}
- if (fr.maxlego08.menu.common.utils.nms.NmsVersion.getCurrentVersion().isAttributItemStack()) {
+ if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.20.5"))) {
return Base64ItemStack.decode(paramString);
}
@@ -83,7 +85,7 @@ public static ItemStack deserializeItemStack(String paramString) {
ItemStack localItemStack = null;
Object localObject2 = null;
try {
- if (fr.maxlego08.menu.common.utils.nms.NmsVersion.nmsVersion == fr.maxlego08.menu.common.utils.nms.NmsVersion.V_1_20_4) {
+ if (NmsVersion.nmsVersion == NmsVersion.V_1_20_4) {
DataInputStream datainputstream = new DataInputStream(
new BufferedInputStream(new GZIPInputStream(localByteArrayInputStream)));
@@ -96,10 +98,10 @@ public static ItemStack deserializeItemStack(String paramString) {
.invoke(null, localByteArrayInputStream);
}
- if (NMS_VERSION == fr.maxlego08.menu.common.utils.nms.NmsVersion.V_1_11 || NMS_VERSION == fr.maxlego08.menu.common.utils.nms.NmsVersion.V_1_12) {
+ if (NMS_VERSION == NmsVersion.V_1_11 || NMS_VERSION == NmsVersion.V_1_12) {
Constructor> localConstructor = localClass2.getConstructor(localClass1);
localObject2 = localConstructor.newInstance(localObject1);
- } else if (!NMS_VERSION.isItemLegacy()) {
+ } else if (MINECRAFT_VERSION.isAtMost(MinecraftVersion.parse("1.13"))) {
localObject2 = localClass2.getMethod("a", new Class[]{localClass1}).invoke(null,
localObject1);
} else {
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSProvider.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSProvider.java
new file mode 100644
index 000000000..9354abbb5
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSProvider.java
@@ -0,0 +1,61 @@
+package fr.maxlego08.menu.common.utils.nms;
+
+import fr.maxlego08.menu.api.utils.ReflectionsCache;
+import fr.maxlego08.menu.nms.NMSHandler;
+import fr.maxlego08.menu.nms.NMSVersion;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
+import fr.maxlego08.menu.zcore.logger.Logger;
+import org.bukkit.Bukkit;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.reflections.Reflections;
+
+import java.util.Set;
+
+public class NMSProvider {
+
+ private static NMSHandler handler;
+
+ public static NMSHandler getHandler() {
+ if (handler == null) {
+ handler = loadHandler();
+ }
+ return handler;
+ }
+
+ private static NMSHandler loadHandler() {
+ MinecraftVersion current = MinecraftVersion.getCurrentVersion();
+ JavaPlugin plugin = (JavaPlugin) Bukkit.getPluginManager().getPlugin("zMenu");
+ if (plugin == null) return null;
+
+ Reflections reflections = ReflectionsCache.getInstance().getOrCreate(plugin, "fr.maxlego08.menu.nms");
+ Set> candidates = reflections.getTypesAnnotatedWith(NMSVersion.class);
+
+ Class extends NMSHandler> bestMatch = null;
+ MinecraftVersion bestVersion = null;
+
+ for (Class> clazz : candidates) {
+ if (!NMSHandler.class.isAssignableFrom(clazz)) continue;
+
+ NMSVersion annotation = clazz.getAnnotation(NMSVersion.class);
+ MinecraftVersion required = MinecraftVersion.parse(annotation.value());
+
+ if (current.isAtLeast(required)) {
+ if (bestVersion == null || required.isAfter(bestVersion)) {
+ bestVersion = required;
+ bestMatch = (Class extends NMSHandler>) clazz;
+ }
+ }
+ }
+
+ if (bestMatch != null) {
+ try {
+ return bestMatch.getDeclaredConstructor().newInstance();
+ } catch (Exception e) {
+ Logger.info("Could not instantiate NMS handler " + bestMatch.getName() + ": " + e.getMessage());
+ }
+ }
+
+ return null;
+ }
+}
+
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSUtils.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSUtils.java
index df76458f0..90642df9b 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSUtils.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NMSUtils.java
@@ -5,9 +5,9 @@
public class NMSUtils {
/**
- * Get minecraft serveur version
+ * Get minecraft serveur value
*
- * @return version
+ * @return value
*/
public static double getNMSVersion() {
if (version != 0)
@@ -24,7 +24,7 @@ public static double getNMSVersion() {
}
/**
- * Check if minecraft version has shulker
+ * Check if minecraft value has shulker
*
* @return boolean
*/
@@ -33,7 +33,7 @@ public static boolean hasShulker() {
}
/**
- * Check if minecraft version has barrel
+ * Check if minecraft value has barrel
*
* @return booleab
*/
@@ -44,7 +44,7 @@ public static boolean hasBarrel() {
}
/**
- * check if version is granther than 1.13
+ * check if value is granther than 1.13
*
* @return boolean
*/
@@ -53,7 +53,7 @@ public static boolean isNewVersion() {
}
/**
- * Check if version has one hand
+ * Check if value has one hand
*
* @return boolean
*/
@@ -62,7 +62,7 @@ public static boolean isOneHand() {
}
/**
- * Check is version is minecraft 1.7
+ * Check is value is minecraft 1.7
*
* @return boolean
*/
@@ -71,7 +71,7 @@ public static boolean isVeryOldVersion() {
} public static double version = getNMSVersion();
/**
- * Check if version has itemmeta unbreakable
+ * Check if value has itemmeta unbreakable
*
* @return boolean
*/
@@ -80,7 +80,7 @@ public static boolean isUnbreakable() {
}
/**
- * Check if version is old version of minecraft with old material system
+ * Check if value is old value of minecraft with old material system
*
* @return boolean
*/
@@ -90,7 +90,7 @@ public static boolean isOldVersion() {
}
/**
- * Check if server vesion is new version
+ * Check if server vesion is new value
*
* @return boolean
*/
@@ -101,7 +101,7 @@ public static boolean isNewNMSVersion() {
}
/**
- * Allows to check if the version has the colors in hex
+ * Allows to check if the value has the colors in hex
*
* @return boolean
*/
@@ -111,7 +111,7 @@ public static boolean isHexColor() {
}
/**
- * Allows to check if the version has the colors in hex
+ * Allows to check if the value has the colors in hex
*
* @return boolean
*/
@@ -121,7 +121,7 @@ public static boolean isComponentColor() {
}
/**
- * Check if server version is new version
+ * Check if server value is new value
*
* @return boolean
*/
@@ -133,7 +133,7 @@ public static boolean isNewNBTVersion() {
}
/**
- * Check if server version is new version
+ * Check if server value is new value
*
* @return boolean
*/
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NmsVersion.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NmsVersion.java
index 9d27dc7e0..db2e67b53 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NmsVersion.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/nms/NmsVersion.java
@@ -1,5 +1,6 @@
package fr.maxlego08.menu.common.utils.nms;
+import fr.maxlego08.menu.api.utils.version.MinecraftVersion;
import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.Bukkit;
@@ -60,9 +61,7 @@ public enum NmsVersion {
V_1_21_10(12110),
V_1_21_11(12111),
- UNKNOWN(Integer.MAX_VALUE)
-
- ;
+ UNKNOWN(Integer.MAX_VALUE);
public static final NmsVersion nmsVersion = getNmsVersion();
private final int version;
@@ -75,12 +74,14 @@ public enum NmsVersion {
* Gets the current version of the Bukkit server.
*
* @return The NmsVersion instance corresponding to the current version.
+ * @deprecated Use {@link MinecraftVersion#getCurrentVersion()} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public static NmsVersion getCurrentVersion() {
return nmsVersion;
}
- private static NmsVersion getNmsVersion(){
+ private static NmsVersion getNmsVersion() {
Matcher matcher = Pattern.compile("(?\\d+\\.\\d+)(?\\.\\d+)?").matcher(Bukkit.getBukkitVersion());
int currentVersion = matcher.find() ? Integer.parseInt(matcher.group("version").replace(".", "") + (matcher.group("patch") != null ? matcher.group("patch").replace(".", "") : "0")) : 0;
@@ -93,11 +94,11 @@ private static NmsVersion getNmsVersion(){
if (currentVersion > highestSupportedVersionEnum.version) {
Logger.info(String.format(
- "Running Minecraft %s (newer than highest supported version %s). " +
- "Please report this version to help us add support. " +
- "Check for plugin updates if you experience issues.",
- currentVersion,
- highestSupportedVersionEnum.name()
+ "Running Minecraft %s (newer than highest supported version %s). " +
+ "Please report this version to help us add support. " +
+ "Check for plugin updates if you experience issues.",
+ currentVersion,
+ highestSupportedVersionEnum.name()
), Logger.LogType.WARNING);
return UNKNOWN;
}
@@ -116,161 +117,218 @@ private static NmsVersion getNmsVersion(){
}
/**
- * Checks if the current version supports PlayerProfiles.
- *
- * @return True if PlayerProfiles are supported, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.18.1"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean hasPlayerProfiles() {
return this.version >= NmsVersion.V_1_18_1.version;
}
/**
- * Checks if the current version uses obfuscated names.
- *
- * @return True if names are obfuscated, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.17"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean hasObfuscatedNames() {
return this.version >= NmsVersion.V_1_17.version;
}
/**
- * Checks if the current version supports components.
- *
- * @param isPaper True if the server uses Paper, else False.
- * @return True if components are supported, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.16.5"))} instead (combined with your Paper check).
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isComponent(boolean isPaper) {
return isPaper && this.version >= NmsVersion.V_1_16_5.version;
}
/**
- * Checks if the current version is a legacy item version.
- *
- * @return True if the version is legacy, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isBefore(MinecraftVersion.parse("1.13"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isItemLegacy() {
return this.version < NmsVersion.V_1_13.version;
}
/**
- * Checks if the current version supports PersistentDataContainer.
- *
- * @return True if PersistentDataContainer is supported, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.14"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isPdcVersion() {
return this.version >= NmsVersion.V_1_14.version;
}
/**
- * Checks if the current version is a legacy version for Skull owners.
- *
- * @return True if the version is legacy, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtMost(MinecraftVersion.parse("1.12"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isSkullOwnerLegacy() {
return this.version <= NmsVersion.V_1_12.version;
}
/**
- * Checks if the current version supports CustomModelData.
- *
- * @return True if CustomModelData is supported, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.14"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isCustomModelData() {
return this.version >= NmsVersion.V_1_14.version;
}
/**
- * Checks if the current version is a hexadecimal version.
- *
- * @return True if the version is hexadecimal, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.16"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isHexVersion() {
return this.version >= NmsVersion.V_1_16.version;
}
/**
- * Checks if the current version is an Attribute version.
- *
- * @return True if the version is Attribute, else False.
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAfter(MinecraftVersion.parse("1.8.8"))} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isAttributeVersion() {
return this.version != NmsVersion.V_1_8_8.version;
}
/**
- * Gets the version number associated with the enumeration.
- *
- * @return The version number.
+ * @deprecated Use {@link MinecraftVersion#getMinor()} or comparisons via {@link MinecraftVersion} instead.
*/
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public int getVersion() {
return this.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.20.5"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isAttributItemStack() {
return this.version >= NmsVersion.V_1_20_5.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().equals(MinecraftVersion.parse("1.8.8"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isOneHand() {
return this.version == NmsVersion.V_1_8_8.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.14"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isBarrel() {
return this.version >= V_1_14.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.9"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isShulker() {
return this.version >= V_1_9.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.13"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewMaterial() {
return this.version >= V_1_13.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.18"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewNBTVersion() {
return this.version >= V_1_18.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.20"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewHeadApi() {
return this.version >= V_1_20.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.17"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewNMSVersion() {
return this.version >= V_1_17.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.11"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_11OrNewer() {
return this.version >= V_1_11.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.12"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_12OrNewer() {
return this.version >= V_1_12.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewItemStackAPI() {
return this.version >= V_1_21.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.4"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isNewItemModelAPI() {
return this.version >= V_1_21_4.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.7"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean isDialogsVersion() {
return this.version >= V_1_21_7.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.2"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_21_2OrNewer() {
return this.version >= V_1_21_2.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.5"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_21_5OrNewer() {
return this.version >= V_1_21_5.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.9"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_21_9OrNewer() {
return this.version >= V_1_21_9.version;
}
+ /**
+ * @deprecated Use {@code MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.21.11"))} instead.
+ */
+ @Deprecated(since = "1.1.1.4", forRemoval = true)
public boolean is1_21_11OrNewer() {
return this.version >= V_1_21_11.version;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/fr/maxlego08/menu/inventory/VInventory.java b/Common/src/main/java/fr/maxlego08/menu/inventory/VInventory.java
similarity index 84%
rename from src/main/java/fr/maxlego08/menu/inventory/VInventory.java
rename to Common/src/main/java/fr/maxlego08/menu/inventory/VInventory.java
index d83dc1fff..174902cbd 100644
--- a/src/main/java/fr/maxlego08/menu/inventory/VInventory.java
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/VInventory.java
@@ -1,7 +1,7 @@
package fr.maxlego08.menu.inventory;
-import fr.maxlego08.menu.ZMenuPlugin;
import fr.maxlego08.menu.api.InventoryListener;
+import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.animation.PlayerTitleAnimation;
import fr.maxlego08.menu.api.animation.TitleAnimation;
import fr.maxlego08.menu.api.configuration.Configuration;
@@ -11,6 +11,7 @@
import fr.maxlego08.menu.api.exceptions.InventoryOpenException;
import fr.maxlego08.menu.api.utils.ClearInvType;
import fr.maxlego08.menu.common.utils.ZUtils;
+import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
@@ -20,6 +21,7 @@
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import java.util.HashMap;
@@ -28,7 +30,7 @@
public abstract class VInventory extends ZUtils implements Cloneable, BaseInventory {
protected int id;
- protected ZMenuPlugin plugin;
+ protected MenuPlugin plugin;
protected final Map items = new HashMap<>();
protected final Map playerInventoryItems = new HashMap<>();
protected Player player;
@@ -110,7 +112,8 @@ public ItemButton addItem(boolean inPlayerInventory, int slot, ItemStack itemSta
this.createDefaultInventory();
if (itemStack == null) {
- this.plugin.getLogger().severe("Attention, a null ItemStack was found in slot " + slot + " ! > " + this);
+ if (Configuration.enableDebug)
+ Logger.info("Attention, a null ItemStack was found in slot " + slot + " ! > " + this, Logger.LogType.ERROR);
return null;
}
@@ -162,6 +165,11 @@ public void clearItem() {
return this.items;
}
+ @Nullable
+ public ItemButton getItem(int slot) {
+ return this.items.get(slot);
+ }
+
@Override
public @NonNull Map getPlayerInventoryItems() {
return this.playerInventoryItems;
@@ -201,7 +209,7 @@ public String getGuiName() {
return this.guiName;
}
- protected InventoryResult preOpenInventory(@NotNull ZMenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException {
+ protected InventoryResult preOpenInventory(@NotNull MenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException {
this.page = page;
this.args = args;
@@ -211,9 +219,9 @@ protected InventoryResult preOpenInventory(@NotNull ZMenuPlugin main, Player pla
return this.openInventory(main, player, page, args);
}
- public abstract InventoryResult openInventory(ZMenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException;
+ public abstract InventoryResult openInventory(MenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException;
- protected void onPreClose(InventoryCloseEvent event, ZMenuPlugin plugin, Player player) {
+ protected void onPreClose(InventoryCloseEvent event, MenuPlugin plugin, Player player) {
this.isClose = true;
if (this.playerTitleAnimation != null){
this.playerTitleAnimation.stop();
@@ -221,17 +229,21 @@ protected void onPreClose(InventoryCloseEvent event, ZMenuPlugin plugin, Player
this.onClose(event, plugin, player);
}
- protected void onClose(InventoryCloseEvent event, ZMenuPlugin plugin, Player player) {
+ protected void onClose(InventoryCloseEvent event, MenuPlugin plugin, Player player) {
}
- protected void onDrag(InventoryDragEvent event, ZMenuPlugin plugin, Player player) {
+ protected void onInventorySwitch(InventoryCloseEvent event, Player player, VInventory newInventoryEngine) {
+ this.onPreClose(event, this.plugin, player);
}
- public @NonNull ZMenuPlugin getPlugin() {
+ protected void onDrag(InventoryDragEvent event, MenuPlugin plugin, Player player) {
+ }
+
+ public @NonNull MenuPlugin getPlugin() {
return this.plugin;
}
- public void setPlugin(ZMenuPlugin plugin) {
+ public void setPlugin(MenuPlugin plugin) {
this.plugin = plugin;
}
@@ -245,9 +257,11 @@ protected VInventory clone() {
return null;
}
- public void postOpen(ZMenuPlugin plugin, Player player, int page, Object[] objects) {
+ public void postOpen(MenuPlugin plugin, Player player, int page, Object[] objects) {
}
+ protected void onPostOpen(Player player, MenuPlugin plugin, int page, Object[] objects) {}
+
@Override
public @NotNull Inventory getInventory() {
return this.inventory;
@@ -302,7 +316,7 @@ public boolean isClickLimiterEnabled() {
return this.isClickLimiterEnabled;
}
- public void onInventoryClick(InventoryClickEvent event, ZMenuPlugin plugin, Player player) {
+ public void onInventoryClick(InventoryClickEvent event, MenuPlugin plugin, Player player) {
}
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/AnvilInventoryDefault.java b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/AnvilInventoryDefault.java
new file mode 100644
index 000000000..282dc5475
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/AnvilInventoryDefault.java
@@ -0,0 +1,132 @@
+package fr.maxlego08.menu.inventory.inventories;
+
+import fr.maxlego08.menu.api.MenuPlugin;
+import fr.maxlego08.menu.api.button.Button;
+import fr.maxlego08.menu.api.engine.AnvilInventoryEngine;
+import fr.maxlego08.menu.api.engine.ItemButton;
+import fr.maxlego08.menu.api.inventory.AnvilInventory;
+import fr.maxlego08.menu.api.requirement.Requirement;
+import fr.maxlego08.menu.api.utils.Placeholders;
+import fr.maxlego08.menu.api.utils.TextChange;
+import fr.maxlego08.menu.api.utils.TextChangeType;
+import fr.maxlego08.menu.inventory.VInventory;
+import fr.maxlego08.menu.common.network.NMSMenuPacketListener;
+import fr.maxlego08.menu.common.network.PacketQueue;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
+import net.minecraft.network.protocol.game.ServerGamePacketListener;
+import net.minecraft.network.protocol.game.ServerboundRenameItemPacket;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class AnvilInventoryDefault extends InventoryDefault implements AnvilInventoryEngine {
+ private PacketQueue> incomingPackets;
+ private String currentText = "";
+ private volatile int containerId;
+
+
+ private boolean firstPacketReceived = false;
+
+ private TextChange updateText(String newText) {
+ if (newText == null) newText = "";
+ TextChange change = TextChange.compute(currentText, newText);
+ currentText = newText;
+ return change;
+ }
+
+ @Override
+ protected void onPostOpen(Player player, MenuPlugin plugin, int page, Object[] objects) {
+
+ this.containerId = ((CraftPlayer) player).getHandle().containerMenu.containerId;
+
+ super.onPostOpen(player, plugin, page, objects);
+ }
+
+ @Override
+ public @NotNull String getCurrentText() {
+ return this.currentText;
+ }
+
+ @Override
+ public void postOpen(MenuPlugin plugin, Player player, int page, Object[] objects) {
+ incomingPackets = PacketQueue.>builder()
+ .on(ServerboundRenameItemPacket.class, packet -> {
+ if (!(this.getMenuInventory() instanceof AnvilInventory anvilInventory)) return;
+
+ TextChange textChange = updateText(packet.getName());
+
+ if (!firstPacketReceived && textChange.type() == TextChangeType.EQUAL) {
+ // The client sends an initial packet with the default text when the menu is opened, ignore it
+ firstPacketReceived = true;
+ return;
+ }
+
+ Placeholders placeholders = new Placeholders();
+
+ ItemButton item = this.getItem(2);
+
+ if (item != null) {
+ ItemStack nmsCopy = CraftItemStack.asNMSCopy(item.getDisplayItem());
+ ClientboundContainerSetSlotPacket clientboundContainerSetSlotPacket = new ClientboundContainerSetSlotPacket(
+ containerId,
+ 8,
+ 2,
+ nmsCopy
+ );
+
+ NMSMenuPacketListener.get().sendPacket(player, clientboundContainerSetSlotPacket);
+ }
+
+ placeholders.register("type", textChange.type().name());
+ placeholders.register("old_text", textChange.oldText());
+ switch (textChange.type()) {
+ case ADDED, REMOVED -> {
+ placeholders.register("char", String.valueOf(textChange.changedChar()));
+ placeholders.register("new_text", textChange.newText());
+ }
+ case CLEARED -> placeholders.register("new_text", "");
+ case REPLACED, EQUAL -> placeholders.register("new_text", textChange.newText());
+ }
+
+ for (Requirement requirement : anvilInventory.getRenameRequirements()) {
+ requirement.execute(player, null, this, placeholders);
+ }
+
+ List buttons = getButtons();
+ for (Button button : buttons) {
+ button.onAnvilTextChange(player, this, textChange, placeholders);
+ }
+
+ })
+ .build()
+ .schedule(plugin, player);
+
+ NMSMenuPacketListener.get().redirectIncoming(player, ServerboundRenameItemPacket.class, incomingPackets);
+
+ super.postOpen(plugin, player, page, objects);
+ }
+
+ @Override
+ protected void onClose(InventoryCloseEvent event, MenuPlugin plugin, Player player) {
+ NMSMenuPacketListener.get().stopRedirecting(player, ServerboundRenameItemPacket.class);
+ incomingPackets.cancel();
+ super.onClose(event, plugin, player);
+ }
+
+ @Override
+ protected void onInventorySwitch(InventoryCloseEvent event, Player player, VInventory newInventoryEngine) {
+ // Don't
+ if (!(newInventoryEngine instanceof AnvilInventoryEngine)) {
+ this.onClose(event, this.plugin, player);
+ } else {
+ incomingPackets.cancel();
+ super.onClose(event, this.plugin, player);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
similarity index 83%
rename from src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
rename to Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
index 6e60d2123..0d05108de 100644
--- a/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
@@ -1,14 +1,17 @@
package fr.maxlego08.menu.inventory.inventories;
import com.tcoded.folialib.wrapper.task.WrappedTask;
-import fr.maxlego08.menu.ZMenuPlugin;
import fr.maxlego08.menu.api.Inventory;
import fr.maxlego08.menu.api.InventoryManager;
+import fr.maxlego08.menu.api.MenuItemStack;
+import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.button.Button;
import fr.maxlego08.menu.api.engine.InventoryEngine;
import fr.maxlego08.menu.api.engine.InventoryResult;
import fr.maxlego08.menu.api.engine.ItemButton;
import fr.maxlego08.menu.api.exceptions.InventoryOpenException;
+import fr.maxlego08.menu.api.inventory.ChestInventory;
+import fr.maxlego08.menu.api.inventory.ContainerInventory;
import fr.maxlego08.menu.api.pattern.Pattern;
import fr.maxlego08.menu.api.requirement.RefreshRequirement;
import fr.maxlego08.menu.api.utils.Placeholders;
@@ -44,7 +47,7 @@ public class InventoryDefault extends VInventory implements InventoryEngine {
private List updatedButtons = Collections.emptyList();
@Override
- public InventoryResult openInventory(ZMenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException {
+ public InventoryResult openInventory(MenuPlugin main, Player player, int page, Object... args) throws InventoryOpenException {
this.inventory = (Inventory) args[0];
this.perfDebug = PerformanceDebug.create("inventory:" + this.inventory.getFileName());
@@ -56,8 +59,10 @@ public InventoryResult openInventory(ZMenuPlugin main, Player player, int page,
return result;
}
- super.setClearInvType(this.inventory.getClearInvType());
- super.setClickLimiterEnabled(this.inventory.isClickLimiterEnabled());
+ if (this.inventory instanceof ContainerInventory containerInventory) {
+ super.setClearInvType(containerInventory.getClearInvType());
+ super.setClickLimiterEnabled(containerInventory.isClickLimiterEnabled());
+ }
this.oldInventories = this.extractOldInventories(args);
@@ -112,27 +117,33 @@ public InventoryResult openInventory(ZMenuPlugin main, Player player, int page,
String parsedName = super.papi(placeholders.parse(inventoryName), targetPlayer, false);
this.perfDebug.end();
- this.perfDebug.start("openInventory.createMetaInventory");
- if (this.inventory.getType() == InventoryType.CHEST) {
- super.createMetaInventory(parsedName, this.inventory.size());
- } else {
- super.createMetaInventory(parsedName, this.inventory.getType());
- }
- super.setTitleAnimation(this.inventory.getTitleAnimation());
- this.perfDebug.end();
+ if (this.inventory instanceof ContainerInventory containerInventory) {
+ this.perfDebug.start("openInventory.createMetaInventory");
+ if (containerInventory.getType() == InventoryType.CHEST && containerInventory instanceof ChestInventory chestInventory) {
+ super.createMetaInventory(parsedName, chestInventory.size());
+ } else {
+ super.createMetaInventory(parsedName, containerInventory.getType());
+ }
+ this.perfDebug.end();
+
- // Display fill items
- this.perfDebug.start("openInventory.fillItems");
- if (this.inventory.getFillItemStack() != null) {
- ItemStack builtItem = this.inventory.getFillItemStack().build(player);
- if (builtItem != null) {
- ItemStack[] contents = super.getSpigotInventory().getContents();
- for (int slot = 0; slot < contents.length; slot++) {
- this.addItem(slot, builtItem.clone());
+ super.setTitleAnimation(containerInventory.getTitleAnimation());
+
+ // Display fill items
+ this.perfDebug.start("openInventory.fillItems");
+ MenuItemStack fillItemStack = containerInventory.getFillItemStack();
+ if (fillItemStack != null) {
+ ItemStack builtItem = fillItemStack.build(player);
+ if (builtItem != null) {
+ ItemStack[] contents = super.getSpigotInventory().getContents();
+ for (int slot = 0; slot < contents.length; slot++) {
+ this.addItem(slot, builtItem.clone());
+ }
}
}
+ this.perfDebug.end();
}
- this.perfDebug.end();
+
// Display buttons
for (Button button : this.buttons) {
@@ -160,24 +171,24 @@ public InventoryResult openInventory(ZMenuPlugin main, Player player, int page,
}
@Override
- public void postOpen(ZMenuPlugin plugin, Player player, int page, Object[] objects) {
+ public void postOpen(MenuPlugin plugin, Player player, int page, Object[] objects) {
this.inventory.postOpenInventory(player, this);
}
@Override
- protected void onClose(InventoryCloseEvent event, ZMenuPlugin plugin, Player player) {
+ protected void onClose(InventoryCloseEvent event, MenuPlugin plugin, Player player) {
this.inventory.closeInventory(player, this);
this.buttons.forEach(button -> button.onInventoryClose(player, this));
}
@Override
- protected void onDrag(InventoryDragEvent event, ZMenuPlugin plugin, Player player) {
+ protected void onDrag(InventoryDragEvent event, MenuPlugin plugin, Player player) {
this.buttons.forEach(button -> button.onDrag(event, player, this));
}
@Override
- public void onInventoryClick(InventoryClickEvent event, ZMenuPlugin plugin, Player player) {
+ public void onInventoryClick(InventoryClickEvent event, MenuPlugin plugin, Player player) {
this.buttons.forEach(button -> button.onInventoryClick(event, player, this));
}
@@ -197,15 +208,9 @@ public void buildButton(Button button, @NonNull Placeholders placeholders) {
return;
}
final Player targetPlayer = this.getTargetPlayer();
- if (button.hasCustomRender()) {
- this.perfDebug.start("onRender." + button.getName());
- button.onRender(targetPlayer, this);
- this.perfDebug.end();
- return;
- }
this.perfDebug.start("getDisplayButton." + button.getName());
- button = button.getDisplayButton(this, this.player);
+ button = button.getDisplayButton(this, targetPlayer);
this.perfDebug.end();
if (button == null) {
return;
@@ -231,13 +236,25 @@ public void buildButton(Button button, @NonNull Placeholders placeholders) {
} else {
// If the player has the permission, the button
- this.displayButton(button, placeholders);
+ if (button.hasCustomRender()) {
+ this.perfDebug.start("onRender." + button.getName());
+ button.onRender(targetPlayer, this);
+ this.perfDebug.end();
+ } else {
+ this.displayButton(button, placeholders);
+ }
}
} else {
// If there is no permission, then the button
- this.displayButton(button, placeholders);
+ if (button.hasCustomRender()) {
+ this.perfDebug.start("onRender." + button.getName());
+ button.onRender(targetPlayer, this);
+ this.perfDebug.end();
+ } else {
+ this.displayButton(button, placeholders);
+ }
}
}
@@ -266,7 +283,18 @@ public void displayButton(@NotNull Button button, @NotNull Placeholders placehol
runnable = w -> {
this.perfDebug.start("getRealSlot." + button.getName());
- int slot = button.getRealSlot(button.isPlayerInventory() ? 36 : this.inventory.size(), this.page);
+ int slot;
+ if (button.isPlayerInventory()) {
+ slot = button.getRealSlot(36, this.page);
+ } else {
+ if (this.inventory instanceof ChestInventory chestInventory) {
+ slot = button.getRealSlot(chestInventory.size(), this.page);
+ } else {
+ //noinspection deprecation
+ slot = button.getRealSlot(this.inventory.size(), this.page);
+ }
+ }
+
this.perfDebug.end();
this.displayFinalButton(button, placeholders, slot);
};
@@ -343,7 +371,7 @@ public void displayFinalButton(@NotNull Button button, @NotNull Placeholders pla
}
if (button.isDraggable()) {
- //If one button is draggrable enable click
+ //If one button is draggable enable click
super.setDisablePlayerInventoryClick(false);
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/setter/AnvilInventorySetter.java b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/AnvilInventorySetter.java
new file mode 100644
index 000000000..6f19ea79b
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/AnvilInventorySetter.java
@@ -0,0 +1,6 @@
+package fr.maxlego08.menu.inventory.setter;
+
+import fr.maxlego08.menu.api.inventory.AnvilInventory;
+
+public interface AnvilInventorySetter extends AnvilInventory, ContainerInventorySetter {
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ChestInventorySetter.java b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ChestInventorySetter.java
new file mode 100644
index 000000000..54dd236ba
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ChestInventorySetter.java
@@ -0,0 +1,6 @@
+package fr.maxlego08.menu.inventory.setter;
+
+import fr.maxlego08.menu.api.inventory.ChestInventory;
+
+public interface ChestInventorySetter extends ChestInventory, ContainerInventorySetter {
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ContainerInventorySetter.java b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ContainerInventorySetter.java
new file mode 100644
index 000000000..1dd0ce633
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/setter/ContainerInventorySetter.java
@@ -0,0 +1,46 @@
+package fr.maxlego08.menu.inventory.setter;
+
+import fr.maxlego08.menu.api.MenuItemStack;
+import fr.maxlego08.menu.api.inventory.ContainerInventory;
+import fr.maxlego08.menu.api.pattern.Pattern;
+import fr.maxlego08.menu.api.requirement.Action;
+import fr.maxlego08.menu.api.requirement.Requirement;
+import fr.maxlego08.menu.api.utils.ClearInvType;
+import fr.maxlego08.menu.api.utils.OpenWithItem;
+import org.bukkit.event.inventory.InventoryType;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+public interface ContainerInventorySetter extends ContainerInventory {
+ void setType(InventoryType type);
+
+ void setCancelItemPickup(boolean ItemPickupDisabled);
+
+ void setFillItemStack(MenuItemStack fillItemStack);
+
+ void setUpdateInterval(int updateInterval);
+
+ void setFile(File file);
+
+ void setOpenRequirement(Requirement openRequirement);
+
+ void setOpenWithItem(OpenWithItem openWithItem);
+
+ void setTranslatedNames(Map translatedNames);
+
+ void setClearInventory(boolean clearInventory);
+
+ void setPatterns(List patterns);
+
+ void setTargetPlayerNamePlaceholder(String targetPlaceholder);
+
+ void setOpenActions(List openActions);
+
+ void setCloseActions(List closeActions);
+
+ void setClickLimiterEnabled(boolean enabled);
+
+ void setClearInvType(ClearInvType clearInvType);
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZAnvilInventory.java b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZAnvilInventory.java
new file mode 100644
index 000000000..a199a75e6
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZAnvilInventory.java
@@ -0,0 +1,23 @@
+package fr.maxlego08.menu.inventory.zinv;
+
+import fr.maxlego08.menu.api.button.Button;
+import fr.maxlego08.menu.api.requirement.Requirement;
+import fr.maxlego08.menu.inventory.setter.AnvilInventorySetter;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ZAnvilInventory extends ZInventory implements AnvilInventorySetter {
+ private final List renameRequirements;
+
+ public ZAnvilInventory(Plugin plugin, String name, String fileName, int size, List buttons, List renameRequirements) {
+ super(plugin, name, fileName, size, buttons);
+ this.renameRequirements = renameRequirements;
+ }
+
+ @Override
+ public @NotNull List getRenameRequirements() {
+ return renameRequirements;
+ }
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZChestInventory.java b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZChestInventory.java
new file mode 100644
index 000000000..dc669b4bc
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZChestInventory.java
@@ -0,0 +1,22 @@
+package fr.maxlego08.menu.inventory.zinv;
+
+import fr.maxlego08.menu.api.button.Button;
+import fr.maxlego08.menu.inventory.setter.ChestInventorySetter;
+import org.bukkit.plugin.Plugin;
+
+import java.util.List;
+
+public class ZChestInventory extends ZInventory implements ChestInventorySetter {
+ /**
+ * @param plugin The plugin where the inventory comes from
+ * @param name Inventory name
+ * @param fileName Inventory file name
+ * @param size Inventory size
+ * @param buttons List of {@link Button}
+ */
+ public ZChestInventory(Plugin plugin, String name, String fileName, int size, List buttons) {
+ super(plugin, name, fileName, size, buttons);
+ }
+
+
+}
diff --git a/src/main/java/fr/maxlego08/menu/ZInventory.java b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZInventory.java
similarity index 89%
rename from src/main/java/fr/maxlego08/menu/ZInventory.java
rename to Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZInventory.java
index 932ed1a4b..cbcf588b3 100644
--- a/src/main/java/fr/maxlego08/menu/ZInventory.java
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/zinv/ZInventory.java
@@ -1,13 +1,14 @@
-package fr.maxlego08.menu;
+package fr.maxlego08.menu.inventory.zinv;
-import fr.maxlego08.menu.api.Inventory;
import fr.maxlego08.menu.api.MenuItemStack;
+import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.animation.TitleAnimation;
import fr.maxlego08.menu.api.button.Button;
import fr.maxlego08.menu.api.button.PaginateButton;
import fr.maxlego08.menu.api.configuration.Configuration;
import fr.maxlego08.menu.api.engine.InventoryEngine;
import fr.maxlego08.menu.api.engine.InventoryResult;
+import fr.maxlego08.menu.api.inventory.ContainerInventory;
import fr.maxlego08.menu.api.pattern.Pattern;
import fr.maxlego08.menu.api.players.inventory.InventoriesPlayer;
import fr.maxlego08.menu.api.requirement.Action;
@@ -16,17 +17,19 @@
import fr.maxlego08.menu.api.utils.*;
import fr.maxlego08.menu.common.utils.ZUtils;
import fr.maxlego08.menu.inventory.inventories.InventoryDefault;
+import fr.maxlego08.menu.inventory.setter.ContainerInventorySetter;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
-public class ZInventory extends ZUtils implements Inventory {
+public class ZInventory extends ZUtils implements ContainerInventorySetter {
private final Plugin plugin;
private final String name;
@@ -47,7 +50,7 @@ public class ZInventory extends ZUtils implements Inventory {
private Requirement openRequirement;
private OpenWithItem openWithItem;
private InventoryType type = InventoryType.CHEST;
- private String targetPlayerNamePlaceholder;
+ private String targetPlayerNamePlaceholder = null;
private TitleAnimation titleAnimation;
private List openActions = new ArrayList<>();
private List closeActions = new ArrayList<>();
@@ -79,7 +82,7 @@ public String getName() {
}
@Override
- public String getName(Player player, InventoryEngine inventoryDefault, Placeholders placeholders) {
+ public String getName(@NotNull Player player, InventoryEngine inventoryDefault, Placeholders placeholders) {
if (!this.conditionalNames.isEmpty()) {
ConditionalName selected = null;
@@ -107,7 +110,8 @@ public InventoryType getType() {
return this.type;
}
- public void setType(InventoryType type) {
+ @Override
+ public void setType(@NotNull InventoryType type) {
this.type = type;
}
@@ -116,6 +120,7 @@ public boolean shouldCancelItemPickup() {
return this.ItemPickupDisabled;
}
+ @Override
public void setCancelItemPickup(boolean ItemPickupDisabled) {
this.ItemPickupDisabled = ItemPickupDisabled;
}
@@ -229,27 +234,21 @@ public InventoryResult openInventory(Player player, InventoryEngine inventoryDef
if (holder instanceof InventoryDefault inventoryHolder) {
this.clearPlayerInventoryButtons(player, inventoryHolder);
- if (inventoryHolder.getMenuInventory().cleanInventory() && !this.clearInventory) {
+ if (inventoryHolder.getMenuInventory() instanceof ContainerInventory containerInventory && containerInventory.clearInventory() && !this.clearInventory) {
inventoriesPlayer.giveInventory(player);
} else if (this.clearInventory) {
if (this.clearInvType == ClearInvType.DEFAULT){
inventoriesPlayer.storeInventory(player);
} else {
- if (inventoriesPlayer.hasSavedInventory(player.getUniqueId())) {
- inventoriesPlayer.getPlayerInventory(player.getUniqueId()).ifPresent(inventoryPlayer -> {
- for (int slot : inventoryPlayer.getItems().keySet()) {
- player.getInventory().setItem(slot, null);
- this.clearInvType.getRemoveItem().accept(player, slot, player.getInventory());
- }
- });
- } else {
- removePlayerInventoryButtons(player, inventoryHolder);
- inventoriesPlayer.storeInventory(player);
- }
+ inventoriesPlayer.storeInventoryTemporaryOrClear(player);
}
}
} else if (this.clearInventory) {
- inventoriesPlayer.storeInventory(player);
+ if (this.clearInvType == ClearInvType.DEFAULT) {
+ inventoriesPlayer.storeInventory(player);
+ } else {
+ inventoriesPlayer.storeInventoryTemporary(player);
+ }
}
var placeholders = new Placeholders();
@@ -270,12 +269,6 @@ private void clearPlayerInventoryButtons(Player player, InventoryEngine inventor
}
}
- private void removePlayerInventoryButtons(Player player, InventoryEngine inventoryDefault) {
- for (int slot : inventoryDefault.getPlayerInventoryItems().keySet()) {
- player.getInventory().setItem(slot, null);
- }
- }
-
private List collectSessionItems(Player player, InventoryEngine inventoryDefault) {
Set buttonSlots = new HashSet<>(inventoryDefault.getPlayerInventoryItems().keySet());
for (Button button : inventoryDefault.getButtons()) {
@@ -312,7 +305,8 @@ public void postOpenInventory(Player player, InventoryEngine inventoryDefault) {
@Override
public void closeInventory(Player player, InventoryEngine inventoryDefault) {
- ZMenuPlugin.getInstance().getScheduler().runAtEntityLater(player, task -> {
+ MenuPlugin menuPlugin = inventoryDefault.getPlugin();
+ menuPlugin.getScheduler().runAtEntityLater(player, task -> {
InventoryHolder newHolder = CompatibilityUtil.getTopInventory(player).getHolder();
boolean isInNewzMenuInventory = newHolder instanceof InventoryDefault;
if (newHolder != null && !(newHolder instanceof InventoryDefault)) {
@@ -322,8 +316,8 @@ public void closeInventory(Player player, InventoryEngine inventoryDefault) {
this.clearPlayerInventoryButtons(player, inventoryDefault);
if (this.clearInventory) {
- InventoriesPlayer inventoriesPlayer = inventoryDefault.getPlugin().getInventoriesPlayer();
- inventoriesPlayer.giveInventory(player);
+ InventoriesPlayer inventoriesPlayer = menuPlugin.getInventoriesPlayer();
+ this.clearInvType.getOnInventoryClose().accept(inventoriesPlayer, player);
restoreSessionItems(player, sessionItems);
}
}
@@ -346,6 +340,7 @@ public MenuItemStack getFillItemStack() {
return this.fillItemStack;
}
+ @Override
public void setFillItemStack(MenuItemStack fillItemStack) {
this.fillItemStack = fillItemStack;
}
@@ -355,6 +350,7 @@ public int getUpdateInterval() {
return this.updateInterval;
}
+ @Override
public void setUpdateInterval(int updateInterval) {
this.updateInterval = updateInterval;
}
@@ -373,6 +369,7 @@ public File getFile() {
return this.file;
}
+ @Override
public void setFile(File file) {
this.file = file;
}
@@ -387,6 +384,7 @@ public Requirement getOpenRequirement() {
return this.openRequirement;
}
+ @Override
public void setOpenRequirement(Requirement openRequirement) {
this.openRequirement = openRequirement;
}
@@ -396,6 +394,7 @@ public OpenWithItem getOpenWithItem() {
return this.openWithItem;
}
+ @Override
public void setOpenWithItem(OpenWithItem openWithItem) {
this.openWithItem = openWithItem;
}
@@ -405,6 +404,7 @@ public Map getTranslatedNames() {
return this.translatedNames;
}
+ @Override
public void setTranslatedNames(Map translatedNames) {
this.translatedNames = translatedNames;
}
@@ -414,6 +414,7 @@ public List getConditionalNames() {
return this.conditionalNames;
}
+ @Override
public void setClearInventory(boolean clearInventory) {
this.clearInventory = clearInventory;
}
@@ -423,6 +424,7 @@ public List getPatterns() {
return this.patterns;
}
+ @Override
public void setPatterns(List patterns) {
this.patterns = patterns;
}
@@ -442,6 +444,7 @@ public TitleAnimation getTitleAnimation() {
return this.titleAnimation;
}
+ @Override
public void setTargetPlayerNamePlaceholder(String targetPlaceholder) {
this.targetPlayerNamePlaceholder = targetPlaceholder;
}
@@ -451,6 +454,7 @@ public List getOpenActions() {
return this.openActions;
}
+ @Override
public void setOpenActions(List openActions) {
this.openActions = openActions;
}
@@ -460,6 +464,7 @@ public List getCloseActions() {
return this.closeActions;
}
+ @Override
public void setCloseActions(List closeActions) {
this.closeActions = closeActions;
}
@@ -484,10 +489,12 @@ public void setInventoryReplacement(InventoryReplacement inventoryReplacement) {
this.inventoryReplacement = inventoryReplacement;
}
+ @Override
public void setClickLimiterEnabled(boolean enabled) {
this.isClickLimiterEnabled = enabled;
}
+ @Override
public void setClearInvType(ClearInvType clearInvType) {
this.clearInvType = clearInvType;
}
diff --git a/src/main/java/fr/maxlego08/menu/loader/components/spigot/SpigotAttackRangeItemComponentLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractAttackRangeItemComponentLoader.java
similarity index 58%
rename from src/main/java/fr/maxlego08/menu/loader/components/spigot/SpigotAttackRangeItemComponentLoader.java
rename to Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractAttackRangeItemComponentLoader.java
index 00f753423..4a469a33a 100644
--- a/src/main/java/fr/maxlego08/menu/loader/components/spigot/SpigotAttackRangeItemComponentLoader.java
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractAttackRangeItemComponentLoader.java
@@ -1,72 +1,73 @@
-package fr.maxlego08.menu.loader.components.spigot;
+package fr.maxlego08.menu.loader.components;
import fr.maxlego08.menu.api.configuration.Configuration;
-import fr.maxlego08.menu.api.context.MenuItemStackContext;
-import fr.maxlego08.menu.api.itemstack.ItemComponent;
-import fr.maxlego08.menu.api.itemstack.components.AttackRangeComponent;
import fr.maxlego08.menu.api.loader.ItemComponentLoader;
import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.configuration.ConfigurationSection;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import java.io.File;
+public abstract class AbstractAttackRangeItemComponentLoader extends ItemComponentLoader {
-public class SpigotAttackRangeItemComponentLoader extends ItemComponentLoader {
-
- public SpigotAttackRangeItemComponentLoader() {
+ public AbstractAttackRangeItemComponentLoader() {
super("attack-range");
}
- @Override
- public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
- if (componentSection == null) {
- return null;
- }
- double minReach = componentSection.getDouble("min-reach", 0f);
+ protected float getMinReach(ConfigurationSection section, String path) {
+ double minReach = section.getDouble("min-reach", 0f);
if (minReach > 64f || minReach < 0f) {
if (Configuration.enableDebug)
Logger.info("Invalid min-reach value in attack-range component at path: " + path + ". Value: " + minReach + ". It must be between 0 and 64. Using default value 0f.");
minReach = 0f;
}
- double maxReach = componentSection.getDouble("max-reach", 3f);
+ return (float) minReach;
+ }
+
+ protected float getMaxReach(ConfigurationSection section, String path) {
+ double maxReach = section.getDouble("max-reach", 3f);
if (maxReach > 64f || maxReach < 0f) {
if (Configuration.enableDebug)
Logger.info("Invalid max-reach value in attack-range component at path: " + path + ". Value: " + maxReach + ". It must be between 0 and 64. Using default value 3f.");
maxReach = 3f;
}
- double minCreativeReach = componentSection.getDouble("min-creative-reach", 0f);
+ return (float) maxReach;
+ }
+
+ protected float getMinCreativeReach(ConfigurationSection section, String path) {
+ double minCreativeReach = section.getDouble("min-creative-reach", 0f);
if (minCreativeReach > 64f || minCreativeReach < 0f) {
if (Configuration.enableDebug)
Logger.info("Invalid min-creative-reach value in attack-range component at path: " + path + ". Value: " + minCreativeReach + ". It must be between 0 and 64. Using default value 0f.");
minCreativeReach = 0f;
}
- double maxCreativeReach = componentSection.getDouble("max-creative-reach", 5f);
+ return (float) minCreativeReach;
+ }
+
+ protected float getMaxCreativeReach(ConfigurationSection section, String path) {
+ double maxCreativeReach = section.getDouble("max-creative-reach", 5f);
if (maxCreativeReach > 64f || maxCreativeReach < 0f) {
if (Configuration.enableDebug)
Logger.info("Invalid max-creative-reach value in attack-range component at path: " + path + ". Value: " + maxCreativeReach + ". It must be between 0 and 64. Using default value 5f.");
maxCreativeReach = 5f;
}
- double hitboxMargin = componentSection.getDouble("hitbox-margin", 0.3f);
+ return (float) maxCreativeReach;
+ }
+
+ protected float getHitboxMargin(ConfigurationSection section, String path) {
+ double hitboxMargin = section.getDouble("hitbox-margin", 0.3f);
if (hitboxMargin < 0f || hitboxMargin > 1f) {
if (Configuration.enableDebug)
Logger.info("Invalid hitbox-margin value in attack-range component at path: " + path + ". Value: " + hitboxMargin + ". It must be between 0 and 1. Using default value 0.3f.");
hitboxMargin = 0.3f;
}
- double mobFactor = componentSection.getDouble("mob-factor", 1f);
+ return (float) hitboxMargin;
+ }
+
+ protected float getMobFactor(ConfigurationSection section, String path) {
+ double mobFactor = section.getDouble("mob-factor", 1f);
if (mobFactor < 0f || mobFactor > 2f) {
if (Configuration.enableDebug)
Logger.info("Invalid mob-factor value in attack-range component at path: " + path + ". Value: " + mobFactor + ". It must be between 0 and 2. Using default value 1f.");
mobFactor = 1f;
}
- return new AttackRangeComponent(
- (float) minReach,
- (float) maxReach,
- (float) minCreativeReach,
- (float) maxCreativeReach,
- (float) hitboxMargin,
- (float) mobFactor
- );
+ return (float) mobFactor;
}
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractEffectItemComponentLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractEffectItemComponentLoader.java
index 4571fd5ed..819d6ee42 100644
--- a/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractEffectItemComponentLoader.java
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractEffectItemComponentLoader.java
@@ -9,10 +9,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
public abstract class AbstractEffectItemComponentLoader extends AbstractColorItemComponentLoader {
@@ -21,12 +18,12 @@ protected AbstractEffectItemComponentLoader(String componentName) {
}
protected @Nullable NamespacedKey parseNamespacedKey(String value) {
- return NamespacedKey.fromString(value.toLowerCase());
+ return NamespacedKey.fromString(value.toLowerCase(Locale.ROOT));
}
protected @Nullable ConsumeEffectType parseConsumableType(String type) {
try {
- return ConsumeEffectType.valueOf(type.toUpperCase());
+ return ConsumeEffectType.valueOf(type.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException | NullPointerException e) {
return null;
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractFireworkItemComponentLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractFireworkItemComponentLoader.java
index cc445f876..c9bcad5c2 100644
--- a/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractFireworkItemComponentLoader.java
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/AbstractFireworkItemComponentLoader.java
@@ -4,6 +4,7 @@
import org.bukkit.FireworkEffect;
import org.jetbrains.annotations.NotNull;
+import java.util.Locale;
import java.util.Map;
import java.util.Optional;
@@ -17,7 +18,7 @@ protected Optional loadFireworkEffect(@NotNull Map> extends ItemComponentLoader {
- private final Class enumClass;
- private final Function componentFactory;
-
- protected EnumVariantLoader(String path, Class enumClass, Function componentFactory) {
- super(path);
- this.enumClass = enumClass;
- this.componentFactory = componentFactory;
- }
-
- @Override
- public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
- path = this.normalizePath(path);
- String value = configuration.getString(path);
- if (value == null) return null;
- try {
- T variant = Enum.valueOf(this.enumClass, value.toUpperCase());
- return this.componentFactory.apply(variant);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
- }
-
- protected static abstract class RegistryVariantLoader extends ItemComponentLoader {
- private final Registry registry;
- private final Function componentFactory;
-
- protected RegistryVariantLoader(String path, Registry registry, Function componentFactory) {
- super(path);
- this.registry = registry;
- this.componentFactory = componentFactory;
- }
-
- @Override
- public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
- path = this.normalizePath(path);
- String value = configuration.getString(path);
- if (value == null) return null;
- NamespacedKey key = NamespacedKey.fromString(value.toLowerCase());
- if (key == null) return null;
- try {
- return this.componentFactory.apply(this.registry.getOrThrow(key));
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
- }
-
- private static abstract class DyeColorVariantLoader extends ItemComponentLoader {
- private final Function componentFactory;
-
- protected DyeColorVariantLoader(String path, Function componentFactory) {
- super(path);
- this.componentFactory = componentFactory;
- }
-
- @Override
- public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
- path = this.normalizePath(path);
- String value = configuration.getString(path);
- if (value == null) return null;
- try {
- DyeColor dyeColor = DyeColor.valueOf(value.toUpperCase());
- return this.componentFactory.apply(dyeColor);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
- }
-
- private static abstract class CollarColorLoader extends AbstractColorItemComponentLoader {
- private final Function componentFactory;
-
- protected CollarColorLoader(String path, Function componentFactory) {
- super(path);
- this.componentFactory = componentFactory;
- }
-
- @Override
- public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
- path = this.normalizePath(path);
- Object rawColor = configuration.get(path);
- if (rawColor == null) return null;
- Color color = this.parseColor(rawColor);
- DyeColor dyeColor;
- if (color == null) {
- try {
- dyeColor = DyeColor.valueOf(rawColor.toString().toUpperCase());
- } catch (IllegalArgumentException e) {
- return null;
- }
- } else {
- dyeColor = DyeColor.getByColor(color);
- }
- if (dyeColor == null) return null;
- return this.componentFactory.apply(dyeColor);
- }
- }
-
- // Specific loaders
- public class Axolotl extends EnumVariantLoader {
- public Axolotl() {
- super("axolotl/variant", org.bukkit.entity.Axolotl.Variant.class, SpigotVariantItemComponentLoader.this.variantFactory::createAxolotl);
- }
- }
-
- public static class Cat {
- private final VariantComponent variantFactory;
-
- public Cat(VariantComponent variantFactory) {
- this.variantFactory = variantFactory;
- }
-
- public class Collar extends CollarColorLoader {
- public Collar() {
- super("cat/collar", Cat.this.variantFactory::createCatCollar);
- }
- }
-
- public class Variant extends RegistryVariantLoader {
- public Variant() {
- super("cat/variant", Registry.CAT_VARIANT, Cat.this.variantFactory::createCatVariant);
- }
- }
- }
-
- public class Chicken extends RegistryVariantLoader {
- public Chicken() {
- super("chicken/variant", Registry.CHICKEN_VARIANT, SpigotVariantItemComponentLoader.this.variantFactory::createChicken);
- }
- }
-
- public class Cow extends RegistryVariantLoader {
- public Cow() {
- super("cow/variant", Registry.COW_VARIANT, SpigotVariantItemComponentLoader.this.variantFactory::createCow);
- }
- }
-
- public class Fox extends EnumVariantLoader {
- public Fox() {
- super("fox/variant", org.bukkit.entity.Fox.Type.class, SpigotVariantItemComponentLoader.this.variantFactory::createFox);
- }
- }
-
- public class Frog extends RegistryVariantLoader {
- public Frog() {
- super("frog/variant", Registry.FROG_VARIANT, SpigotVariantItemComponentLoader.this.variantFactory::createFrog);
- }
- }
-
- public class Horse extends EnumVariantLoader {
- public Horse() {
- super("horse/variant", org.bukkit.entity.Horse.Color.class, SpigotVariantItemComponentLoader.this.variantFactory::createHorse);
- }
- }
-
- public class Llama extends EnumVariantLoader {
- public Llama() {
- super("llama/variant", org.bukkit.entity.Llama.Color.class, SpigotVariantItemComponentLoader.this.variantFactory::createLlama);
- }
- }
-
- public class MushroomCow extends EnumVariantLoader {
- public MushroomCow() {
- super("mooshroom/variant", org.bukkit.entity.MushroomCow.Variant.class, SpigotVariantItemComponentLoader.this.variantFactory::createMushroomCow);
- }
- }
-
- public class Painting extends RegistryVariantLoader {
- public Painting() {
- super("painting/variant", Registry.ART, SpigotVariantItemComponentLoader.this.variantFactory::createPainting);
- }
- }
-
- public class Parrot extends EnumVariantLoader {
- public Parrot() {
- super("parrot/variant", org.bukkit.entity.Parrot.Variant.class, SpigotVariantItemComponentLoader.this.variantFactory::createParrot);
- }
- }
-
- public class Pig extends RegistryVariantLoader {
- public Pig() {
- super("pig/variant", Registry.PIG_VARIANT, SpigotVariantItemComponentLoader.this.variantFactory::createPig);
- }
- }
-
- public class Rabbit extends EnumVariantLoader {
- public Rabbit() {
- super("rabbit/variant", org.bukkit.entity.Rabbit.Type.class, SpigotVariantItemComponentLoader.this.variantFactory::createRabbit);
- }
- }
-
- public class Salmon extends EnumVariantLoader {
- public Salmon() {
- super("salmon/size", org.bukkit.entity.Salmon.Variant.class, SpigotVariantItemComponentLoader.this.variantFactory::createSalmon);
- }
- }
-
- public class Sheep extends DyeColorVariantLoader {
- public Sheep() {
- super("sheep/color", SpigotVariantItemComponentLoader.this.variantFactory::createSheep);
- }
- }
-
- public class ShulkerBox extends DyeColorVariantLoader {
- public ShulkerBox() {
- super("shulker/color", SpigotVariantItemComponentLoader.this.variantFactory::createShulkerBox);
- }
- }
-
- public static class TropicalFish {
- private final VariantComponent variantFactory;
-
- public TropicalFish(VariantComponent variantFactory) {
- this.variantFactory = variantFactory;
- }
-
- public class BaseColor extends DyeColorVariantLoader {
- public BaseColor() {
- super("tropical_fish/base_color", TropicalFish.this.variantFactory::createTropicalFishBaseColor);
- }
- }
-
- public class PatternColor extends DyeColorVariantLoader {
- public PatternColor() {
- super("tropical_fish/pattern_color", TropicalFish.this.variantFactory::createTropicalFishPatternColor);
- }
- }
- }
-
- public class Villager extends RegistryVariantLoader {
- public Villager() {
- super("villager/variant", Registry.VILLAGER_TYPE, SpigotVariantItemComponentLoader.this.variantFactory::createVillager);
- }
- }
-
- public static class Wolf {
- private final VariantComponent variantFactory;
-
- public Wolf(VariantComponent variantFactory) {
- this.variantFactory = variantFactory;
- }
-
- public class Collar extends CollarColorLoader {
- public Collar() {
- super("wolf/collar", Wolf.this.variantFactory::createWolfCollar);
- }
- }
-
- public class Variant extends RegistryVariantLoader {
- public Variant() {
- super("wolf/variant", Registry.WOLF_VARIANT, Wolf.this.variantFactory::createWolfVariant);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/CollarColorLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/CollarColorLoader.java
new file mode 100644
index 000000000..c50622718
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/CollarColorLoader.java
@@ -0,0 +1,44 @@
+package fr.maxlego08.menu.loader.components.variants.base;
+
+import fr.maxlego08.menu.api.context.MenuItemStackContext;
+import fr.maxlego08.menu.api.itemstack.ItemComponent;
+import fr.maxlego08.menu.loader.components.AbstractColorItemComponentLoader;
+import org.bukkit.Color;
+import org.bukkit.DyeColor;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.function.Function;
+
+public abstract class CollarColorLoader extends AbstractColorItemComponentLoader {
+ private final Function componentFactory;
+
+ protected CollarColorLoader(String path, Function componentFactory) {
+ super(path);
+ this.componentFactory = componentFactory;
+ }
+
+ @Override
+ public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
+ path = normalizePath(path);
+ Object rawColor = configuration.get(path);
+ if (rawColor == null) return null;
+ Color color = parseColor(rawColor);
+ DyeColor dyeColor;
+ if (color == null) {
+ try {
+ dyeColor = DyeColor.valueOf(rawColor.toString().toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ } else {
+ dyeColor = DyeColor.getByColor(color);
+ }
+ if (dyeColor == null) return null;
+ return componentFactory.apply(dyeColor);
+ }
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/DyeColorVariantLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/DyeColorVariantLoader.java
new file mode 100644
index 000000000..781e19d0e
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/DyeColorVariantLoader.java
@@ -0,0 +1,36 @@
+package fr.maxlego08.menu.loader.components.variants.base;
+
+import fr.maxlego08.menu.api.context.MenuItemStackContext;
+import fr.maxlego08.menu.api.itemstack.ItemComponent;
+import fr.maxlego08.menu.api.loader.ItemComponentLoader;
+import org.bukkit.DyeColor;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.function.Function;
+
+public abstract class DyeColorVariantLoader extends ItemComponentLoader {
+ private final Function componentFactory;
+
+ protected DyeColorVariantLoader(String path, Function componentFactory) {
+ super(path);
+ this.componentFactory = componentFactory;
+ }
+
+ @Override
+ public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
+ path = normalizePath(path);
+ String value = configuration.getString(path);
+ if (value == null) return null;
+ try {
+ DyeColor dyeColor = DyeColor.valueOf(value.toUpperCase(Locale.ROOT));
+ return componentFactory.apply(dyeColor);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/EnumVariantLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/EnumVariantLoader.java
new file mode 100644
index 000000000..11bf1cb74
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/EnumVariantLoader.java
@@ -0,0 +1,37 @@
+package fr.maxlego08.menu.loader.components.variants.base;
+
+import fr.maxlego08.menu.api.context.MenuItemStackContext;
+import fr.maxlego08.menu.api.itemstack.ItemComponent;
+import fr.maxlego08.menu.api.loader.ItemComponentLoader;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.function.Function;
+
+public abstract class EnumVariantLoader> extends ItemComponentLoader {
+ private final Class enumClass;
+ private final Function componentFactory;
+
+ protected EnumVariantLoader(String path, Class enumClass, Function componentFactory) {
+ super(path);
+ this.enumClass = enumClass;
+ this.componentFactory = componentFactory;
+ }
+
+ @Override
+ public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
+ path = normalizePath(path);
+ String value = configuration.getString(path);
+ if (value == null) return null;
+ try {
+ T variant = Enum.valueOf(enumClass, value.toUpperCase(Locale.ROOT));
+ return componentFactory.apply(variant);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
diff --git a/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/RegistryVariantLoader.java b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/RegistryVariantLoader.java
new file mode 100644
index 000000000..055e0502e
--- /dev/null
+++ b/Common/src/main/java/fr/maxlego08/menu/loader/components/variants/base/RegistryVariantLoader.java
@@ -0,0 +1,41 @@
+package fr.maxlego08.menu.loader.components.variants.base;
+
+import fr.maxlego08.menu.api.context.MenuItemStackContext;
+import fr.maxlego08.menu.api.itemstack.ItemComponent;
+import fr.maxlego08.menu.api.loader.ItemComponentLoader;
+import org.bukkit.Keyed;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Registry;
+import org.bukkit.configuration.ConfigurationSection;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.function.Function;
+
+public abstract class RegistryVariantLoader extends ItemComponentLoader {
+ private final Registry registry;
+ private final Function componentFactory;
+
+ protected RegistryVariantLoader(String path, Registry registry, Function componentFactory) {
+ super(path);
+ this.registry = registry;
+ this.componentFactory = componentFactory;
+ }
+
+ @Override
+ public @Nullable ItemComponent load(@NotNull MenuItemStackContext context, @NotNull File file, @NotNull YamlConfiguration configuration, @NotNull String path, @Nullable ConfigurationSection componentSection) {
+ path = normalizePath(path);
+ String value = configuration.getString(path);
+ if (value == null) return null;
+ NamespacedKey key = NamespacedKey.fromString(value.toLowerCase(Locale.ROOT));
+ if (key == null) return null;
+ try {
+ return componentFactory.apply(registry.getOrThrow(key));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
diff --git a/Common/src/test/java/fr/maxlego08/menu/test/common/PluginVersionTest.java b/Common/src/test/java/fr/maxlego08/menu/test/common/PluginVersionTest.java
new file mode 100644
index 000000000..1807a5b13
--- /dev/null
+++ b/Common/src/test/java/fr/maxlego08/menu/test/common/PluginVersionTest.java
@@ -0,0 +1,193 @@
+package fr.maxlego08.menu.test.common;
+
+import fr.maxlego08.menu.api.utils.version.PluginVersion;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class PluginVersionTest {
+
+ // -------------------------------------------------------------------------
+ // toString / parsing
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testToString() {
+ Assertions.assertEquals("1.1.0", PluginVersion.parse("1.1.0").toString());
+ Assertions.assertEquals("61.2", PluginVersion.parse("61.2").toString());
+ Assertions.assertEquals("0", PluginVersion.parse("0").toString());
+ Assertions.assertEquals("1.0.0", PluginVersion.parse("1.0.0").toString());
+ }
+
+ @Test
+ void testParseNull() {
+ Assertions.assertEquals("0", PluginVersion.parse(null).toString());
+ }
+
+ @Test
+ void testParseBlank() {
+ Assertions.assertEquals("0", PluginVersion.parse("").toString());
+ Assertions.assertEquals("0", PluginVersion.parse(" ").toString());
+ }
+
+ @Test
+ void testParseWithBuildSuffix() {
+ // Everything after '-' should be ignored
+ Assertions.assertEquals("1.2.3", PluginVersion.parse("1.2.3-SNAPSHOT").toString());
+ Assertions.assertEquals("2.0", PluginVersion.parse("2.0-beta1").toString());
+ }
+
+ @Test
+ void testParseWithNonNumericChars() {
+ // Non-digit characters inside a segment are stripped
+ Assertions.assertEquals("1.2.3", PluginVersion.parse("v1.2.3").toString());
+ Assertions.assertEquals("1.2.3", PluginVersion.parse("1.2.3a").toString());
+ }
+
+ // -------------------------------------------------------------------------
+ // Leading zero stripping → 0.61.2 == 61.2
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testLeadingZeroSegmentsStripped() {
+ PluginVersion a = PluginVersion.parse("0.61.2");
+ PluginVersion b = PluginVersion.parse("61.2");
+ Assertions.assertEquals(0, a.compareTo(b), "0.61.2 should equal 61.2");
+ Assertions.assertTrue(a.isSameAs(b));
+ }
+
+ @Test
+ void testMultipleLeadingZeroSegmentsStripped() {
+ PluginVersion a = PluginVersion.parse("0.0.1");
+ PluginVersion b = PluginVersion.parse("1");
+ Assertions.assertTrue(a.isSameAs(b), "0.0.1 should equal 1");
+ }
+
+ @Test
+ void testSingleZeroVersion() {
+ PluginVersion a = PluginVersion.parse("0");
+ PluginVersion b = PluginVersion.parse("0.0");
+ // 0.0 strips leading zero → [0], same as [0]
+ Assertions.assertTrue(a.isSameAs(b), "0 should equal 0.0 after stripping");
+ }
+
+ // -------------------------------------------------------------------------
+ // Trailing zeros are significant → 61.2.0 > 61.2
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testTrailingZeroSegmentIsNewer() {
+ PluginVersion base = PluginVersion.parse("61.2");
+ PluginVersion withZero = PluginVersion.parse("61.2.0");
+ Assertions.assertTrue(withZero.isNewerThan(base), "61.2.0 should be newer than 61.2");
+ }
+
+ @Test
+ void testTrailingZeroSegmentMultiple() {
+ PluginVersion a = PluginVersion.parse("1.0.0");
+ PluginVersion b = PluginVersion.parse("1.0.0.0");
+ Assertions.assertTrue(b.isNewerThan(a), "1.0.0.0 should be newer than 1.0.0");
+ Assertions.assertTrue(a.isOlderThan(b), "1.0.0 should be older than 1.0.0.0");
+ }
+
+ // -------------------------------------------------------------------------
+ // compareTo / ordering
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testComparison() {
+ PluginVersion v1 = PluginVersion.parse("1.0.0");
+ PluginVersion v2 = PluginVersion.parse("1.1.0");
+ PluginVersion v3 = PluginVersion.parse("1.0.0");
+
+ Assertions.assertTrue(v1.compareTo(v2) < 0, "1.0.0 < 1.1.0");
+ Assertions.assertTrue(v2.compareTo(v1) > 0, "1.1.0 > 1.0.0");
+ Assertions.assertEquals(0, v1.compareTo(v3), "1.0.0 == 1.0.0");
+ }
+
+ @Test
+ void testMajorVersionDominates() {
+ PluginVersion v1 = PluginVersion.parse("2.0.0");
+ PluginVersion v2 = PluginVersion.parse("1.99.99");
+ Assertions.assertTrue(v1.isNewerThan(v2), "2.0.0 should be newer than 1.99.99");
+ }
+
+ @Test
+ void testMinorVersionComparison() {
+ PluginVersion v1 = PluginVersion.parse("1.2.0");
+ PluginVersion v2 = PluginVersion.parse("1.10.0");
+ Assertions.assertTrue(v1.isOlderThan(v2), "1.2.0 should be older than 1.10.0");
+ }
+
+ @Test
+ void testPatchVersionComparison() {
+ PluginVersion v1 = PluginVersion.parse("1.0.9");
+ PluginVersion v2 = PluginVersion.parse("1.0.10");
+ Assertions.assertTrue(v1.isOlderThan(v2), "1.0.9 should be older than 1.0.10");
+ }
+
+ @Test
+ void testDifferentSegmentCounts() {
+ PluginVersion v1 = PluginVersion.parse("1.0");
+ PluginVersion v2 = PluginVersion.parse("1.0.1");
+ Assertions.assertTrue(v1.isOlderThan(v2), "1.0 should be older than 1.0.1");
+ }
+
+ // -------------------------------------------------------------------------
+ // isNewerThan / isOlderThan / isSameAs helpers
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testHelperMethods() {
+ PluginVersion low = PluginVersion.parse("1.0");
+ PluginVersion high = PluginVersion.parse("2.0");
+ PluginVersion copy = PluginVersion.parse("1.0");
+
+ Assertions.assertTrue(high.isNewerThan(low));
+ Assertions.assertFalse(low.isNewerThan(high));
+ Assertions.assertTrue(low.isOlderThan(high));
+ Assertions.assertFalse(high.isOlderThan(low));
+ Assertions.assertTrue(low.isSameAs(copy));
+ Assertions.assertFalse(low.isSameAs(high));
+ }
+
+ // -------------------------------------------------------------------------
+ // equals / hashCode
+ // -------------------------------------------------------------------------
+
+ @Test
+ void testEqualsSymmetry() {
+ PluginVersion a = PluginVersion.parse("1.2.3");
+ PluginVersion b = PluginVersion.parse("1.2.3");
+ Assertions.assertEquals(a, b);
+ Assertions.assertEquals(b, a);
+ }
+
+ @Test
+ void testEqualsWithLeadingZeros() {
+ PluginVersion a = PluginVersion.parse("0.61.2");
+ PluginVersion b = PluginVersion.parse("61.2");
+ Assertions.assertEquals(a, b, "0.61.2 and 61.2 should be equal");
+ }
+
+ @Test
+ void testNotEquals() {
+ PluginVersion a = PluginVersion.parse("1.0");
+ PluginVersion b = PluginVersion.parse("1.0.0");
+ Assertions.assertNotEquals(a, b, "1.0 and 1.0.0 should not be equal (trailing zero is significant)");
+ }
+
+ @Test
+ void testHashCodeConsistentWithEquals() {
+ PluginVersion a = PluginVersion.parse("0.1.2");
+ PluginVersion b = PluginVersion.parse("1.2");
+ Assertions.assertEquals(a, b);
+ Assertions.assertEquals(a.hashCode(), b.hashCode(), "Equal versions must have the same hashCode");
+ }
+
+ @Test
+ void testHashCodeDifferentForDifferentVersions() {
+ PluginVersion a = PluginVersion.parse("1.0");
+ PluginVersion b = PluginVersion.parse("1.0.0");
+ Assertions.assertNotEquals(a.hashCode(), b.hashCode());
+ }
+}
\ No newline at end of file
diff --git a/Hooks/Bedrock/build.gradle.kts b/Hooks/Bedrock/build.gradle.kts
index 1540df234..8aad0a484 100644
--- a/Hooks/Bedrock/build.gradle.kts
+++ b/Hooks/Bedrock/build.gradle.kts
@@ -7,5 +7,5 @@ repositories {
dependencies {
compileOnly(projects.api)
- compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
+ compileOnly(libs.floodgate)
}
\ No newline at end of file
diff --git a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockInventory.java b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockInventory.java
index ef5e1002c..87ee568d1 100644
--- a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockInventory.java
+++ b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockInventory.java
@@ -1,6 +1,5 @@
package fr.maxlego08.menu.hooks.bedrock;
-import fr.maxlego08.menu.api.BedrockInventory;
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.animation.TitleAnimation;
import fr.maxlego08.menu.api.button.Button;
@@ -8,6 +7,7 @@
import fr.maxlego08.menu.api.button.dialogs.InputButton;
import fr.maxlego08.menu.api.engine.InventoryEngine;
import fr.maxlego08.menu.api.enums.bedrock.BedrockType;
+import fr.maxlego08.menu.api.inventory.bedrock.BedrockInventory;
import fr.maxlego08.menu.api.requirement.Action;
import fr.maxlego08.menu.api.requirement.ConditionalName;
import fr.maxlego08.menu.api.requirement.Requirement;
@@ -15,6 +15,7 @@
import fr.maxlego08.menu.api.utils.InventoryReplacement;
import fr.maxlego08.menu.api.utils.Placeholders;
import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
@@ -57,7 +58,7 @@ public String getName() {
}
@Override
- public String getName(Player player, InventoryEngine inventoryDefault, Placeholders placeholders) {
+ public String getName(@NotNull Player player, InventoryEngine inventoryDefault, Placeholders placeholders) {
if (!this.conditionalNames.isEmpty()) {
Optional optional = this.conditionalNames.stream().filter(conditionalName -> conditionalName.hasPermission(player, null, inventoryDefault, placeholders)).max(Comparator.comparingInt(ConditionalName::priority));
diff --git a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockManager.java b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockManager.java
index e71833b06..d2ceb18dc 100644
--- a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockManager.java
+++ b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/ZBedrockManager.java
@@ -1,6 +1,9 @@
package fr.maxlego08.menu.hooks.bedrock;
-import fr.maxlego08.menu.api.*;
+import fr.maxlego08.menu.api.BedrockManager;
+import fr.maxlego08.menu.api.Inventory;
+import fr.maxlego08.menu.api.InventoryManager;
+import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.button.bedrock.BedrockButton;
import fr.maxlego08.menu.api.button.dialogs.InputButton;
import fr.maxlego08.menu.api.configuration.Configuration;
@@ -9,6 +12,7 @@
import fr.maxlego08.menu.api.exceptions.DialogException;
import fr.maxlego08.menu.api.exceptions.DialogFileNotFound;
import fr.maxlego08.menu.api.exceptions.InventoryException;
+import fr.maxlego08.menu.api.inventory.bedrock.BedrockInventory;
import fr.maxlego08.menu.api.requirement.Requirement;
import fr.maxlego08.menu.api.utils.Loader;
import fr.maxlego08.menu.api.utils.Message;
diff --git a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/button/loader/BedrockButtonLoader.java b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/button/loader/BedrockButtonLoader.java
index 60bb150ed..113885a5f 100644
--- a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/button/loader/BedrockButtonLoader.java
+++ b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/button/loader/BedrockButtonLoader.java
@@ -9,6 +9,8 @@
import org.bukkit.plugin.Plugin;
import org.jspecify.annotations.NonNull;
+import java.util.Locale;
+
public class BedrockButtonLoader extends ButtonLoader {
public BedrockButtonLoader(Plugin plugin) {
@@ -23,7 +25,7 @@ public BedrockButton load(@NonNull YamlConfiguration configuration, @NonNull Str
BedrockImageType imageType;
String imageTypeStr = configuration.getString(path + ".image-type", "");
try {
- imageType = BedrockImageType.valueOf(imageTypeStr.toUpperCase());
+ imageType = BedrockImageType.valueOf(imageTypeStr.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException | NullPointerException e) {
imageType = BedrockImageType.NONE;
}
diff --git a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/listener/BedrockReplacementListener.java b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/listener/BedrockReplacementListener.java
index af4c00139..f2f4f5e59 100644
--- a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/listener/BedrockReplacementListener.java
+++ b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/listener/BedrockReplacementListener.java
@@ -1,11 +1,10 @@
package fr.maxlego08.menu.hooks.bedrock.listener;
-import fr.maxlego08.menu.api.BedrockInventory;
import fr.maxlego08.menu.api.BedrockManager;
import fr.maxlego08.menu.api.Inventory;
import fr.maxlego08.menu.api.event.events.PlayerOpenInventoryEvent;
+import fr.maxlego08.menu.api.inventory.bedrock.BedrockInventory;
import fr.maxlego08.menu.api.utils.InventoryReplacement;
-import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -28,9 +27,8 @@ public void onPlayerOpenInventory(PlayerOpenInventoryEvent event) {
if (this.bedrockManager.isBedrockPlayer(event.getPlayer())) {
InventoryReplacement inventoryReplacement = inventory.getInventoryReplacement();
if (inventoryReplacement != null) {
- Optional optionalBedrockInventory = this.bedrockManager.getBedrockInventory(inventoryReplacement.getPlugin(), inventoryReplacement.getInventoryName());
+ Optional optionalBedrockInventory = this.bedrockManager.getBedrockInventory(inventoryReplacement.plugin(), inventoryReplacement.inventoryName());
if (optionalBedrockInventory.isEmpty()) {
- Logger.info("Bedrock inventory not found for replacement: " + inventoryReplacement.getInventoryName() + " (plugin: " + inventoryReplacement.getPlugin() + ")");
return;
}
BedrockInventory bedrockInventory = optionalBedrockInventory.get();
diff --git a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/loader/BedrockLoader.java b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/loader/BedrockLoader.java
index 98f968b57..665b51429 100644
--- a/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/loader/BedrockLoader.java
+++ b/Hooks/Bedrock/src/main/java/fr/maxlego08/menu/hooks/bedrock/loader/BedrockLoader.java
@@ -1,6 +1,5 @@
package fr.maxlego08.menu.hooks.bedrock.loader;
-import fr.maxlego08.menu.api.BedrockInventory;
import fr.maxlego08.menu.api.InventoryOption;
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.button.Button;
@@ -9,6 +8,7 @@
import fr.maxlego08.menu.api.enums.bedrock.BedrockType;
import fr.maxlego08.menu.api.exceptions.InventoryButtonException;
import fr.maxlego08.menu.api.exceptions.InventoryException;
+import fr.maxlego08.menu.api.inventory.bedrock.BedrockInventory;
import fr.maxlego08.menu.api.requirement.Requirement;
import fr.maxlego08.menu.api.utils.Loader;
import fr.maxlego08.menu.hooks.bedrock.ZBedrockInventory;
@@ -20,10 +20,7 @@
import java.io.File;
import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
import java.util.function.BiConsumer;
public class BedrockLoader implements Loader {
@@ -47,7 +44,7 @@ public BedrockInventory load(YamlConfiguration configuration, String path, Objec
String typeString = configuration.getString("type", "SIMPLE");
BedrockType bedrockType;
try {
- bedrockType = BedrockType.valueOf(typeString.toUpperCase());
+ bedrockType = BedrockType.valueOf(typeString.toUpperCase(Locale.ROOT));
bedrockInventory.setBedrockType(bedrockType);
} catch (IllegalArgumentException e) {
throw new InventoryException("Invalid dialog type: " + typeString);
diff --git a/Hooks/BreweryX/build.gradle.kts b/Hooks/BreweryX/build.gradle.kts
index e29bce7e3..15f811ae3 100644
--- a/Hooks/BreweryX/build.gradle.kts
+++ b/Hooks/BreweryX/build.gradle.kts
@@ -6,5 +6,5 @@ repositories {
dependencies {
compileOnly(projects.common)
- compileOnly("com.dre.brewery:BreweryX:3.6.0")
+ compileOnly(libs.breweryx)
}
diff --git a/Hooks/BreweryX/src/main/java/fr/maxlego08/menu/hooks/BreweryXLoader.java b/Hooks/BreweryX/src/main/java/fr/maxlego08/menu/hooks/BreweryXLoader.java
index aec3cadc8..3e6e828ca 100644
--- a/Hooks/BreweryX/src/main/java/fr/maxlego08/menu/hooks/BreweryXLoader.java
+++ b/Hooks/BreweryX/src/main/java/fr/maxlego08/menu/hooks/BreweryXLoader.java
@@ -1,12 +1,16 @@
package fr.maxlego08.menu.hooks;
import com.dre.brewery.api.BreweryApi;
+import fr.maxlego08.menu.api.annotations.AutoMaterialLoader;
+import fr.maxlego08.menu.api.annotations.RequiresPlugin;
import fr.maxlego08.menu.api.loader.MaterialLoader;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NonNull;
+@AutoMaterialLoader
+@RequiresPlugin("BreweryX")
public class BreweryXLoader extends MaterialLoader {
public BreweryXLoader(){
super("breweryx");
diff --git a/Hooks/CraftEngine/build.gradle.kts b/Hooks/CraftEngine/build.gradle.kts
index fe85d461b..6e70e0e77 100644
--- a/Hooks/CraftEngine/build.gradle.kts
+++ b/Hooks/CraftEngine/build.gradle.kts
@@ -6,6 +6,6 @@ repositories {
dependencies {
compileOnly(projects.common)
- compileOnly("net.momirealms:craft-engine-core:26.5")
- compileOnly("net.momirealms:craft-engine-bukkit:26.5")
+ compileOnly(libs.craft.engine.core)
+ compileOnly(libs.craft.engine.bukkit)
}
\ No newline at end of file
diff --git a/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/CraftEngineLoader.java b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/CraftEngineLoader.java
index ea719fca0..128bc770c 100644
--- a/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/CraftEngineLoader.java
+++ b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/CraftEngineLoader.java
@@ -1,13 +1,17 @@
package fr.maxlego08.menu.hooks;
+import fr.maxlego08.menu.api.annotations.AutoMaterialLoader;
+import fr.maxlego08.menu.api.annotations.RequiresPlugin;
+import fr.maxlego08.menu.api.loader.MaterialLoader;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.item.BukkitItemDefinition;
-import fr.maxlego08.menu.api.loader.MaterialLoader;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NonNull;
+@AutoMaterialLoader
+@RequiresPlugin("CraftEngine")
public class CraftEngineLoader extends MaterialLoader {
public CraftEngineLoader() {
diff --git a/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRule.java b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRule.java
new file mode 100644
index 000000000..c69e403e8
--- /dev/null
+++ b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRule.java
@@ -0,0 +1,26 @@
+package fr.maxlego08.menu.hooks.rules;
+
+import fr.maxlego08.menu.api.rules.AbstractPluginItemRule;
+import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
+import net.momirealms.craftengine.bukkit.item.BukkitItemDefinition;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class CraftEngineRule extends AbstractPluginItemRule {
+
+ public CraftEngineRule(@NotNull List itemIds, boolean ignoreCase) {
+ super(itemIds, ignoreCase);
+ }
+
+ @Override
+ protected @Nullable String resolveId(@NotNull ItemStack itemStack) {
+ BukkitItemDefinition bukkitItemDefinition = CraftEngineItems.byItemStack(itemStack);
+ if (bukkitItemDefinition != null) {
+ return bukkitItemDefinition.id().asString();
+ }
+ return null;
+ }
+}
diff --git a/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRuleLoader.java b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRuleLoader.java
new file mode 100644
index 000000000..67e3c68fa
--- /dev/null
+++ b/Hooks/CraftEngine/src/main/java/fr/maxlego08/menu/hooks/rules/CraftEngineRuleLoader.java
@@ -0,0 +1,29 @@
+package fr.maxlego08.menu.hooks.rules;
+
+import fr.maxlego08.menu.api.annotations.AutoRuleLoader;
+import fr.maxlego08.menu.api.annotations.RequiresPlugin;
+import fr.maxlego08.menu.api.rules.Rule;
+import fr.maxlego08.menu.api.rules.loader.AbstractPluginRuleLoader;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+@AutoRuleLoader
+@RequiresPlugin("CraftEngine")
+public class CraftEngineRuleLoader extends AbstractPluginRuleLoader {
+ @Override
+ protected @NotNull Rule createRule(@NotNull List