diff --git a/API/src/main/java/fr/maxlego08/menu/api/ButtonManager.java b/API/src/main/java/fr/maxlego08/menu/api/ButtonManager.java
index cf4d5b7cd..87c50b403 100644
--- a/API/src/main/java/fr/maxlego08/menu/api/ButtonManager.java
+++ b/API/src/main/java/fr/maxlego08/menu/api/ButtonManager.java
@@ -9,6 +9,7 @@
import fr.maxlego08.menu.api.requirement.Action;
import fr.maxlego08.menu.api.requirement.Permissible;
import fr.maxlego08.menu.api.requirement.Requirement;
+import fr.maxlego08.menu.api.rules.loader.RuleLoader;
import fr.maxlego08.menu.api.utils.Loader;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
@@ -110,6 +111,16 @@ public interface ButtonManager {
*/
void registerAction(@NotNull ActionLoader actionLoader);
+ /**
+ * Registers a {@link RuleLoader} so its rule type (and aliases) can be used in item rules,
+ * for example in an {@code item_drag} button or an {@code item-rule} requirement.
+ *
+ * Unlike {@code @AutoRuleLoader}, which only scans the zMenu package, this lets external plugins add rule types.
+ *
+ * @param ruleLoader The {@link RuleLoader} instance to register.
+ */
+ void registerRuleLoader(@NotNull RuleLoader ruleLoader);
+
/**
* Retrieves an {@link Optional} {@link ActionLoader} based on a specific action key.
*
diff --git a/API/src/main/java/fr/maxlego08/menu/api/InventoryManager.java b/API/src/main/java/fr/maxlego08/menu/api/InventoryManager.java
index 212763cf0..2d536ec06 100644
--- a/API/src/main/java/fr/maxlego08/menu/api/InventoryManager.java
+++ b/API/src/main/java/fr/maxlego08/menu/api/InventoryManager.java
@@ -15,6 +15,7 @@
import fr.maxlego08.menu.api.itemstack.MenuItemStackConversion;
import fr.maxlego08.menu.api.loader.MaterialLoader;
import fr.maxlego08.menu.api.pagination.PaginationManager;
+import fr.maxlego08.menu.api.sound.SoundOption;
import fr.maxlego08.menu.api.utils.Message;
import fr.maxlego08.menu.api.utils.MetaUpdater;
import fr.maxlego08.menu.api.utils.Placeholders;
@@ -636,4 +637,14 @@ public interface InventoryManager extends Listener {
*/
PaginationManager getPaginationManager();
+ String serializeItemStack(ItemStack itemStack);
+
+ ItemStack deserializeItemStack(String serializedItemStack);
+
+ default SoundOption createSoundOption(String soundName, String categoryName, float pitch, float volume) {
+ return this.createSoundOption(soundName, categoryName, pitch, volume, false);
+ }
+
+ SoundOption createSoundOption(String soundName, String categoryName, float pitch, float volume, boolean isCustom);
+
}
\ No newline at end of file
diff --git a/API/src/main/java/fr/maxlego08/menu/api/button/PerformButton.java b/API/src/main/java/fr/maxlego08/menu/api/button/PerformButton.java
index a4859ce66..91ea97937 100644
--- a/API/src/main/java/fr/maxlego08/menu/api/button/PerformButton.java
+++ b/API/src/main/java/fr/maxlego08/menu/api/button/PerformButton.java
@@ -1,12 +1,14 @@
package fr.maxlego08.menu.api.button;
import com.tcoded.folialib.impl.PlatformScheduler;
+import com.tcoded.folialib.wrapper.task.WrappedTask;
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.configuration.Configuration;
import fr.maxlego08.menu.api.utils.Placeholders;
import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.jetbrains.annotations.Contract;
@@ -15,6 +17,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
public abstract class PerformButton extends SlotButton {
@@ -171,11 +174,12 @@ private void execute(@NotNull MenuPlugin plugin, @NotNull Player player, @NotNul
player.chat("/" + plugin.parse(player, command));
} else {
String finalCommand = command;
- Runnable runnable = () -> Bukkit.dispatchCommand(executor, plugin.parse(player, finalCommand));
- if (plugin.isFolia()) {
- if (executor instanceof Player) scheduler.runAtEntity(((Player) executor), w -> runnable.run());
- else scheduler.runNextTick(w -> runnable.run());
- } else runnable.run();
+ Consumer task = w -> Bukkit.dispatchCommand(executor, plugin.parse(player, finalCommand));
+ if (executor instanceof Entity entity) {
+ scheduler.runAtEntity(entity, task);
+ } else {
+ scheduler.runNextTick(task);
+ }
}
} catch (Exception exception) {
if (Configuration.enableDebug){
diff --git a/API/src/main/java/fr/maxlego08/menu/api/utils/CompatibilityUtil.java b/API/src/main/java/fr/maxlego08/menu/api/utils/CompatibilityUtil.java
index 0d4886747..7d3192a6a 100644
--- a/API/src/main/java/fr/maxlego08/menu/api/utils/CompatibilityUtil.java
+++ b/API/src/main/java/fr/maxlego08/menu/api/utils/CompatibilityUtil.java
@@ -14,6 +14,22 @@
*/
public class CompatibilityUtil {
+ /**
+ * getTopInventory is called on every move, click and packet, so the lookup is resolved once per view class.
+ * Holds null when the class has no such method.
+ */
+ private static final ClassValue TOP_INVENTORY_METHODS = new ClassValue<>() {
+ @Override
+ protected Method computeValue(@NotNull Class> type) {
+ try {
+ Method method = type.getMethod("getTopInventory");
+ method.setAccessible(true);
+ return method;
+ } catch (NoSuchMethodException | RuntimeException e) {
+ return null;
+ }
+ }
+ };
/**
* In API versions 1.20.6 and earlier, InventoryView is a class.
@@ -26,24 +42,22 @@ public class CompatibilityUtil {
*/
@Nullable
public static Inventory getTopInventory(@NotNull InventoryEvent event) {
- try {
- Object view = event.getView();
- Method getTopInventory = view.getClass().getMethod("getTopInventory");
- getTopInventory.setAccessible(true);
- return (Inventory) getTopInventory.invoke(view);
- } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
- return null;
- }
+ return getTopInventory(event.getView());
}
@Nullable
public static Inventory getTopInventory(@NotNull Player player) {
+ return getTopInventory((Object) player.getOpenInventory());
+ }
+
+ @Nullable
+ private static Inventory getTopInventory(@Nullable Object view) {
+ if (view == null) return null;
+ Method getTopInventory = TOP_INVENTORY_METHODS.get(view.getClass());
+ if (getTopInventory == null) return null;
try {
- Object view = player.getOpenInventory();
- Method getTopInventory = view.getClass().getMethod("getTopInventory");
- getTopInventory.setAccessible(true);
return (Inventory) getTopInventory.invoke(view);
- } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+ } catch (InvocationTargetException | IllegalAccessException e) {
return null;
}
}
diff --git a/API/src/main/java/fr/maxlego08/menu/api/utils/OfflinePlayerCache.java b/API/src/main/java/fr/maxlego08/menu/api/utils/OfflinePlayerCache.java
index 3e9444815..4f6e3e82e 100644
--- a/API/src/main/java/fr/maxlego08/menu/api/utils/OfflinePlayerCache.java
+++ b/API/src/main/java/fr/maxlego08/menu/api/utils/OfflinePlayerCache.java
@@ -19,10 +19,9 @@ public class OfflinePlayerCache {
*/
@NotNull
public static OfflinePlayer get(@NotNull String playerName) {
- return offlinePlayer.computeIfAbsent(playerName, name -> {
- Player player = Bukkit.getPlayer(name);
- return player != null ? player : Bukkit.getOfflinePlayer(name);
- });
+ Player player = Bukkit.getPlayer(playerName);
+ if (player != null) return player;
+ return offlinePlayer.computeIfAbsent(playerName, Bukkit::getOfflinePlayer);
}
public static void clearCache(){
diff --git a/API/src/main/java/fr/maxlego08/menu/api/utils/Tuples.java b/API/src/main/java/fr/maxlego08/menu/api/utils/Tuples.java
deleted file mode 100644
index ba6124606..000000000
--- a/API/src/main/java/fr/maxlego08/menu/api/utils/Tuples.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package fr.maxlego08.menu.api.utils;
-
-/**
- * Generic tuple class storing two values of different types.
- * Useful for returning pairs from methods where a dedicated class is unnecessary.
- *
- * @param Type of the first element
- * @param Type of the second element
- * @param first The first element in the tuple.
- * @param second The second element in the tuple.
- */
-public record Tuples(T first, U second) {
-}
diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/PapiUtils.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/PapiUtils.java
index d2b4de264..7598013f7 100644
--- a/Common/src/main/java/fr/maxlego08/menu/common/utils/PapiUtils.java
+++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/PapiUtils.java
@@ -1,17 +1,22 @@
package fr.maxlego08.menu.common.utils;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import fr.maxlego08.menu.api.configuration.Configuration;
import fr.maxlego08.menu.api.placeholder.Placeholder;
import org.bukkit.OfflinePlayer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
public class PapiUtils extends TranslationHelper {
+ private static final long MAXIMUM_CACHE_SIZE = 10_000;
+
private static volatile Placeholder placeholder;
- private transient final ConcurrentHashMap cache = new ConcurrentHashMap<>();
+ private transient volatile Cache cache;
+ private transient volatile long cacheDuration = -1;
private Placeholder use() {
if (placeholder == null) {
@@ -20,6 +25,20 @@ private Placeholder use() {
return placeholder;
}
+ private Cache getCache() {
+ long duration = Configuration.cachePlaceholderAPI;
+ Cache current = this.cache;
+ if (current == null || this.cacheDuration != duration) {
+ current = CacheBuilder.newBuilder()
+ .maximumSize(MAXIMUM_CACHE_SIZE)
+ .expireAfterWrite(Math.max(duration, 0), TimeUnit.MILLISECONDS)
+ .build();
+ this.cache = current;
+ this.cacheDuration = duration;
+ }
+ return current;
+ }
+
public Object papi(Object placeHolder, OfflinePlayer player, boolean useCache) {
return placeHolder instanceof String ? this.papi((String) placeHolder, player, useCache) : placeHolder;
}
@@ -28,22 +47,30 @@ public String papi(String placeHolder, OfflinePlayer player, boolean useCache) {
if (placeHolder == null) return null;
if (player == null) return placeHolder;
if (!placeHolder.contains("%")) return placeHolder;
- String cacheKey = placeHolder + ";" + player.getUniqueId();
- CacheEntry cachedResult = this.cache.get(cacheKey);
- if (Configuration.enableCachePlaceholderAPI && cachedResult != null && cachedResult.isValid() && useCache) {
- return cachedResult.value;
+ if (!Configuration.enableCachePlaceholderAPI) {
+ return this.parse(placeHolder, player);
}
- String result = this.use().setPlaceholders(player, placeHolder).replace("%player%", player.getName());
+ Cache cache = this.getCache();
+ String cacheKey = placeHolder + ";" + player.getUniqueId();
- if (Configuration.enableCachePlaceholderAPI) {
- this.cache.put(cacheKey, new CacheEntry(result, System.currentTimeMillis()));
+ if (useCache) {
+ String cachedResult = cache.getIfPresent(cacheKey);
+ if (cachedResult != null) return cachedResult;
}
+ String result = this.parse(placeHolder, player);
+ cache.put(cacheKey, result);
return result;
}
+ private String parse(String placeHolder, OfflinePlayer player) {
+ String result = this.use().setPlaceholders(player, placeHolder);
+ String playerName = player.getName();
+ return playerName == null ? result : result.replace("%player%", playerName);
+ }
+
public List papi(List placeHolders, OfflinePlayer player, boolean useCache) {
if (player == null) return placeHolders;
List parsed = new ArrayList<>(placeHolders.size());
@@ -52,15 +79,4 @@ public List papi(List placeHolders, OfflinePlayer player, boolea
}
return parsed;
}
-
- /**
- * @param timeStamp Time when the cache entry was created
- */
- private record CacheEntry(String value, long timeStamp) {
-
- public boolean isValid() {
- // Check if the cache entry is still valid (not older than 1000 milliseconds)
- return System.currentTimeMillis() - this.timeStamp consumer) {
- return this.scheduleFix(plugin, delay, delay, consumer);
- }
-
- /**
- * Schedules a fixed-rate execution of a task associated with the given plugin.
- *
- * @param plugin the MenuPlugin for which the task is scheduled
- * @param startAt the delay in milliseconds before the task is first executed
- * @param delay the delay in milliseconds between successive task executions
- * @param consumer the BiConsumer to be executed with the TimerTask and a boolean indicating the task's success
- * @return the scheduled TimerTask
- */
- protected TimerTask scheduleFix(MenuPlugin plugin, long startAt, long delay, BiConsumer consumer) {
- TimerTask task = new TimerTask() {
- @Override
- public void run() {
- if (!plugin.isEnabled()) {
- this.cancel();
- consumer.accept(this, false);
- return;
- }
- plugin.getScheduler().runNextTick(w -> consumer.accept(this, true));
- }
- };
- TIMER.scheduleAtFixedRate(task, startAt, delay);
- return task;
- }
-
protected String color(String message) {
if (message == null) return null;
- if (NMSUtils.isHexColor()) {
- Pattern pattern = Pattern.compile("#[a-fA-F0-9]{6}");
- Matcher matcher = pattern.matcher(message);
+ if (NMSUtils.isHexColor() && message.indexOf('#') >= 0) {
+ Matcher matcher = HEX_COLOR_PATTERN.matcher(message);
+ StringBuilder builder = new StringBuilder(message.length() + 32);
while (matcher.find()) {
- String color = message.substring(matcher.start(), matcher.end());
- message = message.replace(color, String.valueOf(net.md_5.bungee.api.ChatColor.of(color)));
- matcher = pattern.matcher(message);
+ matcher.appendReplacement(builder, Matcher.quoteReplacement(net.md_5.bungee.api.ChatColor.of(matcher.group()).toString()));
}
+ message = matcher.appendTail(builder).toString();
}
return net.md_5.bungee.api.ChatColor.translateAlternateColorCodes('&', message);
}
public String colorReverse(String message) {
- Pattern pattern = Pattern.compile(net.md_5.bungee.api.ChatColor.COLOR_CHAR + "x[a-fA-F0-9-" + net.md_5.bungee.api.ChatColor.COLOR_CHAR + "]{12}");
- Matcher matcher = pattern.matcher(message);
+ Matcher matcher = HEX_COLOR_REVERSE_PATTERN.matcher(message);
+ StringBuilder builder = new StringBuilder(message.length());
while (matcher.find()) {
- String color = message.substring(matcher.start(), matcher.end());
- String colorReplace = color.replace("§x", "#");
- colorReplace = colorReplace.replace("§", "");
- message = message.replace(color, colorReplace);
- matcher = pattern.matcher(message);
+ String colorReplace = matcher.group().replace("§x", "#").replace("§", "");
+ matcher.appendReplacement(builder, Matcher.quoteReplacement(colorReplace));
}
- return message.replace("§", "&");
+ return matcher.appendTail(builder).toString().replace("§", "&");
}
protected List color(List messages) {
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 5be32e82e..134038e6d 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
@@ -10,14 +10,11 @@
import java.io.*;
import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.Map;
import java.util.zip.GZIPInputStream;
public class ItemStackUtils {
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,10 +23,6 @@ public static String serializeItemStack(ItemStack paramItemStack) {
return "null";
}
- if (itemStackSerialized.containsKey(paramItemStack)) {
- return itemStackSerialized.get(paramItemStack);
- }
-
if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.parse("1.20.5"))) {
return Base64ItemStack.encode(paramItemStack);
}
@@ -60,9 +53,7 @@ public static String serializeItemStack(ItemStack paramItemStack) {
Logger.error(localException);
}
}
- String string = Base64.encode(localByteArrayOutputStream.toByteArray());
- itemStackSerialized.put(paramItemStack, string);
- return string;
+ return Base64.encode(localByteArrayOutputStream.toByteArray());
}
public static ItemStack deserializeItemStack(String paramString) {
@@ -116,8 +107,6 @@ public static ItemStack deserializeItemStack(String paramString) {
} catch (Exception localException) {
// Logger.error(localException);
}
- if (localItemStack != null && !itemStackSerialized.containsKey(localItemStack))
- itemStackSerialized.put(localItemStack, paramString);
return localItemStack;
}
diff --git a/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
index 388bb618b..0ddeb0cc3 100644
--- a/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
+++ b/Common/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java
@@ -6,6 +6,7 @@
import fr.maxlego08.menu.api.MenuItemStack;
import fr.maxlego08.menu.api.MenuPlugin;
import fr.maxlego08.menu.api.button.Button;
+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.engine.ItemButton;
@@ -13,7 +14,6 @@
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.configuration.Configuration;
import fr.maxlego08.menu.api.requirement.RefreshRequirement;
import fr.maxlego08.menu.api.requirement.Requirement;
import fr.maxlego08.menu.api.utils.Placeholders;
@@ -31,6 +31,7 @@
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -38,7 +39,7 @@
*/
public class InventoryDefault extends VInventory implements InventoryEngine {
- private final Map timers = new ConcurrentHashMap<>();
+ private final Map timers = new ConcurrentHashMap<>();
private PerformanceDebug perfDebug;
private Inventory inventory;
private List oldInventories = new ArrayList<>();
@@ -184,7 +185,7 @@ public void postOpen(MenuPlugin plugin, Player player, int page, Object[] object
@Override
protected void onClose(InventoryCloseEvent event, MenuPlugin plugin, Player player) {
-
+ this.cancelTimers();
this.inventory.closeInventory(player, this);
this.buttons.forEach(button -> button.onInventoryClose(player, this));
}
@@ -396,19 +397,9 @@ public void displayFinalButton(@NotNull Button button, @NotNull Placeholders pla
if (needRefresh || button.isUpdated()) {
long interval = refreshRequirement != null ? refreshRequirement.getUpdateInterval() : this.inventory.getUpdateInterval();
- TimerTask timerTask = this.scheduleFix(this.plugin, interval, (task, canRun) -> {
- if (!canRun) {
- return;
- }
-
+ WrappedTask timerTask = this.plugin.getScheduler().runAtEntityTimer(this.player, () -> {
if (this.isClose()) {
- task.cancel();
- return;
- }
-
- TimerTask tTask = this.timers.get(slot);
- if (!task.equals(tTask)) {
- task.cancel();
+ this.cancelTimers();
return;
}
@@ -418,9 +409,12 @@ public void displayFinalButton(@NotNull Button button, @NotNull Placeholders pla
} else if (button.isUpdated()) {
this.handleUpdatedButton(button, itemStack, slot, placeholders);
}
- });
+ }, interval, interval, TimeUnit.MILLISECONDS);
- this.timers.put(slot, timerTask);
+ WrappedTask previousTask = this.timers.put(slot, timerTask);
+ if (previousTask != null) {
+ previousTask.cancel();
+ }
}
}
}
@@ -428,12 +422,17 @@ public void displayFinalButton(@NotNull Button button, @NotNull Placeholders pla
@Override
public void cancel(int slot) {
- TimerTask task = this.timers.remove(slot);
+ WrappedTask task = this.timers.remove(slot);
if (task != null) {
task.cancel();
}
}
+ private void cancelTimers() {
+ this.timers.values().forEach(WrappedTask::cancel);
+ this.timers.clear();
+ }
+
@Override
public @NotNull PerformanceDebug getPerformanceDebug() {
return this.perfDebug != null ? this.perfDebug : PerformanceDebug.disabled();
diff --git a/Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/players/ZData.java b/Common/src/main/java/fr/maxlego08/menu/players/ZData.java
similarity index 100%
rename from Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/players/ZData.java
rename to Common/src/main/java/fr/maxlego08/menu/players/ZData.java
diff --git a/Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/hooks/mythicmobs/MythicMobsItemsLoader.java b/Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/hooks/mythicmobs/MythicMobsItemsLoader.java
index 337ad8ee9..da4265030 100644
--- a/Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/hooks/mythicmobs/MythicMobsItemsLoader.java
+++ b/Hooks/MythicMobs/src/main/java/fr/maxlego08/menu/hooks/mythicmobs/MythicMobsItemsLoader.java
@@ -5,6 +5,7 @@
import fr.maxlego08.menu.api.configuration.Configuration;
import fr.maxlego08.menu.api.loader.MaterialLoader;
import fr.maxlego08.menu.zcore.logger.Logger;
+import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.core.items.ItemExecutor;
import io.lumine.mythic.core.items.MythicItem;
@@ -34,6 +35,6 @@ public ItemStack load(@NonNull Player player, @NonNull YamlConfiguration configu
Logger.info("MythicMobsItemsLoader: Mythic item '" + materialString + "' not found, available items: " + this.itemManager.getItemNames());
return null;
}
- return mythicItem.get().getCachedBaseItem();
+ return BukkitAdapter.adapt(mythicItem.get().generateItemStack(1));
}
}
diff --git a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/PacketUtils.java b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/PacketUtils.java
index cfa33bba9..4e2212ebe 100644
--- a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/PacketUtils.java
+++ b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/PacketUtils.java
@@ -27,9 +27,9 @@
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
-import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
public class PacketUtils implements InventoryListener, PacketManager {
@@ -37,7 +37,7 @@ public class PacketUtils implements InventoryListener, PacketManager {
private PacketTitleListener packetTitleListener;
private PacketEventClickLimiterListener packetEventClickLimiterListener;
- public static final Map fakeContents = new HashMap<>();
+ public static final Map fakeContents = new ConcurrentHashMap<>();
private final MenuPlugin plugin;
private boolean ownsApi;
@@ -109,6 +109,7 @@ public void onPostEnable() {
@SuppressWarnings("ConstantConditions")
public void onDisable() {
this.ready = false;
+ fakeContents.clear();
if (this.ownsApi && PacketEvents.getAPI() != null) {
this.api().terminate();
}
@@ -139,11 +140,6 @@ public void onInventoryPreOpen(Player player, BaseInventory inventory, int page,
@Override
public void onInventoryPostOpen(Player player, BaseInventory inventory) {
- if (fakeContents.containsKey(inventory.getPlayer().getUniqueId())) {
- FakeInventory fakeInventory = fakeContents.get(inventory.getPlayer().getUniqueId());
- Logger.info("OPEN");
- Logger.info(fakeInventory.getSlots().toString());
- }
}
@Override
diff --git a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketTitleListener.java b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketTitleListener.java
index 82a20820e..538e2ad7d 100644
--- a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketTitleListener.java
+++ b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketTitleListener.java
@@ -7,6 +7,7 @@
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerOpenWindow;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowItems;
+import fr.maxlego08.menu.hooks.packetevents.PacketUtils;
import org.bukkit.entity.Player;
import java.util.Map;
@@ -43,6 +44,7 @@ public void onUserDisconnect(UserDisconnectEvent event) {
UUID playerUniqueId = event.getUser().getUUID();
if (playerUniqueId != null) {
this.playerPacketInformation.remove(playerUniqueId);
+ PacketUtils.fakeContents.remove(playerUniqueId);
}
}
diff --git a/README.md b/README.md
index 7b32d0f8a..8bd07322e 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,17 @@ items:
- "&aYou clicked the button!"
```
+### Inventory Types
+
+The `type` field sets the container type (default `CHEST`).
+
+| Type | Minimum Minecraft version | Notes |
+|---|---|---|
+| `CHEST` | 1.20.6 | Default. Size must be a multiple of 9. |
+| `ANVIL` | 1.21 | Enables `rename-requirements`. On older versions, zMenu logs an error at load time and the menu opens as a chest, so rename requirements do not run. |
+
+`CRAFTING` and `PLAYER` are not supported.
+
## API Usage
**Maven**
diff --git a/build.gradle.kts b/build.gradle.kts
index 96021249c..8aa43cf17 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -101,6 +101,11 @@ allprojects {
options.encoding = "UTF-8"
}
+ tasks.withType().configureEach {
+ isPreserveFileTimestamps = false
+ isReproducibleFileOrder = true
+ }
+
if (project.name == "API") {
tasks.javadoc {
options.encoding = "UTF-8"
@@ -171,6 +176,7 @@ dependencies {
dependencies {
compileOnly(project(":Hooks:Paper-26"))
+ compileOnly(project(":Hooks:Paper-26-3"))
}
@@ -180,6 +186,9 @@ tasks {
dependsOn(paper26.tasks.jar)
val jarFile = paper26.tasks.jar.get().archiveFile.get().asFile
from(zipTree(jarFile))
+ val paper263 = project(":Hooks:Paper-26-3")
+ dependsOn(paper263.tasks.jar)
+ from(zipTree(paper263.tasks.jar.get().archiveFile.get().asFile))
relocate("com.tcoded.folialib", "fr.maxlego08.menu.hooks.folialib")
relocate("fr.traqueur.currencies", "fr.maxlego08.menu.hooks.currencies")
diff --git a/src/main/java/fr/maxlego08/menu/ZButtonManager.java b/src/main/java/fr/maxlego08/menu/ZButtonManager.java
index 4c132858d..6599f3955 100644
--- a/src/main/java/fr/maxlego08/menu/ZButtonManager.java
+++ b/src/main/java/fr/maxlego08/menu/ZButtonManager.java
@@ -13,11 +13,13 @@
import fr.maxlego08.menu.api.requirement.Action;
import fr.maxlego08.menu.api.requirement.Permissible;
import fr.maxlego08.menu.api.requirement.Requirement;
+import fr.maxlego08.menu.api.rules.loader.RuleLoader;
import fr.maxlego08.menu.api.utils.Loader;
import fr.maxlego08.menu.api.utils.TypedMapAccessor;
import fr.maxlego08.menu.common.utils.ZUtils;
import fr.maxlego08.menu.loader.RequirementLoader;
import fr.maxlego08.menu.loader.ZButtonLoader;
+import fr.maxlego08.menu.registry.ZRuleLoaderRegistry;
import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -156,11 +158,16 @@ public void registerPermissible(@NotNull PermissibleLoader permissibleLoader) {
@Override
public void registerAction(@NotNull ActionLoader actionLoader) {
for (String value : actionLoader.getKeys()) {
- this.actionsLoader.put(value, actionLoader);
+ this.actionsLoader.put(value.toLowerCase(Locale.ROOT), actionLoader);
this.plugin.getInventoryManager().loadElement(InventoryRequirementType.ACTION, value);
}
}
+ @Override
+ public void registerRuleLoader(@NotNull RuleLoader ruleLoader) {
+ ZRuleLoaderRegistry.getInstance().register(ruleLoader);
+ }
+
@Override
public @NotNull Optional getActionLoader(@NotNull String key) {
return Optional.ofNullable(this.actionsLoader.getOrDefault(key.toLowerCase(Locale.ROOT), null));
diff --git a/src/main/java/fr/maxlego08/menu/ZCommandManager.java b/src/main/java/fr/maxlego08/menu/ZCommandManager.java
index 01f94006d..9aeb43a81 100644
--- a/src/main/java/fr/maxlego08/menu/ZCommandManager.java
+++ b/src/main/java/fr/maxlego08/menu/ZCommandManager.java
@@ -27,11 +27,12 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
public class ZCommandManager extends ZUtils implements CommandManager {
private final Map> commands = new HashMap<>();
- private final Map> playerArguments = new HashMap<>();
+ private final Map> playerArguments = new ConcurrentHashMap<>();
private final List commandArgumentValidators = new ArrayList<>();
private final ZMenuPlugin plugin;
@@ -107,6 +108,7 @@ public void unregisterCommands(Command command) {
public void loadCommands() {
this.unregisterCommands(this.plugin);
+ this.playerArguments.clear();
// Check if file exist
File folder = new File(this.plugin.getDataFolder(), "commands");
@@ -182,10 +184,11 @@ public Optional getCommand(Inventory inventory) {
@Override
public void setPlayerArgument(Player player, String key, String value) {
- Map arguments = this.playerArguments.getOrDefault(player.getUniqueId(), new HashMap<>());
- arguments.put(key, value);
- this.playerArguments.put(player.getUniqueId(), arguments);
+ this.playerArguments.computeIfAbsent(player.getUniqueId(), uuid -> new HashMap<>()).put(key, value);
+ }
+ public void removePlayerArguments(UUID uuid) {
+ this.playerArguments.remove(uuid);
}
@Override
diff --git a/src/main/java/fr/maxlego08/menu/ZInventoryManager.java b/src/main/java/fr/maxlego08/menu/ZInventoryManager.java
index 669863675..437af0dde 100644
--- a/src/main/java/fr/maxlego08/menu/ZInventoryManager.java
+++ b/src/main/java/fr/maxlego08/menu/ZInventoryManager.java
@@ -1,5 +1,6 @@
package fr.maxlego08.menu;
+import com.cryptomorin.xseries.XSound;
import com.tcoded.folialib.impl.PlatformScheduler;
import fr.maxlego08.menu.api.*;
import fr.maxlego08.menu.api.annotations.*;
@@ -21,6 +22,7 @@
import fr.maxlego08.menu.api.itemstack.MenuItemStackConversion;
import fr.maxlego08.menu.api.loader.*;
import fr.maxlego08.menu.api.pagination.PaginationManager;
+import fr.maxlego08.menu.api.sound.SoundOption;
import fr.maxlego08.menu.api.utils.*;
import fr.maxlego08.menu.api.utils.version.VersionFilter;
import fr.maxlego08.menu.button.buttons.ZNoneButton;
@@ -43,6 +45,7 @@
import fr.maxlego08.menu.loader.deluxemenu.InventoryDeluxeMenuLoader;
import fr.maxlego08.menu.pagination.ZPaginationManager;
import fr.maxlego08.menu.requirement.checker.InventoryRequirementChecker;
+import fr.maxlego08.menu.sound.ZSoundOption;
import fr.maxlego08.menu.zcore.logger.Logger;
import fr.maxlego08.menu.zcore.logger.Logger.LogType;
import fr.maxlego08.menu.zcore.utils.PerformanceDebug;
@@ -152,6 +155,24 @@ public PaginationManager getPaginationManager() {
return this.paginationManager;
}
+ @Override
+ public String serializeItemStack(ItemStack itemStack) {
+ return ItemStackUtils.serializeItemStack(itemStack);
+ }
+
+ @Override
+ public ItemStack deserializeItemStack(String serializedItemStack) {
+ return ItemStackUtils.deserializeItemStack(serializedItemStack);
+ }
+
+ @Override
+ public SoundOption createSoundOption(String soundName, String categoryName, float pitch, float volume, boolean isCustom) {
+ Optional xSound = XSound.of(soundName);
+
+ return xSound.map(sound -> new ZSoundOption(sound, categoryName, soundName, pitch, volume, isCustom)).orElse(null);
+
+ }
+
@Override
public Inventory loadInventory(Plugin plugin, File file) throws InventoryException {
return this.loadInventory(plugin, file, ZInventory.class);
diff --git a/src/main/java/fr/maxlego08/menu/ZMenuPlugin.java b/src/main/java/fr/maxlego08/menu/ZMenuPlugin.java
index 67dec6446..720da3e3a 100644
--- a/src/main/java/fr/maxlego08/menu/ZMenuPlugin.java
+++ b/src/main/java/fr/maxlego08/menu/ZMenuPlugin.java
@@ -91,8 +91,8 @@
import org.bukkit.plugin.ServicesManager;
import java.io.File;
-import java.util.concurrent.TimeUnit;
import java.util.*;
+import java.util.concurrent.TimeUnit;
/**
* System to create your plugins very simply Projet with https://github.com/Maxlego08/TemplatePlugin
@@ -273,9 +273,7 @@ public void onEnable() {
this.clientVersionManager.registerProvider(new ProtocolSupportClientVersionProvider());
});
}
- if (this.isPaperOrFolia()) {
- this.safeHook("Paper client version", () -> this.clientVersionManager.registerProvider(new PaperProtocolClientVersionProvider()));
- }
+ this.safeHook("Paper client version", () -> this.clientVersionManager.registerProvider(new PaperProtocolClientVersionProvider()));
this.addListener(this.clientVersionManager);
servicesManager.register(ClientVersionManager.class, this.clientVersionManager, this, ServicePriority.Highest);
@@ -526,6 +524,7 @@ public void onDisable() {
this.safeDisable("configuration", () -> Configuration.getInstance().save(this.getConfig(), this.configFile));
this.safeDisable("file cache", YamlFileCache::clearCache);
+ this.safeDisable("api offline player cache", fr.maxlego08.menu.api.utils.OfflinePlayerCache::clearCache);
if (this.websiteManager != null) this.safeDisable("website manager", this.websiteManager::onDisable);
@@ -565,8 +564,7 @@ public void onDisable() {
* @return true if dialog support can be loaded
*/
private boolean isDialogCapableServer() {
- return this.isPaperOrFolia()
- && MinecraftVersion.getCurrentVersion().isAtLeast(DIALOG_MINIMUM_SERVER_VERSION)
+ return MinecraftVersion.getCurrentVersion().isAtLeast(DIALOG_MINIMUM_SERVER_VERSION)
&& this.hasClass("io{}papermc{}paper{}registry{}data{}dialog{}action{}DialogAction");
}
diff --git a/src/main/java/fr/maxlego08/menu/ZTitleAnimationManager.java b/src/main/java/fr/maxlego08/menu/ZTitleAnimationManager.java
index d958eb9fd..8ce700081 100644
--- a/src/main/java/fr/maxlego08/menu/ZTitleAnimationManager.java
+++ b/src/main/java/fr/maxlego08/menu/ZTitleAnimationManager.java
@@ -4,12 +4,12 @@
import fr.maxlego08.menu.api.animation.TitleAnimationLoader;
import org.jetbrains.annotations.NotNull;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
public class ZTitleAnimationManager implements TitleAnimationManager {
- private final Map loaders = new HashMap<>();
+ private final Map loaders = new LinkedHashMap<>();
public ZTitleAnimationManager() {
}
@@ -25,7 +25,9 @@ public boolean registerLoader(@NotNull String id, @NotNull TitleAnimationLoader
@Override
public Optional getLoader(@NotNull String id) {
- return Optional.ofNullable(this.loaders.get(id));
+ TitleAnimationLoader loader = this.loaders.get(id);
+ if (loader != null) return Optional.of(loader);
+ return this.loaders.entrySet().stream().filter(entry -> entry.getKey().equalsIgnoreCase(id)).map(Map.Entry::getValue).findFirst();
}
@Override
diff --git a/src/main/java/fr/maxlego08/menu/button/buttons/ZItemDragButton.java b/src/main/java/fr/maxlego08/menu/button/buttons/ZItemDragButton.java
index 71db2e509..79028f2b6 100644
--- a/src/main/java/fr/maxlego08/menu/button/buttons/ZItemDragButton.java
+++ b/src/main/java/fr/maxlego08/menu/button/buttons/ZItemDragButton.java
@@ -24,6 +24,7 @@
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
public class ZItemDragButton extends ItemDragButton {
@@ -41,7 +42,7 @@ public class ZItemDragButton extends ItemDragButton {
private boolean enableErrorItem = false;
private MenuItemStack errorItems;
private boolean useErrorItemCache = true;
- private final Map> activeTasks = new HashMap<>();
+ private final Map> activeTasks = new ConcurrentHashMap<>();
private int ticks;
public ZItemDragButton(@NotNull MenuPlugin plugin) {
@@ -123,6 +124,7 @@ public void onRender(Player player, InventoryEngine inventoryEngine) {
@Override
public void onInventoryClose(@NonNull Player player, @NonNull InventoryEngine inventory) {
+ this.activeTasks.remove(player.getUniqueId());
this.slots.forEach(slot -> {
ItemStack itemStack = inventory.getInventory().getItem(slot);
diff --git a/src/main/java/fr/maxlego08/menu/button/loader/ItemDragLoader.java b/src/main/java/fr/maxlego08/menu/button/loader/ItemDragLoader.java
index 461475eb1..5b580b05e 100644
--- a/src/main/java/fr/maxlego08/menu/button/loader/ItemDragLoader.java
+++ b/src/main/java/fr/maxlego08/menu/button/loader/ItemDragLoader.java
@@ -12,6 +12,7 @@
import fr.maxlego08.menu.button.buttons.ZItemDragButton;
import fr.maxlego08.menu.itemstack.FullSimilar;
import fr.maxlego08.menu.registry.ZRuleLoaderRegistry;
+import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jspecify.annotations.NonNull;
@@ -36,7 +37,10 @@ public Button load(@NonNull YamlConfiguration configuration, @NonNull String pat
if (check_element != null) {
MenuItemStack menuItemStack = this.inventoryManager.loadItemStack(configuration, path + check_element + ".item.", defaultButtonValue.getFile());
String type = configuration.getString(path + check_element + ".type", "full");
- ItemStackSimilar itemStackSimilar = this.inventoryManager.getItemStackVerification(type).orElseGet(FullSimilar::new);
+ ItemStackSimilar itemStackSimilar = this.inventoryManager.getItemStackVerification(type).orElseGet(() -> {
+ Logger.info("Unknown check-item type '" + type + "' for the item_drag button " + path + " in " + defaultButtonValue.getFile().getName() + ", using 'full'.", Logger.LogType.WARNING);
+ return new FullSimilar();
+ });
button.setCheckItem(menuItemStack, itemStackSimilar);
}
@@ -50,6 +54,10 @@ public Button load(@NonNull YamlConfiguration configuration, @NonNull String pat
ConfigurationSection ruleSection = configuration.getConfigurationSection(path + "rule");
if (ruleSection != null) {
Rule rule = ZRuleLoaderRegistry.getInstance().loadRule(ruleSection.getValues(true));
+ if (rule == null || !rule.isValid()) {
+ Logger.info("The rule of the item_drag button " + path + " in " + defaultButtonValue.getFile().getName() + " is invalid, the button will refuse every item.", Logger.LogType.WARNING);
+ rule = context -> false;
+ }
button.setRule(rule);
}
diff --git a/src/main/java/fr/maxlego08/menu/command/commands/CommandDumplog.java b/src/main/java/fr/maxlego08/menu/command/commands/CommandDumplog.java
index 9ef006208..8ee702937 100644
--- a/src/main/java/fr/maxlego08/menu/command/commands/CommandDumplog.java
+++ b/src/main/java/fr/maxlego08/menu/command/commands/CommandDumplog.java
@@ -40,7 +40,7 @@ public CommandDumplog(ZMenuPlugin plugin) {
this.plugin.getScheduler().runAsync(var -> {
try {
String url = this.uploadLog(logPath).replace("\\","");
- this.plugin.getScheduler().runNextTick(task -> MessageUtils.message(this.plugin, sender, Message.DUMPLOG_SUCCESS, "%url%", this.plugin.isSpigot() ? url : "" + url + ""));
+ this.plugin.getScheduler().runNextTick(task -> MessageUtils.message(this.plugin, sender, Message.DUMPLOG_SUCCESS, "%url%", "" + url + ""));
if (Configuration.enableInformationMessage) {
Logger.info("Log uploaded: " + url);
}
diff --git a/src/main/java/fr/maxlego08/menu/command/commands/reload/CommandMenuReload.java b/src/main/java/fr/maxlego08/menu/command/commands/reload/CommandMenuReload.java
index 1d324473f..dba63e985 100644
--- a/src/main/java/fr/maxlego08/menu/command/commands/reload/CommandMenuReload.java
+++ b/src/main/java/fr/maxlego08/menu/command/commands/reload/CommandMenuReload.java
@@ -7,6 +7,7 @@
import fr.maxlego08.menu.api.configuration.Configuration;
import fr.maxlego08.menu.api.pattern.PatternManager;
import fr.maxlego08.menu.api.utils.Message;
+import fr.maxlego08.menu.api.utils.OfflinePlayerCache;
import fr.maxlego08.menu.common.enums.Permission;
import fr.maxlego08.menu.common.utils.MessageUtils;
import fr.maxlego08.menu.common.utils.cache.YamlFileCache;
@@ -16,8 +17,6 @@
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
-import java.util.Collection;
-
public class CommandMenuReload extends SubCommand {
public CommandMenuReload(ZMenuPlugin plugin) {
@@ -33,6 +32,7 @@ public CommandMenuReload(ZMenuPlugin plugin) {
InventoryManager inventoryManager = this.plugin.getInventoryManager();
YamlFileCache.clearCache();
+ OfflinePlayerCache.clearCache();
this.plugin.getMetaUpdater().clearCache();
this.plugin.loadGlobalPlaceholders();
@@ -56,12 +56,9 @@ public CommandMenuReload(ZMenuPlugin plugin) {
ItemManager itemManager = this.plugin.getItemManager();
itemManager.reloadCustomItems();
- Collection extends Player> onlinePlayers = this.plugin.getServer().getOnlinePlayers();
- this.plugin.getScheduler().runAsync(w->{
- for (Player player : onlinePlayers) {
- itemManager.executeCheckInventoryItems(player);
- }
- });
+ for (Player player : this.plugin.getServer().getOnlinePlayers()) {
+ this.plugin.getScheduler().runAtEntity(player, w -> itemManager.executeCheckInventoryItems(player));
+ }
this.plugin.getDataManager().loadDefaultValues();
diff --git a/src/main/java/fr/maxlego08/menu/command/commands/website/CommandMenuDownload.java b/src/main/java/fr/maxlego08/menu/command/commands/website/CommandMenuDownload.java
index 4a62beb9c..c2e1e401f 100644
--- a/src/main/java/fr/maxlego08/menu/command/commands/website/CommandMenuDownload.java
+++ b/src/main/java/fr/maxlego08/menu/command/commands/website/CommandMenuDownload.java
@@ -27,10 +27,10 @@ public CommandMenuDownload(ZMenuPlugin plugin) {
String link = commandDispatch.getArgument("link", String.class);
boolean force = commandDispatch.getArgument("force", Boolean.class, false);
- if (Configuration.enableDownloadCommand) {
+ if (!Configuration.enableDownloadCommand) {
Logger.info(commandDispatch.getSender().getName() + " try to download the link '" + link + "' (force: " + force + ") while the command is disable !", Logger.LogType.WARNING);
- MessageUtils.message(commandDispatch.getPlugin(), commandDispatch.getSender(), "&cThe command is disable for safety reason, you need to enable it in config.json.");
+ MessageUtils.message(commandDispatch.getPlugin(), commandDispatch.getSender(), "&cThe command is disable for safety reason, you need to enable it in config.yml.");
return CommandResultType.SUCCESS;
}
diff --git a/src/main/java/fr/maxlego08/menu/inventory/VInventoryManager.java b/src/main/java/fr/maxlego08/menu/inventory/VInventoryManager.java
index 1fcba80f0..73ab7699d 100644
--- a/src/main/java/fr/maxlego08/menu/inventory/VInventoryManager.java
+++ b/src/main/java/fr/maxlego08/menu/inventory/VInventoryManager.java
@@ -2,8 +2,7 @@
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
-import fr.maxlego08.menu.zcore.logger.Logger;
-
+import fr.maxlego08.menu.ZCommandManager;
import fr.maxlego08.menu.ZMenuPlugin;
import fr.maxlego08.menu.api.VInvManager;
import fr.maxlego08.menu.api.configuration.Configuration;
@@ -22,17 +21,14 @@
import fr.maxlego08.menu.common.utils.nms.ItemStackUtils;
import fr.maxlego08.menu.inventory.inventories.InventoryDefault;
import fr.maxlego08.menu.listener.ListenerAdapter;
+import fr.maxlego08.menu.zcore.logger.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
-import org.bukkit.event.inventory.InventoryClickEvent;
-import org.bukkit.event.inventory.InventoryCloseEvent;
-import org.bukkit.event.inventory.InventoryDragEvent;
-import org.bukkit.event.inventory.InventoryOpenEvent;
-import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.event.inventory.*;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@@ -371,6 +367,7 @@ protected void onDamage(EntityDamageEvent event, Player player) {
* Closes the open menu when the player left the location where it was opened.
*/
private void checkDistance(Player player) {
+ if (!Configuration.closeInventoryOnMove) return;
if (CompatibilityUtil.getTopInventory(player).getHolder() instanceof VInventory inventory && this.hasMovedTooFar(player, inventory)) {
this.closeGhostInventory(player);
}
@@ -458,5 +455,8 @@ protected void onQuit(PlayerQuitEvent event, Player player) {
}
this.cooldownClick.remove(player.getUniqueId());
this.plugin.getInventoryManager().getPaginationManager().removePlayerStates(player.getUniqueId());
+ if (this.plugin.getCommandManager() instanceof ZCommandManager commandManager) {
+ commandManager.removePlayerArguments(player.getUniqueId());
+ }
}
}
diff --git a/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java b/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java
index 6cb9b1026..9a255fd02 100644
--- a/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java
+++ b/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java
@@ -322,6 +322,8 @@ private void loadTitleAnimation(ContainerInventorySetter inventory, YamlConfigur
TitleAnimationLoader animationLoader = titleAnimationLoader.get();
TitleAnimation titleAnimation = animationLoader.load(configuration, titleAnimationPath, file);
inventory.setTitleAnimation(titleAnimation);
+ } else {
+ Logger.info("The title animation of " + file.getName() + " was ignored: no title animation provider " + (pluginName == null ? "is registered" : "named '" + pluginName + "' is registered (is PacketEvents installed?)") + ".", Logger.LogType.WARNING);
}
}
}
diff --git a/src/main/java/fr/maxlego08/menu/loader/ZButtonLoader.java b/src/main/java/fr/maxlego08/menu/loader/ZButtonLoader.java
index 917e16ef8..dcf213b21 100644
--- a/src/main/java/fr/maxlego08/menu/loader/ZButtonLoader.java
+++ b/src/main/java/fr/maxlego08/menu/loader/ZButtonLoader.java
@@ -46,9 +46,12 @@
import java.lang.reflect.Constructor;
import java.nio.file.Path;
import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
public class ZButtonLoader extends ZUtils implements Loader