Serde

Serializing an ECS can be very easy or difficult, it all depends on what you're trying to achieve.
For example, let's say we only want to serialize two components: Player and Position.
We have many format options:

  1. An array of (EntityId, Player) then another array of (EntityId, Position).
    This could include entities that have either component or only the ones that have both.
  2. An array of (EntityId, (Player, Position)).
  3. An array of EntityId then an array of (Player, Position).
  4. An array of EntityId then another of Player then yet another of Position.
  5. The list goes on.

So which option is the best? It depends on the use case.
Option 1, for example, is one of the worst at memory but the best at retrieving random components.

There are as many options possible when deserializing: should components be overwritten? should EntityIds match? ...

EntityId

Serializing EntityId is technically all that is needed to serialize all entities and components in a World.
It will require lots of work on the user's side but is the most flexible.
This will let us pick the best format for our use case.

EntityId is very easy to (de)serialize. Example:

use shipyard::{EntityId, World};

let mut world = World::new();

let eid1 = world.add_entity(());

let serialized = serde_json::to_string(&eid1).unwrap();
assert_eq!(serialized, r#"{"index":0,"gen":0}"#);

let new_eid: EntityId = serde_json::from_str(&serialized).unwrap();
assert_eq!(new_eid, eid1);

A Vec<EntityId> would be just as simple.

Views

This is where our options become limited.
If shipyard does the entire view(s) serialization, it has to pick a format.

The current implementation leaves the door open for future user customization.
For now, only Option 1 is implemented. Each component will create an array of (EntityId, Component).
When deserializing, components will be attributed to the same EntityId they were serialized with. They will override any existing component.

use shipyard::{Component, EntityId, View, ViewMut, World};

#[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Name(String);

let mut world = World::new();

let eid1 = world.add_entity(Name("Alice".to_string()));
let eid2 = world.add_entity(Name("Bob".to_string()));

// There is also a World::serialize
let serialized = world.run(|v_name: View<Name>| serde_json::to_string(&v_name).unwrap());

drop(world);

let mut world = World::new();

let mut deserializer = serde_json::de::Deserializer::from_str(&serialized);
world
    .deserialize::<_, ViewMut<Name>>(&mut deserializer)
    .unwrap();

assert_eq!(world.get::<&Name>(eid2).unwrap().0, "Bob");
assert_eq!(world.get::<&Name>(eid1).unwrap().0, "Alice");

// Note that we never added eid1 or eid2 to this second World
// they weren't added during deserialization either
// the World is currently in an unstable state

assert_eq!(world.is_entity_alive(eid1), false);

// To fix it, we can use `World::spawn` for example
// we could've also created empty entities
// or (de)serialized EntitiesViewMut

world.spawn(eid1);
world.spawn(eid2);

assert_eq!(world.is_entity_alive(eid1), true);
assert_eq!(world.is_entity_alive(eid2), true);

Serializing multiple components is not that much more work.

use shipyard::{
    error, Component, EntitiesViewMut, EntityId, View, ViewMut, World, WorldBorrow,
};

#[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
struct Name(String);

#[derive(Component, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
enum FavoriteLanguage {
    Rust,
}

#[derive(WorldBorrow, serde::Serialize, serde::Deserialize)]
struct LanguagesViewMut<'v> {
    #[serde(borrow)]
    entities: EntitiesViewMut<'v>,
    #[serde(borrow)]
    vm_name: ViewMut<'v, Name>,
    #[serde(borrow)]
    vm_favorite_language: ViewMut<'v, FavoriteLanguage>,
}

let mut world = World::new();

let eid1 = world.add_entity((Name("Alice".to_string()), FavoriteLanguage::Rust));
let eid2 = world.add_entity(Name("Bob".to_string()));

let serialized =
    world.run(|vm_languages: LanguagesViewMut| serde_json::to_string(&vm_languages).unwrap());

drop(world);

let mut world = World::new();

let mut deserializer = serde_json::de::Deserializer::from_str(&serialized);
world
    .deserialize::<_, LanguagesViewMut>(&mut deserializer)
    .unwrap();

assert_eq!(world.get::<&Name>(eid1).unwrap().0, "Alice");
assert_eq!(
    *world.get::<&FavoriteLanguage>(eid1).unwrap(),
    &FavoriteLanguage::Rust
);
assert_eq!(world.get::<&Name>(eid2).unwrap().0, "Bob");
assert!(matches!(
    world.get::<&FavoriteLanguage>(eid2),
    Err(error::GetComponent::MissingComponent(_))
));

// This time we serialized EntitiesViewMut
// so no unstable state

assert_eq!(world.is_entity_alive(eid1), true);
assert_eq!(world.is_entity_alive(eid2), true);