New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[B+C] Add ItemMeta.hasCustomData/getCustomData. Adds BUKKIT-3221 #1064
base: master
Are you sure you want to change the base?
Changes from 9 commits
a6631c4
10b3367
9c68c5e
8ec74fc
0f3caaa
5998742
9aeeb63
6efa7b2
a53497b
b8ed542
3f49f5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.bukkit.metadata; | ||
|
||
import org.bukkit.configuration.serialization.ConfigurationSerializable; | ||
import org.bukkit.plugin.Plugin; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
* A PersistentMetadataValue is a special case metadata item that may | ||
* only contain ConfigurationSerializable Objects, Lists, Maps or | ||
* basic Object types. | ||
* <p> | ||
* This class is primarily here to serve as a reminder that | ||
* a persistent data store must hold serializable data. | ||
*/ | ||
public class PersistentMetadataValue extends MetadataValueAdapter implements MetadataValue { | ||
/** | ||
* Store the internal value that is represented by this value. | ||
* This is expected to be one of a set of a valid serializable types. | ||
*/ | ||
private final Object internalValue; | ||
|
||
/** | ||
* Initializes a PersistentMetadataValue with an Object value. | ||
* <p> | ||
* The Object must be one of the valid persistable object types. | ||
* Otherwise, an IllegalArgumentException will be thrown. | ||
* | ||
* @throws java.lang.IllegalArgumentException on non-persistable input | ||
* @param owningPlugin the {@link org.bukkit.plugin.Plugin} that created this metadata value | ||
* @param value the value assigned to this metadata value | ||
*/ | ||
public PersistentMetadataValue(Plugin owningPlugin, final Object value) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hrm, type erasure really sucks, this isn't safe. Map<Player, Location> would pass your check and then fail to persistent later. Don't know if we can fix this but it would be nice to look in to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a similar issue even without type erasure, unless we actually walk through Lists and Maps here. Otherwise there could always be something non-serializeable buried deep inside. I will admit the PersistentMetadataValue class feels a little tacked-on right now. I recently re-worked it, originally I had separate constructors for each type it could accept, which actually works a lot better for compile-time validation, and for sort of nudging callers in the right direction. But I found myself, internally in the NBTMetadataStore, wanting to just be able to call an Object constructor for convenience. I could put it back the other way, and put the onus of calling the correct constructed based on the the type of data being stored on the caller (including NBTMetadataStore itself). I kind of liked the forced-type constructors, otherwise there's not much point to this class, honestly. The Map and List constructors could also do recursive type inspection of contents, but I think that will have the type erasure issue one way or another for any nested Lists or Maps.. right? Anyway, whatever you think is best- this side of the API is kind of loose to me, I really want it to be better but I'm struggling to find a good fit. Another good point someone brought up earlier, it'd be nice if PersistentMetadataValue could somehow throw an error if added to a non-peristent store, though I don't see a way to make that happen. It was suggested that developers might suddenly think all Metadatable stores supported persistence if they just use this special value type, and since that is not true I can see how this may be confusing in general. |
||
super(owningPlugin); | ||
this.internalValue = value; | ||
|
||
if (!( | ||
value instanceof ConfigurationSerializable | ||
|| value instanceof String | ||
|| value instanceof Integer | ||
|| value instanceof Double | ||
|| value instanceof Long | ||
|| value instanceof Boolean | ||
|| value instanceof Short | ||
|| value instanceof Float | ||
|| value instanceof Map | ||
|| value instanceof List | ||
)) { | ||
throw new IllegalArgumentException("Invalid Object type for PersistentMetadataValue: " + value.getClass()); | ||
} | ||
} | ||
|
||
@Override | ||
public Object value() { | ||
return internalValue; | ||
} | ||
|
||
@Override | ||
public void invalidate() { | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't actually support subclassing this class so mentioning you can override it to make it faster doesn't make sense. Same this for #hasMetadata(String, Plugin). I'm not sure what you mean about checking for it without unpacking it, can you explain this better? If it means what I think it does that shouldn't be here either since, again, the only supported implementation is the one that you've written here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may have worded this poorly- by "subclass" I just meant CraftItemStack, though that really doesn't belong in the comments here.
Basically, the way it's implemented here isn't really useful- but the overridden implementation on CraftItemStack (which I think you will get at least some of, if not the majority of, the time?) provides much quicker access.
One of my main concerns with implementing this as a "get a copy of the data" system is that the data could be quite large, and copying it expensive. I wanted to ensure devs could at least do a quick "is there data here I care about?" check before having to make a copy of all data on the item (even data they don't care about) to inspect it.
So, that said, the implementations of these methods on CraftItemStack do not call the meta methods, but rather call static NBTDatastore methods to do direct inspection of the data without making a copy.