diff --git a/pom.xml b/pom.xml index e74de44..0fa646d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,14 +6,14 @@ tech.sbdevelopment ShowControl - 1.6.1 + 1.7 jar ShowControl https://sbdevelopment.tech Create shows easily using this plugin! - 11 + 17 UTF-8 ${project.build.directory}/javadoc-delombok @@ -39,7 +39,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 ${release} @@ -57,7 +57,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.4.1 + 3.6.0 package @@ -115,7 +115,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.0 + 3.11.2 ${release} ${maven.lombok.delombok-target} @@ -157,7 +157,7 @@ org.spigotmc spigot-api - 1.20.2-R0.1-SNAPSHOT + 1.21.4-R0.1-SNAPSHOT provided @@ -168,7 +168,7 @@ com.github.cryptomorin XSeries - 9.10.0 + 13.0.0 co.aikar @@ -179,7 +179,7 @@ com.github.SkytAsul GuardianBeam - 97b0078aba + 2.4.0 compile diff --git a/src/main/java/tech/sbdevelopment/showcontrol/elements/Fireworks.java b/src/main/java/tech/sbdevelopment/showcontrol/elements/Fireworks.java index 2719e38..fccd01e 100644 --- a/src/main/java/tech/sbdevelopment/showcontrol/elements/Fireworks.java +++ b/src/main/java/tech/sbdevelopment/showcontrol/elements/Fireworks.java @@ -50,7 +50,7 @@ public class Fireworks { } private void spawn(Location loc) { - org.bukkit.entity.Firework fw = (org.bukkit.entity.Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK); + org.bukkit.entity.Firework fw = (org.bukkit.entity.Firework) loc.getWorld().spawnEntity(loc, EntityType.FIREWORK_ROCKET); FireworkMeta fwm = fw.getFireworkMeta(); fwm.addEffect(effectBuilder.build()); fwm.setPower(power); diff --git a/src/main/java/tech/sbdevelopment/showcontrol/utils/ItemBuilder.java b/src/main/java/tech/sbdevelopment/showcontrol/utils/ItemBuilder.java index 64eb9ae..099fef3 100644 --- a/src/main/java/tech/sbdevelopment/showcontrol/utils/ItemBuilder.java +++ b/src/main/java/tech/sbdevelopment/showcontrol/utils/ItemBuilder.java @@ -1,7 +1,9 @@ package tech.sbdevelopment.showcontrol.utils; -import com.cryptomorin.xseries.SkullUtils; import com.cryptomorin.xseries.XMaterial; +import com.cryptomorin.xseries.profiles.builder.XSkull; +import com.cryptomorin.xseries.profiles.objects.ProfileInputType; +import com.cryptomorin.xseries.profiles.objects.Profileable; import lombok.Getter; import org.bukkit.Color; import org.bukkit.Material; @@ -10,15 +12,19 @@ import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.potion.PotionData; +import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.potion.PotionType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.function.UnaryOperator; +import static tech.sbdevelopment.showcontrol.utils.ReflectionUtil.callDeclaredConstructor; +import static tech.sbdevelopment.showcontrol.utils.ReflectionUtil.callDeclaredMethod; + @Getter public class ItemBuilder { private final ItemStack itemStack; @@ -87,12 +93,14 @@ public class ItemBuilder { return flag(ItemFlag.values()); } - public ItemBuilder customModelData(int customModelData) { - if (XMaterial.supports(13)) { + public ItemBuilder customModelData(int customModelData, Function notSupportedFunction) { + if (XMaterial.supports(14)) { applyToMeta(meta -> { meta.setCustomModelData(customModelData); return meta; }); + } else { + return notSupportedFunction.apply(this); } return this; } @@ -140,7 +148,7 @@ public class ItemBuilder { public ItemBuilder skullTexture(String identifier) { applyToMeta(meta -> { - SkullUtils.applySkin(meta, identifier); + meta = XSkull.of(meta).profile(Profileable.of(ProfileInputType.USERNAME, identifier)).apply(); return meta; }); return this; @@ -160,7 +168,15 @@ public class ItemBuilder { throw new UnsupportedOperationException("ItemStack is not a potion! (Type: " + itemStack.getType().name() + ")"); applyToMeta(meta -> { if (!(meta instanceof org.bukkit.inventory.meta.PotionMeta)) return meta; - ((org.bukkit.inventory.meta.PotionMeta) meta).setBasePotionData(new PotionData(type)); + PotionMeta pm = (PotionMeta) meta; + try { + pm.setBasePotionType(type); + } catch (NoSuchMethodError e) { + // Fallback for versions below 1.20.5 (using reflection) + Class potionDataClass = ReflectionUtil.getClass("org.bukkit.potion.PotionData"); + Object potionData = callDeclaredConstructor(potionDataClass, type); + callDeclaredMethod(pm, "setBasePotionData", potionDataClass, potionData); + } return meta; }); return this; diff --git a/src/main/java/tech/sbdevelopment/showcontrol/utils/ReflectionUtil.java b/src/main/java/tech/sbdevelopment/showcontrol/utils/ReflectionUtil.java new file mode 100644 index 0000000..4b656dc --- /dev/null +++ b/src/main/java/tech/sbdevelopment/showcontrol/utils/ReflectionUtil.java @@ -0,0 +1,309 @@ +package tech.sbdevelopment.showcontrol.utils; + +import lombok.experimental.UtilityClass; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +@UtilityClass +public class ReflectionUtil { + private static final Map> constructorCache = new HashMap<>(); + private static final Map methodCache = new HashMap<>(); + private static final Map fieldCache = new HashMap<>(); + + /** + * Helper class converted to {@link List} + * + * @param The storage type + */ + public static class ListParam extends ArrayList { + } + + /** + * Helper class converted to {@link Collection} + * + * @param The storage type + */ + public static class CollectionParam extends ArrayList { + } + + private static Class wrapperToPrimitive(Class clazz) { + if (clazz == Boolean.class) return boolean.class; + if (clazz == Integer.class) return int.class; + if (clazz == Double.class) return double.class; + if (clazz == Float.class) return float.class; + if (clazz == Long.class) return long.class; + if (clazz == Short.class) return short.class; + if (clazz == Byte.class) return byte.class; + if (clazz == Void.class) return void.class; + if (clazz == Character.class) return char.class; + if (clazz == CollectionParam.class) return Collection.class; + if (clazz == ListParam.class) return List.class; + if (clazz == ArrayList.class) return Collection.class; //LEGACY! + if (clazz == HashMap.class) return Map.class; + return clazz; + } + + private static Class[] toParamTypes(Object... params) { + return Arrays.stream(params) + .map(obj -> obj != null ? wrapperToPrimitive(obj.getClass()) : null) + .toArray(Class[]::new); + } + + public static Class getClass(String name) { + try { + return Class.forName(name); + } catch (ClassNotFoundException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callConstructorNull(Class clazz, Class paramClass) { + try { + String cacheKey = "ConstructorNull:" + clazz.getName() + ":" + paramClass.getName(); + + if (constructorCache.containsKey(cacheKey)) { + Constructor cachedConstructor = constructorCache.get(cacheKey); + return cachedConstructor.newInstance(clazz.cast(null)); + } else { + Constructor con = clazz.getConstructor(paramClass); + con.setAccessible(true); + constructorCache.put(cacheKey, con); + return con.newInstance(clazz.cast(null)); + } + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | + InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callFirstConstructor(Class clazz, Object... params) { + try { + String cacheKey = "FirstConstructor:" + clazz.getName(); + + if (constructorCache.containsKey(cacheKey)) { + Constructor cachedConstructor = constructorCache.get(cacheKey); + return cachedConstructor.newInstance(params); + } else { + Constructor con = clazz.getConstructors()[0]; + con.setAccessible(true); + constructorCache.put(cacheKey, con); + return con.newInstance(params); + } + } catch (IllegalAccessException | InstantiationException | + InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callConstructor(Class clazz, Object... params) { + try { + String cacheKey = "Constructor:" + clazz.getName() + ":" + Arrays.hashCode(params); + + if (constructorCache.containsKey(cacheKey)) { + Constructor cachedConstructor = constructorCache.get(cacheKey); + return cachedConstructor.newInstance(params); + } else { + Constructor con = clazz.getConstructor(toParamTypes(params)); + con.setAccessible(true); + constructorCache.put(cacheKey, con); + return con.newInstance(params); + } + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | + InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callDeclaredConstructor(Class clazz, Object... params) { + try { + String cacheKey = "DeclaredConstructor:" + clazz.getName() + ":" + Arrays.hashCode(params); + + if (constructorCache.containsKey(cacheKey)) { + Constructor cachedConstructor = constructorCache.get(cacheKey); + return cachedConstructor.newInstance(params); + } else { + Constructor con = clazz.getDeclaredConstructor(toParamTypes(params)); + con.setAccessible(true); + constructorCache.put(cacheKey, con); + return con.newInstance(params); + } + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | + InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callMethod(Class clazz, String method, Object... params) { + try { + String cacheKey = "Method:" + clazz.getName() + ":" + method + ":" + Arrays.hashCode(params); + + if (methodCache.containsKey(cacheKey)) { + Method cachedMethod = methodCache.get(cacheKey); + return cachedMethod.invoke(null, params); + } else { + Method m = clazz.getMethod(method, toParamTypes(params)); + m.setAccessible(true); + methodCache.put(cacheKey, m); + return m.invoke(null, params); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callMethod(Object obj, String method, Object... params) { + try { + String cacheKey = "Method:" + obj.getClass().getName() + ":" + method + ":" + Arrays.hashCode(params); + + if (methodCache.containsKey(cacheKey)) { + Method cachedMethod = methodCache.get(cacheKey); + return cachedMethod.invoke(obj, params); + } else { + Method m = obj.getClass().getMethod(method, toParamTypes(params)); + m.setAccessible(true); + methodCache.put(cacheKey, m); + return m.invoke(obj, params); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object callDeclaredMethod(Object obj, String method, Object... params) { + try { + String cacheKey = "DeclaredMethod:" + obj.getClass().getName() + ":" + method + ":" + Arrays.hashCode(params); + + if (methodCache.containsKey(cacheKey)) { + Method cachedMethod = methodCache.get(cacheKey); + return cachedMethod.invoke(obj, params); + } else { + Method m = obj.getClass().getDeclaredMethod(method, toParamTypes(params)); + m.setAccessible(true); + methodCache.put(cacheKey, m); + return m.invoke(obj, params); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) { + ex.printStackTrace(); + return null; + } + } + + public static boolean hasField(Object packet, String field) { + try { + String cacheKey = "HasField:" + packet.getClass().getName() + ":" + field; + + if (!fieldCache.containsKey(cacheKey)) { + packet.getClass().getDeclaredField(field); + fieldCache.put(cacheKey, null); + } + return true; + } catch (NoSuchFieldException ex) { + return false; + } + } + + public static Object getField(Object object, String field) { + try { + String cacheKey = "Field:" + object.getClass().getName() + ":" + field; + + if (fieldCache.containsKey(cacheKey)) { + Field cachedField = fieldCache.get(cacheKey); + return cachedField.get(object); + } else { + Field f = object.getClass().getField(field); + f.setAccessible(true); + fieldCache.put(cacheKey, f); + return f.get(object); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object getDeclaredField(Class clazz, String field) { + try { + String cacheKey = "DeclaredField:" + clazz.getName() + ":" + field; + + if (fieldCache.containsKey(cacheKey)) { + Field cachedField = fieldCache.get(cacheKey); + return cachedField.get(null); + } else { + Field f = clazz.getDeclaredField(field); + f.setAccessible(true); + fieldCache.put(cacheKey, f); + return f.get(null); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object getDeclaredField(Object object, String field) { + try { + String cacheKey = "DeclaredField:" + object.getClass().getName() + ":" + field; + + if (fieldCache.containsKey(cacheKey)) { + Field cachedField = fieldCache.get(cacheKey); + return cachedField.get(object); + } else { + Field f = object.getClass().getDeclaredField(field); + f.setAccessible(true); + fieldCache.put(cacheKey, f); + return f.get(object); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + return null; + } + } + + public static Object getDeclaredField(Class clazz, Object object, String field) { + try { + String cacheKey = "DeclaredField:" + clazz.getName() + ":" + field; + + if (fieldCache.containsKey(cacheKey)) { + Field cachedField = fieldCache.get(cacheKey); + return cachedField.get(object); + } else { + Field f = clazz.getDeclaredField(field); + f.setAccessible(true); + fieldCache.put(cacheKey, f); + return f.get(object); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + return null; + } + } + + public static void setDeclaredField(Object object, String field, Object value) { + try { + String cacheKey = "DeclaredField:" + object.getClass().getName() + ":" + field; + + if (fieldCache.containsKey(cacheKey)) { + Field cachedField = fieldCache.get(cacheKey); + cachedField.set(object, value); + } else { + Field f = object.getClass().getDeclaredField(field); + f.setAccessible(true); + fieldCache.put(cacheKey, f); + f.set(object, value); + } + } catch (NoSuchFieldException | IllegalAccessException ex) { + ex.printStackTrace(); + } + } +} \ No newline at end of file