palkia/serde/
mod.rs

1/*!
2Serializing and deserializing worlds.
3
4Worlds are stored as:
5
6- a mapping of user-defined keys to resource data
7- the backing allocator for the entities
8- a mapping of entities to, a mapping of "friendly-name" keys to component data.
9
10Although some of the internals of this module are exposed, in practice you
11should just have to call
12[`World::serialize`] and [`World::deserialize`], and it should Just Work(tm).
13
14In pseudo-Ron, a serialized world will look something like this:
15
16```text
17SerDeWorld(
18    // The allocator (generational_arena) serializes itself;
19    // this is what it happens to look like on the inside.
20    // Frankly I'm not really sure what it's doing; the internals of that crate are
21    // really smart.
22    // (It uses a skip list to compactly store where the free entity slots are,
23    // didja know?!)
24    allocator: [
25        Some(0, ()),
26        Some(1, ()),
27        Some(2, ()),
28        Some(3, ()),
29        None, None, None, None
30    ],
31    resources: {
32        // Assuming you have some struct MyResource { foo: i32, bar: i32 }
33        "my_resource": (foo: 10, bar: 20),
34        "my_other_resource": (baz: "fizzbuzz", quxx: (spam: "eggs")),
35        ...
36    },
37    entities: {
38        // Entities are stored as [index, generation]
39        [0,0]: [
40            {"position": [0.0, 1.0, 2.0]},
41            {"velocity": [0.1, 0.2, 0.3]},
42            {"player": ()},
43        ],
44        [1,0]: {
45            {"position": [0.0, 1.0, 2.0]},
46            {"velocity": [0.1, 0.2, 0.3]},
47        },
48        [2,0]: {
49            {"position": [0.0, 1.0, 2.0]},
50            {"velocity": [0.1, 0.2, 0.3]},
51            {"collider": ()},
52        },
53        ...
54    }
55)
56```
57
58Note that after deserializing, world insertion callbacks WILL be called
59 So, if you're using those callbacks to
60create a cache, like for (say) entity positions, then you
61shouldn't serialize whatever you're caching, or invalidate it before you
62load it.
63
64---
65
66Note that the entity serialization requires the ability to have keys
67that aren't strings. So, if you want to use a human-readable format,
68json won't work. But [Ron](https://crates.io/crates/ron) works great.
69
70For something compact, remember that a lot of binary formats aren't amazingly
71compatible when the schema changes.
72I personally haven't looked into this, but it might be worth using something
73like [MessagePack](https://github.com/3Hren/msgpack-rust)
74which serializes struct field names so you can change component definitions
75without breaking things.
76
77If you're worried about this leading to gigantic save files, gzipping it should
78probably help a lot.
79
80But, you can freely add *new* component types as you develop a game, and old saves should be compatible.
81
82*/
83
84mod component;
85mod entity;
86mod resource;
87
88use generational_arena::Arena;
89
90use serde::{
91  de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer,
92};
93
94use crate::{
95  prelude::World, vtablesathome::DeserializeFn, world::storage::EntityStorage,
96};
97
98use self::{
99  entity::{EntitiesDeWrapper, EntitiesSerWrapper},
100  resource::{ResourcesDeWrapper, ResourcesSerWrapper},
101};
102
103impl Serialize for World {
104  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105  where
106    S: Serializer,
107  {
108    let allocator = self.entities.allocator.try_read().unwrap();
109    let entities = EntitiesSerWrapper::new(self);
110    let resources = ResourcesSerWrapper::new(self);
111
112    let wrapper = WorldSerWrapper {
113      allocator: &*allocator,
114      entities,
115      resources,
116    };
117    wrapper.serialize(serializer)
118  }
119}
120
121impl<'de> Deserialize<'de> for World {
122  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
123  where
124    D: Deserializer<'de>,
125  {
126    let wrapper = WorldDeWrapper::deserialize(deserializer)?;
127
128    let mut world = World::new();
129    // do i ... repeat, repeat myself?
130    world.resources = wrapper.resources.resources;
131    world.entities =
132      EntityStorage::new(wrapper.allocator, wrapper.entities.entities);
133
134    for e in world.entities() {
135      world.run_creation_callbacks(e);
136    }
137    world.finalize();
138    Ok(world)
139  }
140}
141
142#[derive(Serialize)]
143struct WorldSerWrapper<'w> {
144  allocator: &'w Arena<()>,
145  entities: EntitiesSerWrapper<'w>,
146  resources: ResourcesSerWrapper<'w>,
147}
148
149#[derive(Deserialize)]
150struct WorldDeWrapper {
151  allocator: Arena<()>,
152  entities: EntitiesDeWrapper,
153  resources: ResourcesDeWrapper,
154}
155
156struct ErasedSerWrapper<'a, T: ?Sized> {
157  inner: &'a T,
158}
159
160impl<'a, T: ?Sized> ErasedSerWrapper<'a, T> {
161  fn new(inner: &'a T) -> Self {
162    Self { inner }
163  }
164}
165
166impl<'a, T: ?Sized> Serialize for ErasedSerWrapper<'a, T>
167where
168  T: erased_serde::Serialize,
169{
170  fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171  where
172    S: Serializer,
173  {
174    erased_serde::serialize(self.inner, serializer)
175  }
176}
177
178/// Deserializer that applies the deser fn to an erased deserializer.
179/// Used for component/resource deserialization.
180///
181/// Thanks typetag for notes here.
182struct ApplyDeserFn<T: ?Sized> {
183  deser: DeserializeFn<T>,
184}
185
186impl<'de, T> DeserializeSeed<'de> for ApplyDeserFn<T>
187where
188  T: ?Sized,
189{
190  type Value = Box<T>;
191
192  fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
193  where
194    D: Deserializer<'de>,
195  {
196    // it's automatically maintainable and readable because it's written
197    // in crab language
198    let mut erased = <dyn erased_serde::Deserializer>::erase(deserializer);
199    (self.deser)(&mut erased).map_err(serde::de::Error::custom)
200  }
201}