Skip to content

Commit

Permalink
serde hell
Browse files Browse the repository at this point in the history
  • Loading branch information
JieningYu committed Feb 6, 2024
1 parent 255887d commit 081da24
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 20 deletions.
34 changes: 34 additions & 0 deletions core/item/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "rimecraft-item"
version = "0.1.0"
edition = "2021"
authors = ["JieningYu <[email protected]>"]
description = "Minecraft Item primitives and registry"
repository = "https://github.com/rimecraft-rs/rimecraft/"
license = "AGPL-3.0-or-later"
categories = []

[badges]
maintenance = { status = "passively-maintained" }

[dependencies]
rimecraft-registry = { path = "../../util/registry" }
rimecraft-fmt = { path = "../../util/fmt" }
rimecraft-freezer = { path = "../../util/freezer" }
rimecraft-attachment = { path = "../../util/attachment" }
rimecraft-serde-update = { path = "../../util/serde-update", optional = true }
fastnbt = "2.4"
rimecraft-nbt-ext = { path = "../../util/nbt-ext" }
serde = { version = "1.0", optional = true, features = ["derive"] }

[features]
default = ["serde"]
serde = [
"dep:serde",
"rimecraft-registry/serde",
"rimecraft-attachment/serde",
"dep:rimecraft-serde-update",
]

[lints]
workspace = true
115 changes: 115 additions & 0 deletions core/item/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! Minecraft Item primitives and registry.

use std::{marker::PhantomData, num::NonZeroU32};

use rimecraft_fmt::Formatting;
use rimecraft_registry::{ProvideRegistry, Reg};

mod stack;

pub use stack::ItemStack;

/// Item containing settings.
#[derive(Debug)]
pub struct RawItem<P> {
settings: Settings,
_marker: PhantomData<P>,
}

impl<P> RawItem<P> {
/// Creates a new `Item` with the given settings.
#[inline]
pub const fn new(settings: Settings) -> Self {
Self {
settings,
_marker: PhantomData,
}
}

/// Returns the settings of the item.
#[inline]
pub fn settings(&self) -> &Settings {
&self.settings
}
}

impl<P> From<Settings> for RawItem<P> {
#[inline]
fn from(settings: Settings) -> Self {
Self::new(settings)
}
}

impl<'r, K, P> ProvideRegistry<'r, K, Self> for RawItem<P>
where
P: ProvideRegistry<'r, K, Self>,
{
#[inline]
fn registry() -> &'r rimecraft_registry::Registry<K, Self> {
P::registry()
}
}

/// An item usable by players and other entities.
pub type Item<'r, K, P> = Reg<'r, K, RawItem<P>>;

/// A trait for converting a value to an [`Item`].
pub trait ToItem<'s, 'r, K, P> {
/// Converts the value to an [`Item`].
fn to_item(&'s self) -> Item<'r, K, P>;
}

/// The max item count of an `ItemStack`.
pub const MAX_STACK_COUNT: u32 = 64;

/// Settings of an [`Item`].
///
/// A setting configure behaviors common to all items, such as the
/// stack's max count.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Settings {
/// The maximum count of the item that can be stacked in a single slot.
pub max_count: NonZeroU32,
/// The maximum amount of damage the item can take.
pub max_damage: Option<NonZeroU32>,

/// The rarity of the item.
pub rarity: Rarity,
}

impl Default for Settings {
#[inline]
fn default() -> Self {
Self {
max_count: NonZeroU32::new(MAX_STACK_COUNT).unwrap(),
max_damage: None,
rarity: Default::default(),
}
}
}

/// Rarity of an item.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum Rarity {
/// Common rarity.
#[default]
Common,
/// Uncommon rarity.
Uncommon,
/// Rare rarity.
Rare,
/// Epic rarity.
Epic,
}

