diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 276b6a6..e35af68 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -51,6 +51,11 @@
+
+
+
+
+
@@ -61,6 +66,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index d0729bf..3de4e8b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,14 +71,14 @@
-
- io.papermc
- https://repo.papermc.io/repository/maven-public/
-
jitpack.io
https://jitpack.io
+
+ io.papermc
+ https://repo.papermc.io/repository/maven-public/
+
minecraft-repo
https://libraries.minecraft.net/
@@ -175,26 +175,15 @@
net.Indyuce
MMOItems-API
- LATEST
+ 6.10-SNAPSHOT
provided
io.lumine
MythicLib-dist
- LATEST
+ 1.6.2-SNAPSHOT
provided
-
- com.github.Realizedd
- TokenManager
- 3.2.4
-
-
- *
- *
-
-
-
com.arcaniax
HeadDatabase-API
diff --git a/resource/plugin.yml b/resource/plugin.yml
index 55f8145..7ba2699 100644
--- a/resource/plugin.yml
+++ b/resource/plugin.yml
@@ -1,4 +1,4 @@
-version: 3.21.4.1
+version: 3.21.4.2
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 f271228..23e38de 100644
--- a/src/me/rockyhawk/commandpanels/CommandPanels.java
+++ b/src/me/rockyhawk/commandpanels/CommandPanels.java
@@ -7,6 +7,7 @@ import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.classresources.ExecuteOpenVoids;
import me.rockyhawk.commandpanels.classresources.MiniMessageUtils;
import me.rockyhawk.commandpanels.classresources.customheads.GetCustomHeads;
+import me.rockyhawk.commandpanels.classresources.customheads.methods.CustomHeadGameProfile;
import me.rockyhawk.commandpanels.classresources.HasSections;
import me.rockyhawk.commandpanels.classresources.ItemCreation;
import me.rockyhawk.commandpanels.classresources.placeholders.expansion.CpPlaceholderExpansion;
@@ -120,7 +121,6 @@ public class CommandPanels extends JavaPlugin{
public InventorySaver inventorySaver = new InventorySaver(this);
public ItemStackSerializer itemSerializer = new ItemStackSerializer(this);
public UserInputUtils inputUtils = new UserInputUtils(this);
- public OpenFloodgateGUI floodgateOpenGUI = new OpenFloodgateGUI(this);
public File panelsf = new File(this.getDataFolder() + File.separator + "panels");
public YamlConfiguration blockConfig; //where panel block locations are stored
@@ -128,6 +128,9 @@ public class CommandPanels extends JavaPlugin{
public void onEnable() {
Bukkit.getLogger().info("[CommandPanels] RockyHawk's CommandPanels v" + this.getDescription().getVersion() + " Plugin Loading...");
+ //Initialise classes that are not used externally
+ new OpenFloodgateGUI(this);
+
//register config files
this.blockConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "blocks.yml"));
panelData.dataConfig = YamlConfiguration.loadConfiguration(new File(getDataFolder() + File.separator + "data.yml"));
diff --git a/src/me/rockyhawk/commandpanels/classresources/HasSections.java b/src/me/rockyhawk/commandpanels/classresources/HasSections.java
index 8bd7317..5a8ea32 100644
--- a/src/me/rockyhawk/commandpanels/classresources/HasSections.java
+++ b/src/me/rockyhawk/commandpanels/classresources/HasSections.java
@@ -9,9 +9,7 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import java.math.BigDecimal;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.*;
public class HasSections {
CommandPanels plugin;
@@ -20,10 +18,29 @@ public class HasSections {
}
public String hasSection(Panel panel, PanelPosition position, ConfigurationSection cf, Player p) {
- for (String setName : cf.getKeys(false)) {
- if (!cf.isConfigurationSection(setName)) continue;
+ // Use a TreeMap to automatically sort the sections by the extracted number.
+ Map sortedSections = new TreeMap<>();
- ConfigurationSection currentSection = cf.getConfigurationSection(setName);
+ // Loop through the section names and filter for the ones starting with "has".
+ for (String key : cf.getKeys(false)) {
+ if (!cf.isConfigurationSection(key)) continue;
+
+ // Check if the section starts with "has" and is followed by a number.
+ if (key.startsWith("has")) {
+ try {
+ // Extract the number after "has" and put it in the map for sorting.
+ int number = Integer.parseInt(key.substring(3));
+ sortedSections.put(number, key);
+ } catch (NumberFormatException ignore) {
+ // If the section name doesn't have a valid number after "has", skip it.
+ }
+ }
+ }
+
+ for (String hasSection : sortedSections.values()) {
+ if (!cf.isConfigurationSection(hasSection)) continue;
+
+ ConfigurationSection currentSection = cf.getConfigurationSection(hasSection);
int numberOfConditions = currentSection.getKeys(false).size();
Boolean currentBlockResult = null; // This will store the result of the current block (a set of conditions combined by AND or OR).
@@ -48,7 +65,7 @@ public class HasSections {
HashSet values = doOperators(new HashSet<>(Collections.singletonList(value)));
boolean localResult = false; // This tracks the result of the current condition.
for (String val : values) {
- if (hasProcess(setName, val, compare, p)) {
+ if (hasProcess(val, compare)) {
localResult = true;
break;
}
@@ -71,7 +88,7 @@ public class HasSections {
if (currentBlockResult != null && currentBlockResult) {
// If the result of this section is true, check nested sections.
- return "." + setName + hasSection(panel, position, currentSection, p);
+ return "." + hasSection + hasSection(panel, position, currentSection, p);
}
// If the result is false, continue to the next 'has' section.
}
@@ -90,7 +107,7 @@ public class HasSections {
return value;
}
- private boolean hasProcess(String setName, String value, String compare,Player p){
+ private boolean hasProcess(String value, String compare){
//check to see if the value should be reversed
boolean outputValue = true;
if(value.startsWith("NOT ")){
@@ -99,18 +116,16 @@ public class HasSections {
}
//the current has section with all the functions implemented inside it
- if(setName.startsWith("has")) {
- if(value.endsWith(" HASPERM")) {
- String playername = value.substring(0, value.length()-8);
- Player player = Bukkit.getPlayerExact(playername);
- if(player != null){
- return player.hasPermission(compare) == outputValue;
- }
- }else if(value.endsWith(" ISGREATER")) {
- return (new BigDecimal(compare).compareTo(new BigDecimal(value.substring(0, value.length()-10).replace(",",""))) <= 0 == outputValue);
- }else{
- return compare.equals(value) == outputValue;
+ if(value.endsWith(" HASPERM")) {
+ String playername = value.substring(0, value.length()-8);
+ Player player = Bukkit.getPlayerExact(playername);
+ if(player != null){
+ return player.hasPermission(compare) == outputValue;
}
+ }else if(value.endsWith(" ISGREATER")) {
+ return (new BigDecimal(compare).compareTo(new BigDecimal(value.substring(0, value.length()-10).replace(",",""))) <= 0 == outputValue);
+ }else{
+ return compare.equals(value) == outputValue;
}
return false;
}
diff --git a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java
index 177ae4f..38f06a8 100644
--- a/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java
+++ b/src/me/rockyhawk/commandpanels/classresources/ItemCreation.java
@@ -4,6 +4,7 @@ import dev.lone.itemsadder.api.CustomStack;
import me.arcaniax.hdb.api.HeadDatabaseAPI;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
+import me.rockyhawk.commandpanels.classresources.customheads.SavedCustomHead;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
import net.Indyuce.mmoitems.MMOItems;
@@ -27,6 +28,7 @@ import org.bukkit.inventory.meta.trim.TrimMaterial;
import org.bukkit.inventory.meta.trim.TrimPattern;
import org.bukkit.potion.PotionType;
+import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
@@ -95,7 +97,41 @@ public class ItemCreation {
}
}
- //ItemsAdder support, needs namespaceID (eg, money:coin)
+ //Oraxen support, uses itemID (eg, oraxen= coin)
+ if (matraw.split("\\s")[0].equalsIgnoreCase("oraxen=")) {
+ String itemID = matraw.split("\\s")[1];
+ try {
+ // Load the OraxenItems class
+ Class> oraxenItemsClass = Class.forName("io.th0rgal.oraxen.api.OraxenItems");
+
+ // Retrieve the 'getItemById' method from the OraxenItems class
+ Method getItemByIdMethod = oraxenItemsClass.getMethod("getItemById", String.class);
+ getItemByIdMethod.setAccessible(true);
+
+ // Invoke the 'getItemById' method with the itemID
+ Object oraxenItem = getItemByIdMethod.invoke(null, itemID); // static method, so pass 'null'
+
+ // Ensure that the method returned a valid Oraxen item
+ if (oraxenItem != null) {
+ // Now we need to invoke 'getReferenceClone' on the OraxenItem object
+ Method getReferenceCloneMethod = oraxenItem.getClass().getMethod("getReferenceClone");
+ getReferenceCloneMethod.setAccessible(true);
+ ItemStack stack = (ItemStack) getReferenceCloneMethod.invoke(oraxenItem);
+
+ // Check if stack is not null
+ if (stack != null) {
+ s = stack;
+ normalCreation = false;
+ }
+ }
+ } catch (Exception e) {
+ plugin.debug(e, null);
+ // Handle the error or inform the player
+ }
+
+ }
+
+ //ItemsAdder support, needs namespaceID (eg, itemsadder= money:coin)
if (matraw.split("\\s")[0].equalsIgnoreCase("itemsadder=")) {
String namespaceID = matraw.split("\\s")[1];
CustomStack stack = CustomStack.getInstance(namespaceID);
@@ -444,14 +480,7 @@ public class ItemCreation {
}
if(plugin.getHeads.ifSkullOrHead(cont.getType().toString())){
if(!Objects.requireNonNull(file.getString("panels." + panelName + ".item." + i + ".material")).contains("%") && !Objects.requireNonNull(file.getString("panels." + panelName + ".item." + i + ".material")).contains("=")) {
- SkullMeta meta = (SkullMeta) cont.getItemMeta();
- if (plugin.customHeads.getHeadBase64(cont) != null) {
- //inject base64 here, disable for legacy as is not working
- file.set("panels." + panelName + ".item." + i + ".material", "cps= " + plugin.customHeads.getHeadBase64(cont));
- } else{
- //return blank head
- file.set("panels." + panelName + ".item." + i + ".material", plugin.getHeads.playerHeadString());
- }
+ file.set("panels." + panelName + ".item." + i + ".material", plugin.getHeads.playerHeadString());
}
}
try {
diff --git a/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java b/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java
index ea3b3c8..d9c3484 100644
--- a/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java
+++ b/src/me/rockyhawk/commandpanels/classresources/customheads/GetCustomHeads.java
@@ -1,216 +1,34 @@
package me.rockyhawk.commandpanels.classresources.customheads;
-import com.mojang.authlib.GameProfile;
-import com.mojang.authlib.properties.Property;
-import com.mojang.authlib.properties.PropertyMap;
-import me.arcaniax.hdb.api.HeadDatabaseAPI;
import me.rockyhawk.commandpanels.CommandPanels;
+import me.rockyhawk.commandpanels.classresources.customheads.methods.CustomHeadGameProfile;
+import me.rockyhawk.commandpanels.classresources.customheads.methods.CustomHeadPlayerProfile;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
-import org.bukkit.Bukkit;
-import org.bukkit.ChatColor;
-import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
-import org.bukkit.inventory.meta.ItemMeta;
-import org.bukkit.inventory.meta.SkullMeta;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLConnection;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
public class GetCustomHeads {
+ CustomHeadGameProfile gameProfileHeadClass;
+ CustomHeadPlayerProfile playerProfileHeadClass;
CommandPanels plugin;
+
public GetCustomHeads(CommandPanels pl) {
this.plugin = pl;
+ gameProfileHeadClass = new CustomHeadGameProfile(pl);
+ playerProfileHeadClass = new CustomHeadPlayerProfile();
}
- public HashMap savedCustomHeads = new HashMap<>();
-
- public String getHeadBase64(ItemStack head) {
- if (plugin.getHeads.ifSkullOrHead(head.getType().toString()) && head.hasItemMeta()) {
- //check if the head is a HeadDatabase head first
- if(plugin.getServer().getPluginManager().isPluginEnabled("HeadDatabase")) {
- HeadDatabaseAPI api = new HeadDatabaseAPI();
- try {
- String base64 = api.getBase64(head);
- if(base64 != null){
- return base64;
- }
- } catch (Exception ignore) {}
- }
- //try getting Base64 of a custom head
- if (!(head.getItemMeta() instanceof SkullMeta)) return null;
- SkullMeta meta = (SkullMeta) head.getItemMeta();
- if (meta == null) return null;
-
- try {
- Field profileField = meta.getClass().getDeclaredField("profile");
- profileField.setAccessible(true);
- GameProfile profile = (GameProfile) profileField.get(meta);
-
- Collection textures = profile.getProperties().get("textures");
- if (!textures.isEmpty()) {
- // Directly accessing fields within the Property object via reflection.
- Property textureProperty = textures.iterator().next();
- Field valueField = textureProperty.getClass().getDeclaredField("value");
- valueField.setAccessible(true);
- return (String) valueField.get(textureProperty);
- }
-
- } catch (Exception error) {
- plugin.debug(error, null);
- }
- }
- return null;
- }
-
- //getting the head from a Player Name
- public ItemStack getPlayerHead(String name) {
- byte id = 0;
- if (plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)) {
- id = 3;
- }
-
- //get texture if already cached
- 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
- }
- }
-
- //create ItemStack
- ItemStack itemStack = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1, id);
-
- //Run fallback code, if API call fails, use legacy setOwner
- SkullMeta meta = (SkullMeta) itemStack.getItemMeta();
- meta.setOwner(name);
- itemStack.setItemMeta(meta);
-
- // Fetch and cache the texture asynchronously
- Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
- try {
- if(plugin.debug.consoleDebug){
- plugin.getServer().getConsoleSender().sendMessage(plugin.tex.colour(plugin.tag +
- ChatColor.WHITE +
- "Download & Cache Head Texture for " + name));
- }
-
- // Fetch the player UUID from the Mojang API
- URL uuidUrl = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
- URLConnection uuidConnection = uuidUrl.openConnection();
- uuidConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
- uuidConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
- //Json is simple and structured so a hard code solution will avoid the need for a library
- String uuidReader = new Scanner(uuidConnection.getInputStream(),
- StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
- String uuid = uuidReader.split("\"id\" : \"")[1].split("\"")[0];
-
- // Fetch the skin texture from the Mojang API using the player UUID
- URL texturesUrl = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid);
- URLConnection texturesConnection = texturesUrl.openConnection();
- texturesConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
- texturesConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
- //Json is simple and structured so a hard code solution will avoid the need for a library
- String valueReader = new Scanner(texturesConnection.getInputStream(),
- StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
- String value = valueReader.split("\"value\" : \"")[1].split("\"")[0];
-
- // Once the API call is finished, update the ItemStack on the main thread
- Bukkit.getScheduler().runTask(plugin, () -> {
- itemStack.setItemMeta(getCustomHead(name, value).getItemMeta());
- savedCustomHeads.put(name, new SavedCustomHead(itemStack, value, true));
- });
- } catch (Exception ignore) {
- 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) {
- //get head from base64
- GameProfile profile = new GameProfile(UUID.randomUUID(), "");
- PropertyMap propertyMap = profile.getProperties();
- if (propertyMap == null) {
- throw new IllegalStateException("Profile doesn't contain a property map");
- } else {
- propertyMap.put("textures", new Property("textures", b64stringtexture));
- byte id = 0;
- if(plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)){
- id = 3;
- }
- ItemStack head = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1,id);
- ItemMeta headMeta = head.getItemMeta();
- assert headMeta != null;
-
- Field profileField;
- Method setProfileMethod = null;
- try {
- // Attempt to access the 'profile' field directly
- // Also writes to 'serializedProfile' field as one cannot be null while the other is not
- // This block is mainly for 1.20.2+ versions
- profileField = headMeta.getClass().getDeclaredField("profile");
- Field serializedProfileField = headMeta.getClass().getDeclaredField("serializedProfile");
-
- profileField.setAccessible(true);
- serializedProfileField.setAccessible(true);
-
- profileField.set(headMeta, profile);
- serializedProfileField.set(headMeta, profile); // Assuming serializedProfile is of the same type
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e1) {
- try {
- // This block covers versions that have a 'setProfile' method instead of direct field access
- // Likely for versions prior to 1.20.2
- setProfileMethod = headMeta.getClass().getDeclaredMethod("setProfile", GameProfile.class);
- } catch (NoSuchMethodException ignore) {}
- } catch (SecurityException ignored) {}
- try {
- if (setProfileMethod == null) {
- // Attempt to access the 'profile' field directly
- // This block is a generic fallback for versions lacking the 'setProfile' method
- profileField = headMeta.getClass().getDeclaredField("profile");
- profileField.setAccessible(true);
- profileField.set(headMeta, profile);
- } else {
- // Use the 'setProfile' method if it was found
- setProfileMethod.setAccessible(true);
- setProfileMethod.invoke(headMeta, profile);
- }
- } catch (Exception e1) {
- plugin.debug(e1,null);
- }
-
- head.setItemMeta(headMeta);
- return head;
+ public ItemStack getCustomHead(String base64){
+ if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_18)){
+ return playerProfileHeadClass.getCustomHead(base64);
+ }else{
+ return gameProfileHeadClass.getCustomHead(base64);
}
}
-}
\ No newline at end of file
+ public ItemStack getPlayerHead(String playerName){
+ if(plugin.legacy.MAJOR_VERSION.greaterThanOrEqualTo(MinecraftVersions.v1_21)){
+ return playerProfileHeadClass.getPlayerHead(playerName);
+ }else{
+ return gameProfileHeadClass.getPlayerHead(playerName);
+ }
+ }
+}
diff --git a/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadGameProfile.java b/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadGameProfile.java
new file mode 100644
index 0000000..48d0d5b
--- /dev/null
+++ b/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadGameProfile.java
@@ -0,0 +1,185 @@
+package me.rockyhawk.commandpanels.classresources.customheads.methods;
+
+import com.mojang.authlib.GameProfile;
+import com.mojang.authlib.properties.Property;
+import com.mojang.authlib.properties.PropertyMap;
+import me.rockyhawk.commandpanels.CommandPanels;
+import me.rockyhawk.commandpanels.classresources.customheads.SavedCustomHead;
+import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.SkullMeta;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class CustomHeadGameProfile {
+ CommandPanels plugin;
+ public CustomHeadGameProfile(CommandPanels pl) {
+ this.plugin = pl;
+ }
+
+ public HashMap savedCustomHeads = new HashMap<>();
+
+ //getting the head from a Player Name
+ public ItemStack getPlayerHead(String name) {
+ byte id = 0;
+ if (plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)) {
+ id = 3;
+ }
+
+ //get texture if already cached
+ 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
+ }
+ }
+
+ //create ItemStack
+ ItemStack itemStack = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1, id);
+
+ //Run fallback code, if API call fails, use legacy setOwner
+ SkullMeta meta = (SkullMeta) itemStack.getItemMeta();
+ meta.setOwner(name);
+ itemStack.setItemMeta(meta);
+
+ // Fetch and cache the texture asynchronously
+ Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
+ try {
+ if(plugin.debug.consoleDebug){
+ plugin.getServer().getConsoleSender().sendMessage(plugin.tex.colour(plugin.tag +
+ ChatColor.WHITE +
+ "Download & Cache Head Texture for " + name));
+ }
+
+ // Fetch the player UUID from the Mojang API
+ URL uuidUrl = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
+ URLConnection uuidConnection = uuidUrl.openConnection();
+ uuidConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
+ uuidConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
+ //Json is simple and structured so a hard code solution will avoid the need for a library
+ String uuidReader = new Scanner(uuidConnection.getInputStream(),
+ StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
+ String uuid = uuidReader.split("\"id\" : \"")[1].split("\"")[0];
+
+ // Fetch the skin texture from the Mojang API using the player UUID
+ URL texturesUrl = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid);
+ URLConnection texturesConnection = texturesUrl.openConnection();
+ texturesConnection.setConnectTimeout(2000); // Set connection timeout to 2 seconds
+ texturesConnection.setReadTimeout(2000); // Set read timeout to 2 seconds
+ //Json is simple and structured so a hard code solution will avoid the need for a library
+ String valueReader = new Scanner(texturesConnection.getInputStream(),
+ StandardCharsets.UTF_8.name()).useDelimiter("\\A").next();
+ String value = valueReader.split("\"value\" : \"")[1].split("\"")[0];
+
+ // Once the API call is finished, update the ItemStack on the main thread
+ Bukkit.getScheduler().runTask(plugin, () -> {
+ itemStack.setItemMeta(getCustomHead(name, value).getItemMeta());
+ savedCustomHeads.put(name, new SavedCustomHead(itemStack, value, true));
+ });
+ } catch (Exception ignore) {
+ 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;
+ }
+
+ //clear cached textures list until length limit is reached
+ Iterator> iterator = savedCustomHeads.entrySet().iterator();
+ while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
+ iterator.next(); // Move to next entry
+ iterator.remove(); // Remove the entry
+ }
+
+ //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) {
+ //get head from base64
+ GameProfile profile = new GameProfile(UUID.randomUUID(), "");
+ PropertyMap propertyMap = profile.getProperties();
+ if (propertyMap == null) {
+ throw new IllegalStateException("Profile doesn't contain a property map");
+ } else {
+ propertyMap.put("textures", new Property("textures", b64stringtexture));
+ byte id = 0;
+ if(plugin.legacy.MAJOR_VERSION.lessThanOrEqualTo(MinecraftVersions.v1_15)){
+ id = 3;
+ }
+ ItemStack head = new ItemStack(Material.matchMaterial(plugin.getHeads.playerHeadString()), 1,id);
+ ItemMeta headMeta = head.getItemMeta();
+ assert headMeta != null;
+
+ Field profileField;
+ Method setProfileMethod = null;
+ try {
+ // Attempt to access the 'profile' field directly
+ // Also writes to 'serializedProfile' field as one cannot be null while the other is not
+ // This block is mainly for 1.20.2+ versions
+ profileField = headMeta.getClass().getDeclaredField("profile");
+ Field serializedProfileField = headMeta.getClass().getDeclaredField("serializedProfile");
+
+ profileField.setAccessible(true);
+ serializedProfileField.setAccessible(true);
+
+ profileField.set(headMeta, profile);
+ serializedProfileField.set(headMeta, profile); // Assuming serializedProfile is of the same type
+ } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e1) {
+ try {
+ // This block covers versions that have a 'setProfile' method instead of direct field access
+ // Likely for versions prior to 1.20.2
+ setProfileMethod = headMeta.getClass().getDeclaredMethod("setProfile", GameProfile.class);
+ } catch (NoSuchMethodException ignore) {}
+ } catch (SecurityException ignored) {}
+ try {
+ if (setProfileMethod == null) {
+ // Attempt to access the 'profile' field directly
+ // This block is a generic fallback for versions lacking the 'setProfile' method
+ profileField = headMeta.getClass().getDeclaredField("profile");
+ profileField.setAccessible(true);
+ profileField.set(headMeta, profile);
+ } else {
+ // Use the 'setProfile' method if it was found
+ setProfileMethod.setAccessible(true);
+ setProfileMethod.invoke(headMeta, profile);
+ }
+ } catch (Exception e1) {
+ plugin.debug(e1,null);
+ }
+
+ head.setItemMeta(headMeta);
+ return head;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadPlayerProfile.java b/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadPlayerProfile.java
new file mode 100644
index 0000000..4bb11f3
--- /dev/null
+++ b/src/me/rockyhawk/commandpanels/classresources/customheads/methods/CustomHeadPlayerProfile.java
@@ -0,0 +1,118 @@
+package me.rockyhawk.commandpanels.classresources.customheads.methods;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.profile.PlayerProfile;
+import org.bukkit.profile.PlayerTextures;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+public class CustomHeadPlayerProfile {
+ //cached itemstacks stored for access
+ public HashMap savedCustomHeads = new HashMap<>();
+
+ //Using the PlayerProfile API for getting custom heads
+ public ItemStack getCustomHead(String base64Texture) {
+ //check for any saved heads
+ if(savedCustomHeads.containsKey(base64Texture)) {
+ return savedCustomHeads.get(base64Texture);
+ }
+
+ //clear cached textures list until length limit is reached
+ Iterator> iterator = savedCustomHeads.entrySet().iterator();
+ while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
+ iterator.next(); // Move to next entry
+ iterator.remove(); // Remove the entry
+ }
+
+ // Create a new player head ItemStack
+ ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1);
+ SkullMeta skullMeta = (SkullMeta) skull.getItemMeta();
+
+ // Create a new PlayerProfile
+ UUID uuid = UUID.randomUUID(); // Unique ID for the profile
+ PlayerProfile profile = Bukkit.createPlayerProfile(uuid);
+
+ // Decode the base64 texture and extract the texture URL
+ String decodedTexture = extractSkinUrlFromBase64(base64Texture);
+
+ // Set the skin URL using PlayerTextures
+ PlayerTextures textures = profile.getTextures();
+ try {
+ // Using a URL object for the texture
+ textures.setSkin(new URL(decodedTexture));
+ } catch (MalformedURLException ignore) {} // Base64 has no URL, ignore
+
+ // Apply the textures to the profile
+ profile.setTextures(textures);
+
+ // Apply the PlayerProfile to the SkullMeta
+ skullMeta.setOwnerProfile(profile);
+
+ // Set the modified SkullMeta back to the ItemStack
+ skull.setItemMeta(skullMeta);
+
+ savedCustomHeads.put(base64Texture, skull);
+ return skull;
+ }
+
+ // New method to get a player head by player name
+ public ItemStack getPlayerHead(String playerName) {
+ //check for any saved heads
+ if(savedCustomHeads.containsKey(playerName)) {
+ return savedCustomHeads.get(playerName);
+ }
+
+ //clear cached textures list until length limit is reached
+ Iterator> iterator = savedCustomHeads.entrySet().iterator();
+ while (savedCustomHeads.size() > 2000 && iterator.hasNext()) {
+ iterator.next(); // Move to next entry
+ iterator.remove(); // Remove the entry
+ }
+
+ // Create a new player head ItemStack
+ ItemStack skull = new ItemStack(Material.PLAYER_HEAD, 1);
+ SkullMeta skullMeta = (SkullMeta) skull.getItemMeta();
+
+ // Get the OfflinePlayer object for the provided player name
+ OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName);
+
+ // Create a PlayerProfile from the player's UUID
+ UUID playerUUID = offlinePlayer.getUniqueId();
+ PlayerProfile profile = Bukkit.createPlayerProfile(playerUUID);
+
+ // Apply the PlayerProfile to the SkullMeta
+ skullMeta.setOwnerProfile(profile);
+
+ // Set the modified SkullMeta back to the ItemStack
+ skull.setItemMeta(skullMeta);
+
+ savedCustomHeads.put(playerName, skull);
+ return skull;
+ }
+
+ // Helper method to extract the skin URL from the base64 texture
+ private String extractSkinUrlFromBase64(String base64Texture) {
+ // Decode the base64 string
+ byte[] decodedBytes = Base64.getDecoder().decode(base64Texture);
+ String decodedString = new String(decodedBytes);
+
+ // Parse the decoded string as JSON
+ JsonObject jsonObject = JsonParser.parseString(decodedString).getAsJsonObject();
+
+ // Navigate to "textures" -> "SKIN" -> "url"
+ JsonObject textures = jsonObject.getAsJsonObject("textures");
+ JsonObject skin = textures.getAsJsonObject("SKIN");
+
+ // Return the URL if it exists
+ return skin.has("url") ? skin.get("url").getAsString() : null;
+ }
+
+}
diff --git a/src/me/rockyhawk/commandpanels/classresources/placeholders/Placeholders.java b/src/me/rockyhawk/commandpanels/classresources/placeholders/Placeholders.java
index 3b44ce0..c969cac 100644
--- a/src/me/rockyhawk/commandpanels/classresources/placeholders/Placeholders.java
+++ b/src/me/rockyhawk/commandpanels/classresources/placeholders/Placeholders.java
@@ -1,7 +1,6 @@
package me.rockyhawk.commandpanels.classresources.placeholders;
import com.earth2me.essentials.Essentials;
-import me.realized.tokenmanager.api.TokenManager;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.ioclasses.legacy.MinecraftVersions;
@@ -462,13 +461,6 @@ public class Placeholders {
} catch (Exception place) {
//skip
}
- if (plugin.getServer().getPluginManager().isPluginEnabled("TokenManager")) {
- TokenManager api = (TokenManager) Bukkit.getServer().getPluginManager().getPlugin("TokenManager");
- assert api != null;
- if(identifier.equals("tokenmanager-balance")) {
- return Long.toString(api.getTokens(p).orElse(0));
- }
- }
//end nodes with PlaceHolders
return "";
}
diff --git a/src/me/rockyhawk/commandpanels/commands/Commandpanelsreload.java b/src/me/rockyhawk/commandpanels/commands/Commandpanelsreload.java
index aaa968c..07bcb55 100644
--- a/src/me/rockyhawk/commandpanels/commands/Commandpanelsreload.java
+++ b/src/me/rockyhawk/commandpanels/commands/Commandpanelsreload.java
@@ -54,9 +54,6 @@ public class Commandpanelsreload implements CommandExecutor {
registerCommands();
}
- //pre-cache any player head textures from panels will be reloaded
- plugin.customHeads.savedCustomHeads.clear();
-
sender.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.reload")));
}else{
sender.sendMessage(plugin.tex.colour(plugin.tag + plugin.config.getString("config.format.perms")));
diff --git a/src/me/rockyhawk/commandpanels/commandtags/CommandRunner.java b/src/me/rockyhawk/commandpanels/commandtags/CommandRunner.java
index b7d313f..00a0802 100644
--- a/src/me/rockyhawk/commandpanels/commandtags/CommandRunner.java
+++ b/src/me/rockyhawk/commandpanels/commandtags/CommandRunner.java
@@ -52,7 +52,7 @@ public class CommandRunner {
CommandTagEvent tags = new CommandTagEvent(plugin, panel, position, p, commandRAW);
Bukkit.getPluginManager().callEvent(tags);
if (!tags.commandTagUsed) {
- Bukkit.dispatchCommand(p, plugin.tex.placeholders(panel, position, p, commandRAW.trim()));
+ Bukkit.dispatchCommand(p, plugin.tex.attachPlaceholders(panel, position, p, commandRAW.trim()));
}
}
diff --git a/src/me/rockyhawk/commandpanels/commandtags/CommandTagEvent.java b/src/me/rockyhawk/commandpanels/commandtags/CommandTagEvent.java
index 1c09616..c14470d 100644
--- a/src/me/rockyhawk/commandpanels/commandtags/CommandTagEvent.java
+++ b/src/me/rockyhawk/commandpanels/commandtags/CommandTagEvent.java
@@ -3,7 +3,6 @@ package me.rockyhawk.commandpanels.commandtags;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.openpanelsmanager.PanelPosition;
-import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@@ -40,8 +39,8 @@ public class CommandTagEvent extends Event {
if(doApiPlaceholders) {
this.args = plugin.tex.attachPlaceholders(panel1,pos, player, split[1].trim()).split("\\s");
}else{
- this.args = ChatColor.translateAlternateColorCodes('&',plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),false)).split("\\s");
- this.args = ChatColor.translateAlternateColorCodes('&',plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),true)).split("\\s");
+ this.args = plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),false).split("\\s");
+ this.args = plugin.placeholders.setPlaceholders(panel, pos, p,split[1].trim(),true).split("\\s");
}
}
diff --git a/src/me/rockyhawk/commandpanels/commandtags/paywalls/TokenPaywall.java b/src/me/rockyhawk/commandpanels/commandtags/paywalls/TokenPaywall.java
index 003accf..3f03173 100644
--- a/src/me/rockyhawk/commandpanels/commandtags/paywalls/TokenPaywall.java
+++ b/src/me/rockyhawk/commandpanels/commandtags/paywalls/TokenPaywall.java
@@ -1,6 +1,4 @@
package me.rockyhawk.commandpanels.commandtags.paywalls;
-
-import me.realized.tokenmanager.api.TokenManager;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.commandtags.PaywallEvent;
import me.rockyhawk.commandpanels.commandtags.PaywallOutput;
@@ -10,45 +8,67 @@ import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
+import java.lang.reflect.Method;
import java.util.Objects;
+import java.util.OptionalLong;
public class TokenPaywall implements Listener {
- CommandPanels plugin;
+ private final CommandPanels plugin;
+
public TokenPaywall(CommandPanels pl) {
this.plugin = pl;
}
@EventHandler
- public void commandTag(PaywallEvent e){
- if(e.name.equalsIgnoreCase("tokenpaywall=")){
- //if player uses tokenpaywall= [price]
+ public void commandTag(PaywallEvent e) {
+ if (e.name.equalsIgnoreCase("tokenpaywall=")) {
+ // if player uses tokenpaywall= [price]
try {
if (plugin.getServer().getPluginManager().isPluginEnabled("TokenManager")) {
- final TokenManager api = (TokenManager) Bukkit.getPluginManager().getPlugin("TokenManager");
- assert api != null;
- int balance = Integer.parseInt(Long.toString(api.getTokens(e.p).orElse(0)));
- if (balance >= Double.parseDouble(e.args[0])) {
- if (e.doDelete) {
- api.removeTokens(e.p, Long.parseLong(e.args[0]));
- }
- //if the message is empty don't send
- if (plugin.config.getBoolean("purchase.tokens.enable") && e.doDelete) {
- plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.success")).replaceAll("%cp-args%", e.args[0]));
+ // Using reflection in this method as TokenManager has issues with Maven due to the Jitpack dependency
+ Object api = Bukkit.getPluginManager().getPlugin("TokenManager");
+ if (api != null) {
+ // Use reflection to access the getTokens and removeTokens methods
+ Method getTokensMethod = api.getClass().getMethod("getTokens", org.bukkit.entity.Player.class);
+ Method removeTokensMethod = api.getClass().getMethod("removeTokens", org.bukkit.entity.Player.class, long.class);
+
+ // Call getTokens
+ Object result = getTokensMethod.invoke(api, e.p);
+ long balance;
+
+ if (result instanceof OptionalLong) {
+ balance = ((OptionalLong) result).orElse(0L);
+ } else {
+ balance = 0L;
}
- e.PAYWALL_OUTPUT = PaywallOutput.Passed;
- } else {
- if (plugin.config.getBoolean("purchase.tokens.enable")) {
- plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.failure")));
+ if (balance >= Double.parseDouble(e.args[0])) {
+ if (e.doDelete) {
+ // Call removeTokens
+ removeTokensMethod.invoke(api, e.p, Long.parseLong(e.args[0]));
+ }
+ // if the message is empty don't send
+ if (plugin.config.getBoolean("purchase.tokens.enable") && e.doDelete) {
+ plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.success")).replaceAll("%cp-args%", e.args[0]));
+ }
+
+ e.PAYWALL_OUTPUT = PaywallOutput.Passed;
+ } else {
+ if (plugin.config.getBoolean("purchase.tokens.enable")) {
+ plugin.tex.sendString(e.panel, PanelPosition.Top, e.p, Objects.requireNonNull(plugin.config.getString("purchase.tokens.failure")));
+ }
+ e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
+ } else {
+ plugin.tex.sendString(e.p, plugin.tag + ChatColor.RED + "Needs TokenManager to work!");
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
} else {
plugin.tex.sendString(e.p, plugin.tag + ChatColor.RED + "Needs TokenManager to work!");
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
- } catch (Exception buyc) {
- plugin.debug(buyc, e.p);
+ } catch (Exception ex) {
+ plugin.debug(ex, e.p);
plugin.tex.sendString(e.p, plugin.tag + plugin.config.getString("config.format.error") + " " + "commands: " + e.name);
e.PAYWALL_OUTPUT = PaywallOutput.Blocked;
}
diff --git a/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java b/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java
index eccb9bc..073d3cd 100644
--- a/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java
+++ b/src/me/rockyhawk/commandpanels/openpanelsmanager/UtilsPanelsLoader.java
@@ -3,7 +3,6 @@ package me.rockyhawk.commandpanels.openpanelsmanager;
import me.rockyhawk.commandpanels.CommandPanels;
import me.rockyhawk.commandpanels.api.Panel;
import me.rockyhawk.commandpanels.api.PanelClosedEvent;
-import me.rockyhawk.commandpanels.classresources.customheads.SavedCustomHead;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -15,8 +14,6 @@ import org.bukkit.event.inventory.InventoryCloseEvent;
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 {
@@ -74,13 +71,6 @@ public class UtilsPanelsLoader implements Listener {
//close panels and run commands for Top panel
plugin.openPanels.closePanelForLoader(e.getPlayer().getName(),PanelPosition.Top);
-
- //clear cached textures list until length limit is reached
- Iterator> iterator = plugin.customHeads.savedCustomHeads.entrySet().iterator();
- while (plugin.customHeads.savedCustomHeads.size() > 2000 && iterator.hasNext()) {
- iterator.next(); // Move to next entry
- iterator.remove(); // Remove the entry
- }
}
@EventHandler