diff --git a/resource/plugin.yml b/resource/plugin.yml index b91d094..1426b5b 100644 --- a/resource/plugin.yml +++ b/resource/plugin.yml @@ -1,4 +1,4 @@ -version: 3.21.3.2 +version: 3.21.3.3 main: me.rockyhawk.commandpanels.CommandPanels name: CommandPanels author: RockyHawk diff --git a/src/me/rockyhawk/commandpanels/CommandPanels.java b/src/me/rockyhawk/commandpanels/CommandPanels.java index fa531c9..65efc40 100644 --- a/src/me/rockyhawk/commandpanels/CommandPanels.java +++ b/src/me/rockyhawk/commandpanels/CommandPanels.java @@ -1,6 +1,7 @@ package me.rockyhawk.commandpanels; import com.bencodez.votingplugin.VotingPluginHooks; +import com.google.common.collect.ImmutableMultimap; import io.lumine.mythic.lib.api.item.NBTItem; import me.rockyhawk.commandpanels.api.CommandPanelsAPI; import me.rockyhawk.commandpanels.api.Panel; @@ -36,6 +37,7 @@ import me.rockyhawk.commandpanels.ioclasses.nbt.NBTManager; import me.rockyhawk.commandpanels.ioclasses.legacy.LegacyVersion; import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions; import me.rockyhawk.commandpanels.ioclasses.legacy.PlayerHeads; +import me.rockyhawk.commandpanels.ioclasses.potions.ClassicPotionData; import me.rockyhawk.commandpanels.ioclasses.potions.LegacyPotionData; import me.rockyhawk.commandpanels.openpanelsmanager.*; import me.rockyhawk.commandpanels.openwithitem.HotbarItemLoader; @@ -104,6 +106,7 @@ public class CommandPanels extends JavaPlugin{ public GetCustomHeads customHeads = new GetCustomHeads(this); public Updater updater = new Updater(this); public PlayerHeads getHeads = new PlayerHeads(this); + public ClassicPotionData classicPotion = new ClassicPotionData(this); public LegacyPotionData legacyPotion = new LegacyPotionData(this); public LegacyVersion legacy = new LegacyVersion(this); @@ -357,7 +360,7 @@ public class CommandPanels extends JavaPlugin{ } //setAttributeModifiers was added into 1.14 api if(legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_14)){ - renamedMeta.setAttributeModifiers(null); + renamedMeta.setAttributeModifiers(ImmutableMultimap.of()); } } if (customName != null) { diff --git a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java index dba9a7a..dbcd5fb 100644 --- a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java +++ b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java @@ -283,7 +283,11 @@ public class ItemCreation { //potion legacy or current if(plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_19) || (plugin.legacy.MAJOR_VERSION == MinecraftVersions.v1_20 && plugin.legacy.MINOR_VERSION <= 4)){ - plugin.legacyPotion.applyPotionEffect(p,s,effectType); + if(plugin.legacy.MAJOR_VERSION.equals(MinecraftVersions.v1_8)){ + plugin.classicPotion.applyPotionEffect(p, s, effectType); + }else { + plugin.legacyPotion.applyPotionEffect(p, s, effectType); + } }else{ try { PotionMeta potionMeta = (PotionMeta)s.getItemMeta(); @@ -299,6 +303,18 @@ public class ItemCreation { } } } + + if(itemSection.contains("potion-color")){ + if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_11)){ + String[] rgb = Objects.requireNonNull(itemSection.getString("potion-color")).split(","); + Color color = Color.fromRGB(Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]), Integer.parseInt(rgb[2])); + PotionMeta potionMeta = (PotionMeta)s.getItemMeta(); + assert potionMeta != null; + potionMeta.setColor(color); + s.setItemMeta(potionMeta); + } + } + if (itemSection.contains("damage")) { //change the damage amount (placeholders accepted) //if the damage is not unbreakable and should be a value diff --git a/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java b/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java index ca6cbe3..ea3b3c8 100644 --- a/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java +++ b/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java @@ -26,8 +26,7 @@ public class GetCustomHeads { this.plugin = pl; } - //will not go above 2000 elements in the list - public HashSet savedCustomHeads = new HashSet<>(); + public HashMap savedCustomHeads = new HashMap<>(); public String getHeadBase64(ItemStack head) { if (plugin.getHeads.ifSkullOrHead(head.getType().toString()) && head.hasItemMeta()) { @@ -75,10 +74,13 @@ public class GetCustomHeads { } //get texture if already cached - for(SavedCustomHead head : savedCustomHeads){ - if(head.playerName == null) {continue;} - if(head.playerName.equals(name)){ - return head.headItem; + if (savedCustomHeads.containsKey(name)) { + if (!savedCustomHeads.get(name).isValid && (System.currentTimeMillis() - savedCustomHeads.get(name).lastAttempt) < 60000) { + // If the last attempt was less than 60 seconds ago and was invalid, return null or a default item + return new ItemStack(Material.valueOf(plugin.getHeads.playerHeadString())); + } + if(savedCustomHeads.get(name).isValid) { + return savedCustomHeads.get(name).headItem; // Return cached item if valid } } @@ -96,7 +98,7 @@ public class GetCustomHeads { if(plugin.debug.consoleDebug){ plugin.getServer().getConsoleSender().sendMessage(plugin.tex.colour(plugin.tag + ChatColor.WHITE + - "Attempting to Download & Cache Head Texture for " + name)); + "Download & Cache Head Texture for " + name)); } // Fetch the player UUID from the Mojang API @@ -121,27 +123,40 @@ public class GetCustomHeads { // Once the API call is finished, update the ItemStack on the main thread Bukkit.getScheduler().runTask(plugin, () -> { - itemStack.setItemMeta(getCustomHead(value).getItemMeta()); - savedCustomHeads.add(new SavedCustomHead(itemStack, value, name)); + itemStack.setItemMeta(getCustomHead(name, value).getItemMeta()); + savedCustomHeads.put(name, new SavedCustomHead(itemStack, value, true)); }); } catch (Exception ignore) { - // Ignore as errors should be skipped and no need to show in console + Bukkit.getScheduler().runTask(plugin, () -> { + //do not overwrite a valid cached head + if(savedCustomHeads.containsKey(name) && savedCustomHeads.get(name).isValid){ + return; + } + savedCustomHeads.put(name, new SavedCustomHead(null, null, false)); // Mark as invalid + }); } }); return itemStack; } + //will also use cached heads feature to get heads if player name is provided + public ItemStack getCustomHead(String playerName, String b64stringtexture) { + //check for any saved heads + if(savedCustomHeads.containsKey(playerName)) { + if (savedCustomHeads.get(playerName).base64 != null) { + return savedCustomHeads.get(playerName).headItem; + } + savedCustomHeads.get(playerName).isValid = false; + } + + //if saved head is not found from player name, get head manually + return getCustomHead(b64stringtexture); + } + //used to get heads from Base64 Textures @SuppressWarnings("deprecation") public ItemStack getCustomHead(String b64stringtexture) { - //check for any saved heads - for(SavedCustomHead head : savedCustomHeads){ - if(head.base64.equals(b64stringtexture)){ - return head.headItem; - } - } - //get head from base64 GameProfile profile = new GameProfile(UUID.randomUUID(), ""); PropertyMap propertyMap = profile.getProperties(); diff --git a/src/me/rockyhawk/commandpanels/classresources/customheads/SavedCustomHead.java b/src/me/rockyhawk/commandpanels/classresources/customheads/SavedCustomHead.java index 59dd83f..89a84e6 100644 --- a/src/me/rockyhawk/commandpanels/classresources/customheads/SavedCustomHead.java +++ b/src/me/rockyhawk/commandpanels/classresources/customheads/SavedCustomHead.java @@ -4,17 +4,14 @@ import org.bukkit.inventory.ItemStack; public class SavedCustomHead { public String base64; - public String playerName = null; public ItemStack headItem; + public boolean isValid; // true if the head was successfully fetched, false otherwise + public long lastAttempt; // timestamp of the last attempt - public SavedCustomHead(ItemStack head, String base64value) { - base64 = base64value; - headItem = head; - } - - public SavedCustomHead(ItemStack head, String base64value, String player) { - playerName = player; + public SavedCustomHead(ItemStack head, String base64value, boolean isValidAttempt) { base64 = base64value; headItem = head; + isValid = isValidAttempt; + lastAttempt = System.currentTimeMillis(); } } diff --git a/src/me/rockyhawk/commandpanels/commandtags/tags/standard/ItemTags.java b/src/me/rockyhawk/commandpanels/commandtags/tags/standard/ItemTags.java index 9628ccf..a0ca811 100644 --- a/src/me/rockyhawk/commandpanels/commandtags/tags/standard/ItemTags.java +++ b/src/me/rockyhawk/commandpanels/commandtags/tags/standard/ItemTags.java @@ -8,8 +8,8 @@ import org.bukkit.enchantments.Enchantment; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; -import java.util.Objects; import java.util.Set; public class ItemTags implements Listener { @@ -102,21 +102,19 @@ public class ItemTags implements Listener { e.commandTagUsed(); //if player uses setcustomdata= [slot] [position] [data] it will change the custom model data of the item PanelPosition position = PanelPosition.valueOf(e.args[1]); - ItemStack EditItem; + ItemStack editItem; if(position == PanelPosition.Top) { - EditItem = e.p.getOpenInventory().getTopInventory().getItem(Integer.parseInt(e.args[0])); + editItem = e.p.getOpenInventory().getTopInventory().getItem(Integer.parseInt(e.args[0])); }else if(position == PanelPosition.Middle) { - EditItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])+9); + editItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])+9); }else{ - EditItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])); + editItem = e.p.getInventory().getItem(Integer.parseInt(e.args[0])); } - assert EditItem != null; - try{ - if(EditItem.hasItemMeta()){ - Objects.requireNonNull(EditItem.getItemMeta()).setCustomModelData(Integer.valueOf(e.args[2])); - } + ItemMeta itemMeta = editItem.getItemMeta(); + itemMeta.setCustomModelData(Integer.valueOf(e.args[2])); + editItem.setItemMeta(itemMeta); } catch (Exception err){ plugin.debug(err,e.p); } diff --git a/src/me/rockyhawk/commandpanels/floodgatecp/OpenFloodgateGUI.java b/src/me/rockyhawk/commandpanels/floodgatecp/OpenFloodgateGUI.java index e37c899..d1caff4 100644 --- a/src/me/rockyhawk/commandpanels/floodgatecp/OpenFloodgateGUI.java +++ b/src/me/rockyhawk/commandpanels/floodgatecp/OpenFloodgateGUI.java @@ -64,8 +64,12 @@ public class OpenFloodgateGUI implements Listener { form.validResultHandler((SimpleFormResponse response) -> { int clickedButtonId = response.clickedButtonId(); String configKey = buttonCommands.get(clickedButtonId); - if(fgPanel.contains(configKey + ".commands")) { - plugin.commandRunner.runCommands(e.getPanel(), PanelPosition.Top, e.getPlayer(), fgPanel.getStringList(configKey + ".commands"), null); + + String section = plugin.has.hasSection(e.getPanel(), PanelPosition.Top, fgPanel.getConfigurationSection(configKey), e.getPlayer()); + ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(configKey + section); + + if(buttonConfig.contains("commands")) { + plugin.commandRunner.runCommands(e.getPanel(), PanelPosition.Top, e.getPlayer(), buttonConfig.getStringList("commands"), null); } }); @@ -77,7 +81,8 @@ public class OpenFloodgateGUI implements Listener { .filter(key -> key.matches("\\d+")) .sorted(Comparator.comparingInt(Integer::parseInt)) // Ensure numeric sorting .map(key -> { - ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(key); + String section = plugin.has.hasSection(panel, PanelPosition.Top, fgPanel.getConfigurationSection(key), p); + ConfigurationSection buttonConfig = fgPanel.getConfigurationSection(key + section); if (buttonConfig == null) return null; String buttonContent = plugin.tex.placeholders(panel, null, p, buttonConfig.getString("text")); @@ -101,7 +106,9 @@ public class OpenFloodgateGUI implements Listener { List commandsOrder = new ArrayList<>(); fgPanel.getKeys(false).forEach(key -> { if (key.matches("\\d+")) { - ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(key); + String section = plugin.has.hasSection(e.getPanel(), e.getPosition(), fgPanel.getConfigurationSection(key), e.getPlayer()); + ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(key + section); + try { String type = "toggle"; if(fieldConfig.contains("type")) { @@ -145,10 +152,13 @@ public class OpenFloodgateGUI implements Listener { if (!response.hasNext()) { break; // Safety check to prevent NoSuchElementException } - if(fgPanel.contains(configKey + ".commands")) { + String section = plugin.has.hasSection(e.getPanel(), e.getPosition(), fgPanel.getConfigurationSection(configKey), e.getPlayer()); + ConfigurationSection fieldConfig = fgPanel.getConfigurationSection(configKey + section); + + if(fieldConfig.contains("commands")) { Object fieldValue = response.next(); // Retrieve the next response value String value = String.valueOf(fieldValue); // Convert the field value to String - List commands = fgPanel.getStringList(configKey + ".commands"); // Retrieve commands for this field + List commands = fieldConfig.getStringList("commands"); // Retrieve commands for this field List processedCommands = new ArrayList<>(); for (String command : commands) { processedCommands.add(command.replaceAll("%cp-input%", value)); // Replace the placeholder in each command diff --git a/src/me/rockyhawk/commandpanels/ioclasses/potions/ClassicPotionData.java b/src/me/rockyhawk/commandpanels/ioclasses/potions/ClassicPotionData.java new file mode 100644 index 0000000..36a746f --- /dev/null +++ b/src/me/rockyhawk/commandpanels/ioclasses/potions/ClassicPotionData.java @@ -0,0 +1,77 @@ +package me.rockyhawk.commandpanels.ioclasses.potions; + +import me.rockyhawk.commandpanels.CommandPanels; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class ClassicPotionData { + private CommandPanels plugin; + + public ClassicPotionData(CommandPanels plugin) { + this.plugin = plugin; + } + + public void applyPotionEffect(Player p, ItemStack item, String[] effectType) { + try { + if (item == null || item.getType() != org.bukkit.Material.POTION) { + p.sendMessage(plugin.tex.colour(plugin.tag + ChatColor.RED + "Item is not a potion.")); + return; + } + + Class potionClass = Class.forName("org.bukkit.potion.Potion"); + Class potionTypeClass = Class.forName("org.bukkit.potion.PotionType"); + + Object potionType = Enum.valueOf((Class) potionTypeClass, effectType[0].toUpperCase()); + Constructor potionConstructor = potionClass.getConstructor(potionTypeClass, int.class); + Object potion = potionConstructor.newInstance(potionType, effectType.length >= 3 && effectType[2].equalsIgnoreCase("true") ? 2 : 1); + + Method setSplashMethod = potionClass.getMethod("setSplash", boolean.class); + boolean isSplash = (item.getDurability() & 0x4000) != 0; // Checks if the durability indicates it's a splash potion + setSplashMethod.invoke(potion, isSplash); + + try { + Method setHasExtendedDurationMethod = potionClass.getMethod("setHasExtendedDuration", boolean.class); + setHasExtendedDurationMethod.invoke(potion, effectType.length >= 2 && effectType[1].equalsIgnoreCase("true")); + }catch (Exception ignore){ + //ignore as some potions like instant potions cannot be extended + } + + Method applyMethod = potionClass.getMethod("apply", ItemStack.class); + applyMethod.invoke(potion, item); + } catch (Exception er) { + plugin.debug(er, p); + p.sendMessage(plugin.tex.colour(plugin.tag + ChatColor.RED + "Incorrect potion type or format.")); + } + } + + public String retrievePotionData(ItemStack item) { + try { + if (item == null || item.getType() != org.bukkit.Material.POTION) { + return "Item is not a potion"; + } + + Class potionClass = Class.forName("org.bukkit.potion.Potion"); + Method fromItemStackMethod = potionClass.getMethod("fromItemStack", ItemStack.class); + Object potion = fromItemStackMethod.invoke(null, item); + + Method getTypeMethod = potionClass.getMethod("getType"); + Method isExtendedMethod = potionClass.getMethod("hasExtendedDuration"); + Method isUpgradedMethod = potionClass.getMethod("getLevel"); + + Object potionType = getTypeMethod.invoke(potion); + boolean extended = (Boolean) isExtendedMethod.invoke(potion); + boolean upgraded = ((Integer) isUpgradedMethod.invoke(potion)) > 1; + + boolean isSplash = (item.getDurability() & 0x4000) != 0; // Checks if the durability indicates it's a splash potion + + return potionType.toString() + " " + extended + " " + upgraded + " Splash:" + isSplash; + } catch (Exception e) { + plugin.debug(e, null); + return "Failed to retrieve potion data"; + } + } +} diff --git a/src/me/rockyhawk/commandpanels/ioclasses/potions/LegacyPotionData.java b/src/me/rockyhawk/commandpanels/ioclasses/potions/LegacyPotionData.java index 9b4eba0..7014dfe 100644 --- a/src/me/rockyhawk/commandpanels/ioclasses/potions/LegacyPotionData.java +++ b/src/me/rockyhawk/commandpanels/ioclasses/potions/LegacyPotionData.java @@ -10,6 +10,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class LegacyPotionData { + /* + * To be used in Minecraft 1.9 to 1.20.4 + * */ private CommandPanels plugin; public LegacyPotionData(CommandPanels plugin) { diff --git a/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java b/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java index ac0a4e7..eccb9bc 100644 --- a/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java +++ b/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java @@ -16,6 +16,7 @@ import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerQuitEvent; import java.util.Iterator; +import java.util.Map; import java.util.Objects; public class UtilsPanelsLoader implements Listener { @@ -75,10 +76,10 @@ public class UtilsPanelsLoader implements Listener { plugin.openPanels.closePanelForLoader(e.getPlayer().getName(),PanelPosition.Top); //clear cached textures list until length limit is reached - Iterator iterator = plugin.customHeads.savedCustomHeads.iterator(); + Iterator> iterator = plugin.customHeads.savedCustomHeads.entrySet().iterator(); while (plugin.customHeads.savedCustomHeads.size() > 2000 && iterator.hasNext()) { - iterator.next(); // Move to next element - iterator.remove(); // Remove the element + iterator.next(); // Move to next entry + iterator.remove(); // Remove the entry } }