/** * Determine if this workaround is required. * * @return TRUE if we must use this workaround, FALSE otherwise. */ public static boolean isRequired() { Boolean result = IS_REQUIRED; if (result != null) { return result; } try { // Used to check if custom NBT data survives the round trip NbtCompound proof = NbtFactory.ofCompound("", Collections.singletonList(NbtFactory.of("value", "TEST"))); ItemStack source = new ItemStack(Material.IRON_AXE); ItemStack stored = CompoundStore.getNativeStore(source, "com.comphenix.test").saveCompound(proof); StreamSerializer rawSerializer = new StreamSerializer(); SpigotSafeSerializer safeSerializer = new SpigotSafeSerializer(); // We are testing the raw serializer here ItemStack roundTrip = safeSerializer.deserializeItemStack(rawSerializer.serializeItemStack(stored)); NbtCompound extracted = CompoundStore.getNativeStore(roundTrip, "com.comphenix.test").loadCompound(); // Did it survive unscathed? result = !Objects.equal(proof, extracted); IS_REQUIRED = result; return result; } catch (IOException e) { throw new RuntimeException("Unexpected error during round trip test.", e); } }
@Override public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException { // Speed things up if the workaround is not required if (!isRequired()) { super.serializeItemStack(output, stack); return; } NbtCompound tag = stack != null && stack.getType() != Material.AIR ? NbtFactory.asCompound(NbtFactory.fromItemTag(stack)) : null; // Note that we can't write the stack data directly, as 1.8 switched to sending a // item name instead of an item ID. if (tag != null) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); DataOutputStream dataStream = new DataOutputStream(byteStream); ItemStack withoutTag = new ItemStack(stack.getType(), stack.getAmount(), stack.getDurability()); // This will write the stack as normal, without its tag compound and the tag field length super.serializeItemStack(dataStream, withoutTag); output.write(byteStream.toByteArray(), 0, byteStream.size() - 2); serializeCompound(output, tag); } else { // Write the stack as normal super.serializeItemStack(output, stack); } }
/** * Construct a wrapper for an NBT tag stored (in memory) in an item stack. This is where auxillary * data such as enchanting, name and lore is stored. It doesn't include the items material, damage * value or count. * * <p>The item stack must be a wrapper for a CraftItemStack. Use {@link * MinecraftReflection#getCraftItemStack(ItemStack)} if not. * * @param stack - the item stack. * @return A wrapper for its NBT tag. */ public static NbtWrapper<?> fromItemTag(ItemStack stack) { checkItemStack(stack); StructureModifier<NbtBase<?>> modifier = getStackModifier(stack); NbtBase<?> result = modifier.read(0); // Create the tag if it doesn't exist if (result == null) { result = NbtFactory.ofCompound("tag"); modifier.write(0, result); } return fromBase(result); }
/** * Read an ItemStack from a input stream without "scrubbing" the NBT content. * * @param input - the input stream. * @return The deserialized item stack. * @throws IOException If anything went wrong. */ @Override public ItemStack deserializeItemStack(DataInputStream input) throws IOException { ItemStack result = null; short type = input.readShort(); if (type >= 0) { byte amount = input.readByte(); short damage = input.readShort(); result = new ItemStack(type, amount, damage); NbtCompound tag = super.deserializeCompound(input); if (tag != null) { result = MinecraftReflection.getBukkitItemStack(result); NbtFactory.setItemTag(result, tag); } } return result; }
/** * Retrieve the NBT compound from a given NMS handle. * * @param handle - the underlying net.minecraft.server object to wrap. * @return A NBT compound wrapper */ public static NbtCompound fromNMSCompound(@Nonnull Object handle) { if (handle == null) throw new IllegalArgumentException("handle cannot be NULL."); return (NbtCompound) NbtFactory.<Map<String, NbtBase<?>>>fromNMS(handle); }