impl From<Rarity> for Formatting {
#[inline]
fn from(value: Rarity) -> Self {
match value {
Rarity::Common => Formatting::White,
Rarity::Uncommon => Formatting::Yellow,
Rarity::Rare => Self::Aqua,
Rarity::Epic => Self::LightPurple,
}
}
}
169 changes: 169 additions & 0 deletions core/item/src/stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use rimecraft_attachment::Attachments;
use rimecraft_nbt_ext::Compound;

use std::{hash::Hash, marker::PhantomData};

use crate::{Item, ToItem};

/// A stack of items.
///
/// This is a data container that holds the item count and the stack's NBT.
#[derive(Debug)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound(
serialize = "K: serde::Serialize + Hash + Eq",
deserialize = r#"
'r: 'de,
K: serde::Deserialize<'de> + rimecraft_serde_update::Update<'de> + Hash + Eq + std::fmt::Debug + 'r,
P: InitAttachments<K> + rimecraft_registry::ProvideRegistry<'r, K, crate::RawItem<P>> + 'r"#
))
)]
pub struct ItemStack<'r, K, P> {
#[cfg_attr(feature = "serde", serde(rename = "id"))]
item: Item<'r, K, P>,

#[cfg_attr(feature = "serde", serde(rename = "Count"))]
count: u32,

/// Item stack's custom NBT.
#[cfg_attr(feature = "serde", serde(rename = "tag"), serde(default))]
nbt: Option<Compound>,

#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "should_skip_attachment_ser"),
serde(default),
serde(serialize_with = "ser_attachments"),
serde(deserialize_with = "deser_attachments")
)]
attachments: (Attachments<K>, PhantomData<P>),
}

#[cfg(feature = "serde")]
fn should_skip_attachment_ser<K, P>(attachments: &(Attachments<K>, PhantomData<P>)) -> bool {
attachments.0.is_persistent_data_empty()
}

#[cfg(feature = "serde")]
fn ser_attachments<K, P, S>(
attachments: &(Attachments<K>, PhantomData<P>),
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
K: serde::Serialize + Hash + Eq,
{
serde::Serialize::serialize(&attachments.0, serializer)
}

#[cfg(feature = "serde")]
fn deser_attachments<'de, K, P, D>(
deserializer: D,
) -> Result<(Attachments<K>, PhantomData<P>), <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
P: InitAttachments<K>,
K: serde::Deserialize<'de> + rimecraft_serde_update::Update<'de> + Hash + Eq,
{
use rimecraft_serde_update::Update;
let mut attachments = Attachments::new();
P::init_attachments(&mut attachments);
attachments.update(deserializer)?;
Ok((attachments, PhantomData))
}

impl<'r, K, P> ItemStack<'r, K, P>
where
P: InitAttachments<K>,
{
/// Creates a new item stack with the given item and count.
#[inline]
pub fn new(item: Item<'r, K, P>, count: u32) -> Self {
Self::with_nbt(item, count, None)
}

/// Creates a new item stack with the given item, count,
/// and custom NBT tag.
pub fn with_nbt(item: Item<'r, K, P>, count: u32, nbt: Option<Compound>) -> Self {
let mut attachments = Attachments::new();
P::init_attachments(&mut attachments);

Self {
item,
count,
nbt,
attachments: (attachments, PhantomData),
}
}
}

impl<'r, K, P> ItemStack<'r, K, P> {
/// Returns the item of the stack.
#[inline]
pub fn item(&self) -> Item<'r, K, P> {
self.item
}

/// Returns the count of the stack.
#[inline]
pub fn count(&self) -> u32 {
self.count
}

/// Returns the custom NBT of the stack.
#[inline]
pub fn nbt(&self) -> Option<&Compound> {
self.nbt.as_ref()
}

/// Returns a mutable reference to the custom NBT of the stack.
#[inline]
pub fn nbt_mut(&mut self) -> Option<&mut Compound> {
self.nbt.as_mut()
}

