Crate bevy_serde_project

Source
Expand description

§bevy_serde_project

Crates.io Docs Bevy tracking

Stateful, structural and human-readable serialization crate for the bevy engine.

§Features

  • Stateful serialization and deserialization with world access.
  • Treat an Entity, its Components and children as a single serde object.
  • Serialize Handles and provide a generalized data interning interface.
  • Serialize stored Entitys like smart pointers.
  • Deserialize trait objects like Box<dyn T>, as an alternative to typetag.
  • Extremely lightweight and modular. No systems, no plugins.
  • Supports almost every serde format*

* Ergonomics may vary depend on Serializer, Deserializer and DeserializeSeed trait support

§Getting Started

Serialize an Entity Character with some components and children, assuming all components are Serialize and DeserializeOwned:

bind_object!(Character as "Character" {
    #[serde(flatten)]
    character: Character,
    position: Position,
    #[serde(default, skip_serializing_if="Option::is_none")]
    weapon: Maybe<Weapon>,
    shield: Maybe<Shield>,
    #[serde(default, skip_serializing_if="Vec::is_empty")]
    potions: ChildVec<Potion>,
})

Then call save on World, where serializer is something like serde_json::Serializer.

// Save
world.save::<Character>(serializer)
// Load
world.load::<Character>(deserializer)
// Delete
world.despawn_bound_objects::<Character>()

This saves a list of Characters as an array:

[
    { .. },
    { .. },
    ..
]

To save multiple types of objects in a batch, create a batch serialization type with the batch! macro.

type SaveFile = batch!(
    Character, Monster, Terrain,
    // Use `BindResource` to serialize a resource.
    BindResource<MyResource>,
);
world.save::<SaveFile>(serializer)
world.load::<SaveFile>(serializer)
world.despawn_bound_objects::<SaveFile>()

This saves each type in a map entry:

{
    "Character": [ 
        { .. },
        { .. },
        ..
    ],
    "Monster": [ .. ],
    "Terrain": [ .. ],
    "MyResource": ..
}

§FAQ

  • What if my types aren’t Serialize and DeserializeOwned?

We can derive or implement SerdeProject to convert them into serde types.

  • I don’t own the type

Use Convert and the SerdeProject macro to cast the type to an owned newtype.

  • I have an ID and I want to serialize its content

SerdeProject allows you to fetch a resource from the world during serialization.

  • I have a Box<dyn T>

If you are on a non-wasm platform you can try the typetag crate. If not, or if you want more control, checkout the typetagged module in this crate.

§The traits and what they do

  • Serialize and DeserializeOwned

Any Serialize and DeserializeOwned type is automatically SerdeProject and any such Component is automatically a BevyObject.

This comes with the downside that we cannot implement SerdeProject on any foreign type due to the orphan rule. This is where Convert and the SerdeProject macro comes in handy.

  • SerdeProject

SerdeProject projects non-serde types into serde types with world access.

The SerdeProject macro implements SerdeProject on type where all fields either implements SerdeProject or converts to a SerdeProject newtype via the Convert trait.

§Example

Serialize a Handle as its path, stored in AssetServer.

#[derive(SerdeProject)]
struct MySprite {
    // implements serde, therefore is `SerdeProject`.
    pub name: String,
    // Calls `Convert` and `PathHandle<Image>` is `SerdeProject`.
    #[serde_project("PathHandle<Image>")]
    pub handle: Handle<Image>
}
  • Convert

Convert allows you to RefCast a non-serializable type to a newtype that implements SerdeProject.

For example PathHandle<Handle<T>> serializes Handle as a String, while UniqueHandle<Handle<T>> serializes Handle as a T. This zero-cost conversion can be done via the ref_cast crate.

  • BevyObject

A BevyObject allows an Entity to be serialized. This can either be just a component, or a combination of components, children, components on children, etc.

All SerdeProject components are BevyObjects.

  • BindBevyObject

BindBevyObject is a QueryFilter, usually a key component, that determines the entry point for serialization and deserialization.

Any entity that has the QueryFilter but does not satisfy the layout of the bound BevyObject will result in an error.

use the bind_object! macro to create a serialization entry.

  • Named

Provides a serialization name for resources.

  • SaveLoad

Represents a batch serialization type, or contents of a single save file.

§TypeTag

The typetag crate allows you to serialize trait objects like Box<dyn T>, but using typetag will always pull in all implementations linked to your build and does not work on WASM. To address these limitations this crate allows you to register deserializers manually in the bevy World and use the TypeTagged newtype for serialization.

world.register_typetag::<Box<dyn Animal>, Cat>()

then

#[derive(SerdeProject)]
struct MyComponent {
    #[serde_project("TypeTagged<Box<dyn Weapon>>")]
    weapon: Box<dyn Weapon>
}

To have user friendly configuration files, you can use register_deserialize_any and AnyTagged to allow deserialize_any, i.e. deserialize 42 instead of {"int": 42} in self-describing formats. Keep in mind using AnyTagged in a non-self-describing format like postcard will always return an error as this is a limitation of the serde specification.

world.register_deserialize_any(|s: &str| 
    Ok(Box::new(s.parse::<Cat>()
        .map_err(|e| e.to_string())?
    ) as Box<dyn Animal>)
)

§Versions

bevybevy-serde-project
0.13latest

§License

Licensed under either of

at your option.

§Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Modules§

asset
Module for serializing Handles and Assets.
entity
Module for serializing Entitys and EntitySmartPointers.
interning
Module for interning data in a Resource.
typetagged
Module for serializing trait objects.

Macros§

batch
Batches multiple BindBevyObject types to be serialized together as a map.
bind_object
Bind a BevyObject to a Component.

Structs§

BindResource
Bind a Resource to be serialized.
Child
Extractor for a single BevyObject in Children instead of the entity itself.
ChildMap
Extractor for matching BevyObjects on a Children.
ChildUnchecked
Extractor for a single BevyObject in Children instead of the entity itself.
ChildVec
Extractor for matching BevyObjects on a Children.
DefaultInit
Convert a Default or FromWorld component to BevyObject using default initialization.
Map
An internal struct for serializing maps with minimal trait bounds.
Maybe
Extractor that allows a BevyObject to be missing.
NoContext
Represents no context.
Null
BevyObject equivalent to ().
ProjectOption
A projection that serializes an Option containing a SerdeProject type.
WorldAccess
Represents &World and &mut World.

Enums§

Error

Traits§

BevyObject
Treat an Entity, its Components and its Children as a serializable object.
BindBevyObject
Associate a BevyObject to a EntityFilter, usually a component as With<Component>.
Convert
Newtype project a foreign type to a SerdeProject type.
EntityFilter
A subset of QueryFilter that works on EntityRef. Supports tuples, With, Without and Or.
FromWorldAccess
Convenience trait for fetching something from the World.
Named
Specify serialized name of Resource.
SaveLoad
A batch save/load type.
SerdeProject
A type serializable and deserializable with World access.
WorldExtension
Extension methods on World.

Functions§

from_world
Utility for implementing SerdeProject.
from_world_mut
Utility for implementing SerdeProject.

Type Aliases§

De
Alias for SerdeProject::De.
Object
Extractor for casting a BindBevyObject to its bound BevyObject.
ProjectMap
A projection that serializes a Map like container of SerdeProject types.
ProjectVec
A projection that serializes a Vec like container of SerdeProject types.
Ser
Alias for SerdeProject::Ser.

Derive Macros§

SerdeProject
Project a struct to and from a (de)serializable struct using World access. Requires all fields with SerdeProject or Serialize + DeserializeOwned implementations.