Skip to content

Commit bb01df2

Browse files
committed
Added option to set unbreakable in legacy
added option to also set legacy on old versions.
1 parent 42f9767 commit bb01df2

File tree

2 files changed

+241
-1
lines changed

2 files changed

+241
-1
lines changed

Item Creator/src/main/java/org/broken/arrow/library/itemcreator/ItemCreator.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.broken.arrow.library.color.TextTranslator;
55
import org.broken.arrow.library.itemcreator.utility.ConvertToItemStack;
66
import org.broken.arrow.library.itemcreator.utility.ServerVersion;
7+
import org.broken.arrow.library.itemcreator.utility.UnbreakableUtil;
78
import org.broken.arrow.library.itemcreator.utility.builders.ItemBuilder;
89
import org.broken.arrow.library.logging.Logging;
910
import org.broken.arrow.library.logging.Validate;
@@ -14,6 +15,7 @@
1415
import org.bukkit.NamespacedKey;
1516
import org.bukkit.enchantments.Enchantment;
1617
import org.bukkit.inventory.ItemStack;
18+
import org.bukkit.inventory.meta.ItemMeta;
1719
import org.bukkit.map.MapView;
1820
import org.bukkit.plugin.Plugin;
1921

@@ -33,6 +35,7 @@
3335
public class ItemCreator {
3436
private static final Logging log = new Logging(ItemCreator.class);
3537
private static ServerVersion serverVersion;
38+
private static UnbreakableUtil unbreakableUtil;
3639
private static Plugin plugin;
3740
private final NBTManger nbtManger;
3841
private final ConvertToItemStack convertItems;
@@ -443,6 +446,48 @@ private static Enchantment getEnchantment(@Nullable final NamespacedKey key, @Nu
443446
return enchantment == null ? Enchantment.VANISHING_CURSE : enchantment;
444447
}
445448

449+
/**
450+
* Applies the "Unbreakable" property to the given ItemMeta.
451+
*
452+
* <p>On legacy versions (1.8–1.12), this will return a new copy of the metadata.
453+
* On modern versions (1.13+), the original metadata instance is modified and returned.</p>
454+
*
455+
* @param meta the ItemMeta to modify
456+
* @param unbreakable true to make the item unbreakable, false otherwise
457+
* @return the modified ItemMeta, it will be a new instance on legacy versions.
458+
*/
459+
public static ItemMeta applyUnbreakable(final ItemMeta meta, final boolean unbreakable) {
460+
return UnbreakableUtil.applyToMeta(meta,unbreakable);
461+
}
462+
463+
/**
464+
* Applies the "Unbreakable" property directly to the given ItemStack.
465+
*
466+
* <p>On legacy versions, this may create a new ItemStack copy.
467+
* On modern versions, the original ItemStack is modified.</p>
468+
*
469+
* @param item the ItemStack to modify
470+
* @param unbreakable true to make the item unbreakable, false otherwise
471+
* @return the modified ItemStack, may be a new instance on legacy versions
472+
*/
473+
public static ItemStack applyUnbreakableToItem(final ItemStack item, final boolean unbreakable) {
474+
return UnbreakableUtil.applyToItem(item,unbreakable);
475+
}
476+
477+
/**
478+
* Checks whether the given ItemStack is marked as unbreakable.
479+
*
480+
* <p>On modern versions, this reads the metadata.
481+
* On legacy versions, it falls back to NBT reflection.</p>
482+
*
483+
* @param item the ItemStack to check
484+
* @return {@code true} if the item is unbreakable, {@code false} otherwise (or if the check failed on legacy versions)
485+
*/
486+
public static boolean isUnbreakable(ItemStack item) {
487+
return UnbreakableUtil. isUnbreakable(item);
488+
}
489+
490+
446491
/**
447492
* Helper for scaling images to map dimensions (e.g. 128×128).
448493
*
@@ -500,5 +545,4 @@ private static void runWithSchedulerFallback(@Nonnull Runnable runnable) {
500545
private static void setPlugin(final Plugin pluginInstance) {
501546
plugin = pluginInstance;
502547
}
503-
504548
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package org.broken.arrow.library.itemcreator.utility;
2+
3+
import org.broken.arrow.library.logging.Logging;
4+
import org.bukkit.Bukkit;
5+
import org.bukkit.Material;
6+
import org.bukkit.inventory.ItemStack;
7+
import org.bukkit.inventory.meta.ItemMeta;
8+
9+
import java.lang.reflect.Constructor;
10+
import java.lang.reflect.InvocationTargetException;
11+
import java.lang.reflect.Method;
12+
13+
public final class UnbreakableUtil {
14+
private static final Logging logger = new Logging(UnbreakableUtil.class);
15+
private static Method asNMSCopyMethod = null;
16+
private static Method asBukkitCopyMethod = null;
17+
private static Method hasTagMethod = null;
18+
private static Method getTagMethod = null;
19+
private static Method setTagMethod = null;
20+
private static Method setBooleanMethod = null;
21+
private static Method getBooleanMethod = null;
22+
private static Method metaGetMaterialMethod = null;
23+
private static Constructor<?> nbtTagConstructor = null;
24+
25+
private static boolean reflectionReady = false;
26+
private static boolean modernSupported;
27+
28+
29+
static {
30+
try {
31+
Class.forName("org.bukkit.inventory.meta.ItemMeta").getMethod("setUnbreakable", boolean.class);
32+
modernSupported = true;
33+
} catch (Throwable ignored) {
34+
modernSupported = false;
35+
}
36+
37+
if (!modernSupported) {
38+
try {
39+
// ---- Legacy NBT way (1.8 - 1.12) ----
40+
String craftPath = getCraftBukkitPath();
41+
42+
final Class<?> craftItemStackClass = Class.forName(craftPath + ".inventory.CraftItemStack");
43+
asNMSCopyMethod = craftItemStackClass.getMethod("asNMSCopy", ItemStack.class);
44+
asBukkitCopyMethod = craftItemStackClass.getMethod("asBukkitCopy", Class.forName(getMinecraftPath() + ".ItemStack"));
45+
46+
Class<?> craftMetaItem = Class.forName(craftPath + ".inventory.CraftMetaItem"
47+
);
48+
metaGetMaterialMethod = craftMetaItem.getDeclaredMethod("getMaterial");
49+
metaGetMaterialMethod.setAccessible(true);
50+
51+
final Class<?> nbtTagCompoundClass = Class.forName(getMinecraftPath() + ".NBTTagCompound");
52+
53+
nbtTagConstructor = nbtTagCompoundClass.getConstructor();
54+
55+
Class<?> nmsStackClass = Class.forName(getMinecraftPath() + ".ItemStack");
56+
57+
hasTagMethod = nmsStackClass.getMethod("hasTag");
58+
getTagMethod = nmsStackClass.getMethod("getTag");
59+
setTagMethod = nmsStackClass.getMethod("setTag", nbtTagCompoundClass);
60+
setBooleanMethod = nbtTagCompoundClass.getMethod("setBoolean", String.class, boolean.class);
61+
getBooleanMethod = nbtTagCompoundClass.getMethod("getBoolean", String.class);
62+
63+
reflectionReady = true;
64+
} catch (ClassNotFoundException | NoSuchMethodException t) {
65+
logger.logError(t, () -> "Could not resolve the unbreakable tag for this minecraft version");
66+
reflectionReady = false;
67+
}
68+
}
69+
}
70+
71+
/**
72+
* Applies the "Unbreakable" property to the given ItemMeta.
73+
*
74+
* <p>On legacy versions (1.8–1.12), this will return a new copy of the metadata.
75+
* On modern versions (1.13+), the original metadata instance is modified and returned.</p>
76+
*
77+
* @param meta the ItemMeta to modify
78+
* @param unbreakable true to make the item unbreakable, false otherwise
79+
* @return the modified ItemMeta, it will be a new instance on legacy versions.
80+
*/
81+
public static ItemMeta applyToMeta(final ItemMeta meta, final boolean unbreakable) {
82+
if (meta == null) return null;
83+
84+
if (modernSupported) {
85+
meta.setUnbreakable(unbreakable);
86+
return meta;
87+
}
88+
if (!reflectionReady) return meta;
89+
90+
try {
91+
Material original = (Material) metaGetMaterialMethod.invoke(meta);
92+
if (original == null || original == Material.AIR) original = Material.STONE;
93+
94+
ItemStack stack = new ItemStack(original);
95+
stack.setItemMeta(meta);
96+
97+
stack = applyToItem(stack, unbreakable);
98+
return stack.getItemMeta();
99+
} catch (InvocationTargetException | IllegalAccessException e) {
100+
logger.logError(e, () -> "Failed to invoke the material for this meta via legacy fallback");
101+
return meta;
102+
}
103+
}
104+
105+
/**
106+
* Applies the "Unbreakable" property directly to the given ItemStack.
107+
*
108+
* <p>On legacy versions, this may create a new ItemStack copy.
109+
* On modern versions, the original ItemStack is modified.</p>
110+
*
111+
* @param item the ItemStack to modify
112+
* @param unbreakable true to make the item unbreakable, false otherwise
113+
* @return the modified ItemStack, may be a new instance on legacy versions
114+
*/
115+
public static ItemStack applyToItem(final ItemStack item, final boolean unbreakable) {
116+
if (item == null) return null;
117+
118+
if (modernSupported) {
119+
ItemMeta meta = item.getItemMeta();
120+
if (meta != null)
121+
meta.setUnbreakable(unbreakable);
122+
item.setItemMeta(meta);
123+
return item;
124+
}
125+
126+
if (!reflectionReady) return item;
127+
128+
try {
129+
Object nmsItem = getNmsStack(item);
130+
131+
boolean hasTag = (Boolean) hasTagMethod.invoke(nmsItem);
132+
Object tag = hasTag
133+
? getTagMethod.invoke(nmsItem)
134+
: nbtTagConstructor.newInstance();
135+
136+
setBooleanMethod.invoke(tag, "Unbreakable", unbreakable);
137+
setTagMethod.invoke(nmsItem, tag);
138+
139+
return (ItemStack) asBukkitCopyMethod.invoke(null, nmsItem);
140+
141+
} catch (Throwable t) {
142+
logger.logError(t, () -> "Failed to apply legacy unbreakable");
143+
return item;
144+
}
145+
}
146+
147+
/**
148+
* Checks whether the given ItemStack is marked as unbreakable.
149+
*
150+
* <p>On modern versions, this reads the metadata.
151+
* On legacy versions, it falls back to NBT reflection.</p>
152+
*
153+
* @param item the ItemStack to check
154+
* @return {@code true} if the item is unbreakable, {@code false} otherwise (or if the check failed on legacy versions)
155+
*/
156+
public static boolean isUnbreakable(ItemStack item) {
157+
if (modernSupported) {
158+
final ItemMeta itemMeta = item.getItemMeta();
159+
return itemMeta != null && itemMeta.isUnbreakable();
160+
} else {
161+
if (!reflectionReady) return false;
162+
try {
163+
Object nmsItem = getNmsStack(item);
164+
165+
if (!(Boolean) hasTagMethod.invoke(nmsItem)) {
166+
return false;
167+
}
168+
Object tag = getTagMethod.invoke(nmsItem);
169+
return (Boolean) getBooleanMethod.invoke(tag, "Unbreakable");
170+
} catch (Throwable e) {
171+
logger.logError(e, () -> "Failed to invoke the item to check if it is unbreakable.");
172+
return false;
173+
}
174+
}
175+
}
176+
177+
178+
179+
private static String getMinecraftPath() {
180+
return "net.minecraft.server."
181+
+ getPackageVersion();
182+
}
183+
184+
private static String getCraftBukkitPath() {
185+
return "org.bukkit.craftbukkit." + getPackageVersion();
186+
}
187+
188+
189+
private static String getPackageVersion() {
190+
return Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
191+
}
192+
193+
private static Object getNmsStack(final ItemStack item) throws IllegalAccessException, InvocationTargetException {
194+
return asNMSCopyMethod.invoke(null, item);
195+
}
196+
}

0 commit comments

Comments
 (0)