Skip to content

Commit 684b687

Browse files
committed
Rework ItemStack handling for 1.11
Note to developers: You should never supply a null ItemStack to any method. ProtocolLib will never return a null ItemStack. Update your plugins accordingly.
1 parent 7a9b9af commit 684b687

File tree

2 files changed

+105
-13
lines changed

2 files changed

+105
-13
lines changed

modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import javax.annotation.Nonnull;
4242

4343
import org.bukkit.Bukkit;
44+
import org.bukkit.Material;
4445
import org.bukkit.Server;
4546
import org.bukkit.inventory.ItemStack;
4647

@@ -128,13 +129,13 @@ public class MinecraftReflection {
128129
private static String packageVersion;
129130

130131
// Item stacks
131-
private static Method craftNMSMethod;
132+
/* private static Method craftNMSMethod;
132133
private static Method craftBukkitNMS;
133134
private static Method craftBukkitOBC;
134135
private static boolean craftItemStackFailed;
135136
136137
private static Constructor<?> craftNMSConstructor;
137-
private static Constructor<?> craftBukkitConstructor;
138+
private static Constructor<?> craftBukkitConstructor; */
138139

139140
// net.minecraft.server
140141
private static Class<?> itemStackArrayClass;
@@ -1840,7 +1841,7 @@ public static boolean signUpdateExists() {
18401841
* @param bukkitItemStack - the Bukkit ItemStack to convert.
18411842
* @return A CraftItemStack as an ItemStack.
18421843
*/
1843-
public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
1844+
/* public static ItemStack getBukkitItemStack(ItemStack bukkitItemStack) {
18441845
// Delegate this task to the method that can execute it
18451846
if (craftBukkitNMS != null)
18461847
return getBukkitItemByMethod(bukkitItemStack);
@@ -1890,7 +1891,7 @@ private static ItemStack getBukkitItemByMethod(ItemStack bukkitItemStack) {
18901891
* @param minecraftItemStack - the NMS ItemStack to wrap.
18911892
* @return The wrapped ItemStack.
18921893
*/
1893-
public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
1894+
/* public static ItemStack getBukkitItemStack(Object minecraftItemStack) {
18941895
// Delegate this task to the method that can execute it
18951896
if (craftNMSMethod != null)
18961897
return getBukkitItemByMethod(minecraftItemStack);
@@ -1941,13 +1942,104 @@ private static ItemStack getBukkitItemByMethod(Object minecraftItemStack) {
19411942
* @param stack - the Bukkit ItemStack to convert.
19421943
* @return The NMS ItemStack, or NULL if the stack represents air.
19431944
*/
1944-
public static Object getMinecraftItemStack(ItemStack stack) {
1945+
/* public static Object getMinecraftItemStack(ItemStack stack) {
19451946
// Make sure this is a CraftItemStack
19461947
if (!isCraftItemStack(stack))
19471948
stack = getBukkitItemStack(stack);
19481949
19491950
BukkitUnwrapper unwrapper = new BukkitUnwrapper();
19501951
return unwrapper.unwrapItem(stack);
1952+
} */
1953+
1954+
// ---- ItemStack conversions
1955+
1956+
private static Method asNMSCopy = null;
1957+
private static Method asCraftMirror = null;
1958+
1959+
private static Boolean nullEnforced = null;
1960+
private static Method isEmpty = null;
1961+
1962+
/**
1963+
* Retrieves the Bukkit equivalent of a NMS ItemStack. This method should
1964+
* preserve NBT data and will never return null when supplied with a valid
1965+
* ItemStack. Empty ItemStacks are returned as AIR.
1966+
*
1967+
* @param generic NMS ItemStack
1968+
* @return The Bukkit equivalent
1969+
*/
1970+
public static ItemStack getBukkitItemStack(Object generic) {
1971+
// Make sure it actually is an ItemStack
1972+
if (!is(getItemStackClass(), generic)) {
1973+
return null;
1974+
}
1975+
1976+
// Convert null to AIR
1977+
if (generic == null) {
1978+
return new ItemStack(Material.AIR);
1979+
}
1980+
1981+
// Check null enforcement
1982+
try {
1983+
if (nullEnforced == null) {
1984+
isEmpty = getItemStackClass().getMethod("isEmpty");
1985+
nullEnforced = true;
1986+
}
1987+
1988+
if (nullEnforced) {
1989+
if ((boolean) isEmpty.invoke(generic)) {
1990+
return new ItemStack(Material.AIR);
1991+
}
1992+
}
1993+
} catch (ReflectiveOperationException ex) {
1994+
nullEnforced = false;
1995+
}
1996+
1997+
// Find asCraftMirror
1998+
if (asCraftMirror == null) {
1999+
try {
2000+
asCraftMirror = getCraftItemStackClass().getMethod("asCraftMirror", getItemStackClass());
2001+
} catch (ReflectiveOperationException ex) {
2002+
throw new RuntimeException("Failed to obtain CraftItemStack.asCraftMirror", ex);
2003+
}
2004+
}
2005+
2006+
// Convert to a craft mirror to preserve NBT data
2007+
try {
2008+
return (ItemStack) asCraftMirror.invoke(nullEnforced, generic);
2009+
} catch (ReflectiveOperationException ex) {
2010+
throw new RuntimeException("Failed to obtain craft mirror of " + generic, ex);
2011+
}
2012+
}
2013+
2014+
/**
2015+
* Retrieves the NMS equivalent of a Bukkit ItemStack. This method will
2016+
* never return null and should preserve NBT data. Null inputs are treated
2017+
* as empty (AIR) ItemStacks.
2018+
*
2019+
* @param specific Bukkit ItemStack
2020+
* @return The NMS equivalent
2021+
*/
2022+
public static Object getMinecraftItemStack(ItemStack specific) {
2023+
// Grab asNMSCopy first
2024+
if (asNMSCopy == null) {
2025+
try {
2026+
asNMSCopy = getCraftItemStackClass().getMethod("asNMSCopy", ItemStack.class);
2027+
} catch (ReflectiveOperationException ex) {
2028+
throw new RuntimeException("Failed to obtain CraftItemStack.asNMSCopy", ex);
2029+
}
2030+
}
2031+
2032+
// If it's already a CraftItemStack, use its handle
2033+
if (is(getCraftItemStackClass(), specific)) {
2034+
return new BukkitUnwrapper().unwrapItem(specific);
2035+
}
2036+
2037+
// If it's not, grab a NMS copy
2038+
try {
2039+
return asNMSCopy.invoke(null, specific);
2040+
} catch (ReflectiveOperationException ex) {
2041+
throw new RuntimeException("Failed to make NMS copy of " + specific, ex);
2042+
}
19512043
}
19522044

19532045
/**

modules/API/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -713,24 +713,24 @@ public Class<Entity> getSpecificType() {
713713
* @return Item stack converter.
714714
*/
715715
public static EquivalentConverter<ItemStack> getItemStackConverter() {
716-
return new IgnoreNullConverter<ItemStack>() {
716+
return new EquivalentConverter<ItemStack>() {
717717
@Override
718-
protected Object getGenericValue(Class<?> genericType, ItemStack specific) {
719-
return MinecraftReflection.getMinecraftItemStack(specific);
718+
public ItemStack getSpecific(Object generic) {
719+
return MinecraftReflection.getBukkitItemStack(generic);
720720
}
721-
721+
722722
@Override
723-
protected ItemStack getSpecificValue(Object generic) {
724-
return MinecraftReflection.getBukkitItemStack(generic);
723+
public Object getGeneric(Class<?> genericType, ItemStack specific) {
724+
return MinecraftReflection.getMinecraftItemStack(specific);
725725
}
726-
726+
727727
@Override
728728
public Class<ItemStack> getSpecificType() {
729729
return ItemStack.class;
730730
}
731731
};
732732
}
733-
733+
734734
/**
735735
* Retrieve the converter for the ServerPing packet in {@link PacketType.Status.Server#OUT_SERVER_INFO}.
736736
* @return Server ping converter.

0 commit comments

Comments
 (0)