-
Notifications
You must be signed in to change notification settings - Fork 221
/
saveload.rs
255 lines (225 loc) · 9.27 KB
/
saveload.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
extern crate ron;
#[macro_use]
extern crate serde;
extern crate specs;
use ron::ser::PrettyConfig;
use std::{convert::Infallible, fmt};
use specs::{
prelude::*,
saveload::{
DeserializeComponents, MarkedBuilder, SerializeComponents, SimpleMarker,
SimpleMarkerAllocator,
},
};
// This is an example of how the serialized data of two entities might look on
// disk.
//
// When serializing entities, they are written in an array of tuples, each tuple
// representing one entity. The entity's marker and components are written as
// fields into these tuples, knowing nothing about the original entity's id.
const ENTITIES: &str = "
[
(
marker: (0),
components: (
Some((
x: 10,
y: 20,
)),
Some((30.5)),
),
),
(
marker: (1),
components: (
Some(Pos(
x: 5,
y: 2,
)),
None,
),
),
]
";
// A dummy component that can be serialized and deserialized.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
struct Pos {
x: f32,
y: f32,
}
impl Component for Pos {
type Storage = VecStorage<Self>;
}
// A dummy component that can be serialized and deserialized.
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
struct Mass(f32);
impl Component for Mass {
type Storage = VecStorage<Self>;
}
// It is necessary to supply the `(De)SerializeComponents`-trait with an error
// type that implements the `Display`-trait. In this case we want to be able to
// return different errors, and we are going to use a `.ron`-file to store our
// data. Therefore we use a custom enum, which can display both the
// `Infallible`and `ron::error::Error` type. This enum could be extended to
// incorporate for example `std::io::Error` and more.
#[derive(Debug)]
enum Combined {
Ron(ron::error::Error),
}
// Implementing the required `Display`-trait, by matching the `Combined` enum,
// allowing different error types to be displayed.
impl fmt::Display for Combined {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Combined::Ron(ref e) => write!(f, "{}", e),
}
}
}
// This returns the `ron::ser:Error` in form of the `Combined` enum, which can
// then be matched and displayed accordingly.
impl From<ron::error::Error> for Combined {
fn from(x: ron::error::Error) -> Self {
Combined::Ron(x)
}
}
// This cannot be called.
impl From<Infallible> for Combined {
fn from(e: Infallible) -> Self {
match e {}
}
}
struct NetworkSync;
fn main() {
let mut world = World::new();
// Since in this example no system uses these resources, they have to be
// registered manually. This is typically not required.
world.register::<Pos>();
world.register::<Mass>();
world.register::<SimpleMarker<NetworkSync>>();
// Adds a predefined marker allocator to the world, as a resource.
// This predifined marker uses a `HashMap<u64, Entity>` to keep track of all
// entities that should be (de)serializable, as well as which ids are
// already in use.
world.insert(SimpleMarkerAllocator::<NetworkSync>::new());
world
.create_entity()
.with(Pos { x: 1.0, y: 2.0 })
.with(Mass(0.5))
// The `.marked` function belongs to the [`MarkedBuilder`](struct.MarkedBuilder.html) trait,
// which is implemented for example for the [`EntityBuilder`](struct.EntityBuilder.html).
// It yields the next higher id, that is not yet in use.
//
// Since the `Marker` is passed as a generic type parameter, it is possible to use several different `MarkerAllocators`,
// e.g. to keep track of different types of entities, with different ids.
// **Careful when deserializing, it is not always clear for every fileforamt whether a number is supposed to be i.e. a `u32` or `u64`!**
.marked::<SimpleMarker<NetworkSync>>()
.build();
world
.create_entity()
.with(Pos { x: 7.0, y: 2.0 })
.with(Mass(4.5))
.marked::<SimpleMarker<NetworkSync>>()
.build();
// Here we create a system that lets us access the entities to serialize.
struct Serialize;
impl<'a> System<'a> for Serialize {
// This SystemData contains the entity-resource, as well as all components that
// shall be serialized, plus the marker component storage.
type SystemData = (
Entities<'a>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Mass>,
ReadStorage<'a, SimpleMarker<NetworkSync>>,
);
fn run(&mut self, (ents, pos, mass, markers): Self::SystemData) {
// First we need a serializer for the format of choice, in this case the
// `.ron`-format.
let mut buf = Vec::new();
let mut config = PrettyConfig::default();
config.struct_names = true;
let mut ser =
ron::ser::Serializer::with_options(&mut buf, Some(config), Default::default())
.unwrap();
// For serialization we use the
// [`SerializeComponents`](struct.SerializeComponents.html)-trait's `serialize`
// function. It takes two generic parameters:
// * An unbound type -> `Infallible` (However, the serialize function expects it
// to be bound by the `Display`-trait)
// * A type implementing the `Marker`-trait ->
// [SimpleMarker](struct.SimpleMarker.html) (a convenient, predefined marker)
//
// The first parameter resembles the `.join()` syntax from other specs-systems,
// every component that should be serialized has to be put inside a tuple.
//
// The second and third parameters are just the entity-storage and
// marker-storage, which get `.join()`ed internally.
//
// Lastly, we provide a mutable reference to the serializer of choice, which has
// to have the `serde::ser::Serializer`-trait implemented.
SerializeComponents::<Infallible, SimpleMarker<NetworkSync>>::serialize(
&(&pos, &mass),
&ents,
&markers,
&mut ser,
)
.unwrap_or_else(|e| eprintln!("Error: {}", e));
// TODO: Specs should return an error which combines serialization
// and component errors.
// At this point, `ser` could be used to write its contents to a file, which is
// not done here. Instead we print the content of this pseudo-file.
println!("{}", String::from_utf8(buf).expect("Ron should be utf-8"));
}
}
// Running the system results in a print to the standard output channel, in
// `.ron`-format, showing how the serialized dummy entities look like.
Serialize.run_now(&world);
// -----------------
// Just like the previous Serialize-system, we write a Deserialize-system.
struct Deserialize;
impl<'a> System<'a> for Deserialize {
// This requires all the component storages our serialized entities have,
// mutably, plus a `MarkerAllocator` resource to write the deserialized
// ids into, so that we can later serialize again.
type SystemData = (
Entities<'a>,
Write<'a, SimpleMarkerAllocator<NetworkSync>>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Mass>,
WriteStorage<'a, SimpleMarker<NetworkSync>>,
);
fn run(&mut self, (ent, mut alloc, pos, mass, mut markers): Self::SystemData) {
// The `const ENTITIES: &str` at the top of this file was formatted according to
// the `.ron`-specs, therefore we need a `.ron`-deserializer.
// Others can be used, as long as they implement the
// `serde::de::Deserializer`-trait.
use ron::de::Deserializer;
// Typical file operations are omitted in this example, since we do not have a
// seperate file, but a `const &str`. We use a convencience function
// of the `ron`-crate: `from_str`, to convert our data form the top of the file.
if let Ok(mut de) = Deserializer::from_str(ENTITIES) {
// Again, we need to pass in a type implementing the `Display`-trait,
// as well as a type implementing the `Marker`-trait.
// However, from the function parameter `&mut markers`, which refers to the
// `SimpleMarker`-storage, the necessary type of marker can be
// inferred, hence the `, _>´.
DeserializeComponents::<Combined, _>::deserialize(
&mut (pos, mass),
&ent,
&mut markers,
&mut alloc,
&mut de,
)
.unwrap_or_else(|e| eprintln!("Error: {}", e));
}
}
}
// If we run this system now, the `ENTITIES: &str` is going to be deserialized,
// and two entities are created.
Deserialize.run_now(&world);
// Printing the `Pos`-component storage entries to show the result of
// deserializing.
println!(
"{:#?}",
(&world.read_storage::<Pos>()).join().collect::<Vec<_>>()
);
}