WrappedDataWatcher.setObject with unwrapped value being null uses wrong NMS method and causes NPE with unclear reason
Janmm14 opened this issue ยท 1 comments
Describe the bug
I want to send an EntityMetadata packet via MC 1.8 version of WrapperPlayServerEntityMetadata to set the item stack of the item object I spawned. I know that the WrapperPlayServerEntityMetadata packet uses a List of watchableobjects, but I still want to use WrappedDataWatcher to abstract the internal difference between WrapperPlayServerSpawnEntityLiving and WrapperPlayServerSpawnEntity.
I think that this should not error regardless of actual use case.
To Reproduce
Code (kotlin):
val watcher = WrappedDataWatcher()
watcher.setObject(10, ItemStack(Material.AIR)) // <- Error
Stack trace:
java.lang.RuntimeException: An internal error occured.
at com.comphenix.protocol.reflect.accessors.DefaultMethodAccessor.invoke(DefaultMethodAccessor.java:20) ~[?:?]
at com.comphenix.protocol.wrappers.WrappedDataWatcher.setObject(WrappedDataWatcher.java:509) ~[?:?]
at com.comphenix.protocol.wrappers.WrappedDataWatcher.setObject(WrappedDataWatcher.java:400) ~[?:?]
at com.comphenix.protocol.wrappers.WrappedDataWatcher.setObject(WrappedDataWatcher.java:407) ~[?:?]
at ...................... ~[?:?] (multiple lines redacted)
at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:350) [spigot.jar:git-Spigot-db6de12-18fbb24]
at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:723) [spigot.jar:git-Spigot-db6de12-18fbb24]
at net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:374) [spigot.jar:git-Spigot-db6de12-18fbb24]
at net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:654) [spigot.jar:git-Spigot-db6de12-18fbb24]
at net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:557) [spigot.jar:git-Spigot-db6de12-18fbb24]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_262]
Caused by: java.lang.NullPointerException
at net.minecraft.server.v1_8_R3.DataWatcher.a(DataWatcher.java:33) ~[spigot.jar:git-Spigot-db6de12-18fbb24]
at sun.reflect.GeneratedMethodAccessor8.invoke(Unknown Source) ~[?:?]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_262]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_262]
at com.comphenix.protocol.reflect.accessors.DefaultMethodAccessor.invoke(DefaultMethodAccessor.java:16) ~[?:?]
... 31 more
Expected behavior
No error.
Screenshots
If applicable, add screenshots to help explain your problem.
Version Info
ProtocolLib Version: ProtocolLib v4.5.0
Bukkit Version: 1.8.8-R0.1-SNAPSHOT
Server Version: git-Spigot-db6de12-18fbb24 (MC: 1.8.8)
Java Version: 1.8.0_262
Other debug stuff of /protocol dump seems to be completely normal and therefore not included. No reload done.
Additional context
The NPE is thrown at line 33 of DataWatcher.java
. I'll add a part of the source code here (obtained through Buildtools 1.8.8) for convenience:
public <T> void a(int i, T t0) {
int integer = classToId.get(t0.getClass()); // Spigot // <---- Line 33
if (integer == -1) { // Spigot
throw new IllegalArgumentException("Unknown data type: " + t0.getClass());
} else if (i > 31) {
throw new IllegalArgumentException("Data value id is too big with " + i + "! (Max is " + 31 + ")");
} else if (this.dataValues.containsKey(i)) { // Spigot
throw new IllegalArgumentException("Duplicate id value for " + i + "!");
} else {
DataWatcher.WatchableObject datawatcher_watchableobject = new DataWatcher.WatchableObject(integer, i, t0); // Spigot
this.f.writeLock().lock();
this.dataValues.put(i, datawatcher_watchableobject); // Spigot
this.f.writeLock().unlock();
this.b = false;
}
}
Apparantly this is caused by the usage of AIR as Material.
There is method in the NMS 1.8 datawatcher which allows adding a null value specifically, it is even renamed in spigot source to add(int, int)
. This should be used when encountering a null unwrapped value. Alternatively throw an exception when encounrering a null unwrapped value.
The code of the WrappedDataWatcher.setObject method should be altered to do a null check on the unwrapped value and use the specific method for adding a null value to a DataWatcher.
Unfortunaly however looking at the mcp code of the 1.8.8 client, it should be possible to send and recieve an AIR item stack without problem. But it looks like the method to write an item stack has been modified in CraftBukkit so that it does send -1 and not 0 when an air itemstack (represented in nms as itemstack.getItem() == null ) is encountered, as well as CraftItemStack.asNMSCopy is unfortunaly treating an AIR item stack identically to a null item stack.
The reason I was trying to do his with an AIR item stack vanished however, I've decided to switch to another entity type for what I was trying to do.