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}