-
-
Notifications
You must be signed in to change notification settings - Fork 145
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
implement (de)serialize for ItemStack
and ItemKind
#660
Conversation
I thought about doing this PR myself also, however I felt like it was a bit dangerous to expose this feature in the "core" of the framwork. If people start relying on this serialization to work across versions we are limited in what kind of changes we can do to the generation of My personal stance on this is that we should keep serialization out of the scope for the framework(core), as the added overhead of serialized items will result in one of two things (or both):
I think we would be leaning a bit more towards approach 2 here right? from the perspective of what commitment we make for the serialization and de serialization to work over time. I'm not really sure what would be the ideal solution for this (possibly a valance_serialize module that could deal with all (or part of the complexity)? (at the very least tag a serialized "thing" with a version so we can reject de-serializing and make sure we never fail silently)) until then i think it might be wise to leave any kind of serialization and de-serialization to the user. I would be happy to hear all of your opinions on it, but I personally would be a bit scared of us going around and making the structs serializable without some kind of abstraction layer. I think it could be a false sense of security to the users of the framework that this serialization can be safely used, and if we want to commit to that assumption users might make, we are limited in the ways we can change these structs. |
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.
Definitely needs some tests for the ItemKind impls.
where | ||
S: Serializer, | ||
{ | ||
serializer.serialize_str(self.to_str()) |
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.
What if we serialized this into the Minecraft identifier for the item instead? Would that make more sense?
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.
like minecraft:white_wool
or the numerical ID?
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.
Yeah the string ident. Minecraft doesn't have public facing numerical IDs anymore
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.
From what I can tell from the latest efforts to update the string ids are not stable across versions either, updates to Minecraft versions could break de-serilization.
These changes should not be silent (unless a rename (from A to B) and a new item gets introduced with the name A. Where the produced result would yeild another item than what was intended when serializing.
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 assume that if minecraft were to change their IDs, then the enum variants generated by the build script would also change.
If thats the case id say thats out of scope for valence.
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.
yes i think then we probably want to create a valence_serde
or valence_serialize
crate where we can create wrapper structs for commonly serialized structs, like Items, Inventory-content, etc.
Ill try to implement that if I have time for that.
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.
thoughts on something like this? would live in its own valence_serde
crate and be behind a serde
feature flag
/// A Wrapper around [`ItemKind`] that provides serialization and deserialization support.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub struct SerItemKind(pub ItemKind);
impl Serialize for SerItemKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.to_str().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for SerItemKind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = serde::Deserialize::deserialize(deserializer)?;
let item_kind = ItemKind::from_str(s).ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &"the snake case item name, like \"white_wool\"")
});
item_kind.map(SerItemKind)
}
}
impl From<ItemKind> for SerItemKind {
fn from(kind: ItemKind) -> Self {
SerItemKind(kind)
}
}
impl From<SerItemKind> for ItemKind {
fn from(kind: SerItemKind) -> Self {
kind.0
}
}
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 think a wrapper like this should store a private field about what version it was serialized on so we can prevent silent misuse of these across versions. (By giving an error when de-serilization happens in case the version is not the same as the constant defined as "current")
Other than that I think its along the lines of what i expect.
It also needs some documentation about what compability we provide (or in this case lack of compability), doc comments on the wrapper is probably a good place to place such a note.
Also the use cases I primarily imagined dealt mostly with item stacks, but i assume that might also be in scope of what you want to provide in the upcoming pr, there you might also want to support lists of items with a single version tag for space efficiency.
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.
it could also defeer the "failing" to the call to into() (on the SerItemKind) (making it a try_into() and failing when version does not match)
optionaly it could expose a way to get at the itemkind, as long as its really clear that its dangerous, maybe something like unsafe fn get_itemkind_unchecked() -> ItemKind
, should ofc have clear docs on why its dangerous and what should be considered
But it really depends on if we think people are going to use that, it could be left out if we think it would not bring any value
Note: this requires the internal item stack to be private to the module (so people cant access it without using the propper api) and thats probably a good default anyway
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.
Its pretty much a tradeoff between ease of use and safety.
Either we can have those safety checks, but it wouldnt be as easy to use (because of the wrapper types)
or we dont have safety checks and directly implement serialization on the raw types (behind a feature flag).
or we could also implement both maybe, as a serde
and serde_unsafe
feature?
where one would perform those version checks and the other one wouldnt.
I do really think being able to serialize and deserialize the raw structs would be more convenient.
we could put that behind a feature flag
we could either not care, or we could also implement a custom deserializer that would be backwards compatible, but i think its pretty unlikely that the ItemStack / ItemKind format will change any time soon. |
You are right in that we could add a custom deserializer that is backwards compatible, but does such a concern really belong in I also don't see why I could see a future where #[derive(Clone, PartialEq, Debug, Default)]
pub struct ItemStack {
pub item: ItemRegistyItemRef, // <- where this is just an identifier to an item registry that has all the vanilla items and all of the user defined items that might be present in mods.
pub count: i8,
pub nbt: Option<Compound>,
} ^ im not saying we can't support a backwards compatible de-serialize here either by referencing the registry, but it doubt it would be trivial, and definitely not something that should live in this module imo. While everybody might not consider supporting communicating inventory updates and similar stuff containing itemstacks with items that are added via some mod to be in scope of valance, I think a change such as this could have the ability to limit what kind of changes are easy to do (while also being backwards compatible with a serialization feature, and I think most people expect a serialized format to be stable). For a change as the above if serialization to still work with the old format would have to take a dependency on wherever the registry lives just for the sake of being able to support deserializing, while it might not if serialization is not a consern of the I also think all my concerns goes away if we put all of this serialization stuff in its own module, that can deal with the eventual complexities that might arrive if the core framework evolves to for example support the above (and possibly other cases too) |
ill close that and open up a new PR where the serialization will be implemented in its own crate (using wrapper structs that can be serialized as @lukashermansson suggested) |
Objective
Solution
serde::Serialize
andserde::Deserialize
, ItemKinds will be serialized to their snake case name: e.gwhite_wool