/// Returns the custom NBT of the stack, create one if it does not exist.
#[inline]
pub fn get_or_create_nbt(&mut self) -> &mut Compound {
self.nbt.get_or_insert_with(Compound::new)
}

/// Sets the count of the stack.
#[inline]
pub fn set_count(&mut self, count: u32) {
self.count = count;
}

/// Sets the custom NBT of the stack.
#[inline]
pub fn set_nbt(&mut self, nbt: Option<Compound>) {
self.nbt = nbt;
}

/// Returns the attachments of the stack.
#[inline]
pub fn attachments(&self) -> &Attachments<K> {
&self.attachments.0
}

/// Returns the mutable view of attachments of the stack.
#[inline]
pub fn attachments_mut(&mut self) -> &mut Attachments<K> {
&mut self.attachments.0
}
}

impl<'s, 'r, K, P> ToItem<'s, 'r, K, P> for ItemStack<'r, K, P> {
#[inline]
fn to_item(&'s self) -> Item<'r, K, P> {
self.item
}
}

/// A trait for initializing attachments of an item stack.
pub trait InitAttachments<K> {
/// Initializes the attachments of the item stack.
fn init_attachments(attachments: &mut Attachments<K>);
}
4 changes: 1 addition & 3 deletions core/state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ maintenance = { status = "passively-maintained" }
[dependencies]
regex-lite = "0.1"
serde = { version = "1.0", optional = true }
rimecraft-serde-update = { path = "../../util/serde-update", optional = true }

[features]
default = ["serde"]
serde = ["dep:serde", "dep:rimecraft-serde-update"]
serde = ["dep:serde"]

[lints]
workspace = true
13 changes: 11 additions & 2 deletions util/attachment/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub struct Attachments<K> {
serde_state: crate::serde::State<K>,
}

impl<K: 'static> Attachments<K> {
impl<K> Attachments<K> {
/// Creates a new [`Attachments`] instance.
#[inline]
pub fn new() -> Self {
Expand All @@ -87,7 +87,7 @@ impl<K: 'static> Attachments<K> {
}
}

impl<K: 'static> Default for Attachments<K> {
impl<K> Default for Attachments<K> {
#[inline]
fn default() -> Self {
Self::new()
Expand Down Expand Up @@ -170,6 +170,15 @@ impl<K: Hash + Eq> Attachments<K> {
}
}

impl<K> Attachments<K> {
/// Whether the persistent data queue is empty.
#[cfg(feature = "serde")]
#[inline]
pub fn is_persistent_data_empty(&self) -> bool {
self.serde_state.ser.is_empty() && self.serde_state.update.is_empty()
}
}

impl<K, T> Clone for Type<K, T>
where
K: Clone,
Expand Down
8 changes: 4 additions & 4 deletions util/attachment/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ type SerState<K> = Vec<(K, Box<dyn Fn() -> Box<dyn AsErasedSerialize>>)>;
type UpdateState<K> = HashMap<K, Box<dyn Fn() -> Box<dyn AsErasedUpdate>>>;

pub(crate) struct State<K> {
ser: SerState<K>,
update: UpdateState<K>,
pub ser: SerState<K>,
pub update: UpdateState<K>,
}

impl<K> State<K> {
Expand Down Expand Up @@ -121,7 +121,7 @@ impl<'a, T: 'a> AsAttachmentMut<'a> for Persistent<T> {
}
}

trait AsErasedSerialize {
pub(crate) trait AsErasedSerialize {
fn as_serialize(&self) -> &dyn erased_serde::Serialize;
}

Expand All @@ -136,7 +136,7 @@ where
}
}

trait AsErasedUpdate {
pub(crate) trait AsErasedUpdate {
fn as_update(&mut self) -> &mut dyn for<'de> rimecraft_serde_update::erased::ErasedUpdate<'de>;
}

Expand Down
Loading

0 comments on commit 081da24

Please sign in to comment.