You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
amethyst#50 - Improved chapter 11, using the ConvertSaveload macro rather than writing manual serialization helpers. Much nicer code and easier chapter. Still need to port it to all future chapters (which will take a while).
The new line (`.marked::<SimpleMarker<SerializeMe>>()`) needs to be repeated for all of our spawners in this file. It's worth looking at the source for this chapter; to avoid making a *huge* chapter full of source code, I've omitted the repeated details.
328
328
329
-
## Serializing components that don't contain an Entity
329
+
## The ConvertSaveload derive macro
330
+
331
+
The `Entity` class itself (provided by Specs) isn't directly serializable; it's actually a reference to an identity in a special structure called a "slot map" (basically a really efficient way to store data and keep the locations stable until you delete it, but re-use the space when it becomes available). So, in order to save and load `Entity` classes, it becomes necessary to convert these synthetic identities to unique ID numbers. Fortunately, Specs provides a `derive` macro called `ConvertSaveload` for this purpose. It works for most components, but not for all!
330
332
331
-
It's pretty easy to serialize a type that doesn't have an Entity in it: mark it with `#[derive(Component, Serialize, Deserialize, Clone)]`. So we go through all the simple component types in `components.rs`; for example, here's `Position`:
333
+
It's pretty easy to serialize a type that doesn't have an Entity in it - but *does* have data: mark it with `#[derive(Component, ConvertSaveload, Clone)]`. So we go through all the simple component types in `components.rs`; for example, here's `Position`:
Here is where it gets a little messy. There are no provided `derive` functions for handling serialization of `Entity`, so we have to do it the hard way. The good news is that we're not doing it very often. Here's a helper for `InBackpack`:
345
+
* The structure is a `Component`. You can replace this with writing code specifying Specs storage if you prefer, but the macro is much easier!
346
+
*`ConvertSaveload` is actually adding `Serialize` and `Deserialize`, but with extra conversion for any `Entity` classes it encounters.
347
+
*`Clone` is saying "this structure can be copied in memory from one point to another." This is necessary for the inner-workings of Serde, and also allows you to attach `.clone()` to the end of any reference to a component - and get another, perfect copy of it. In most cases, `clone` is *really* fast (and occasionally the compiler can make it do nothing at all!)
When you have a component with no data, the `ConvertSaveload` macro doesn't work! Fortunately, these don't require any additional conversion - so you can fall back to the default Serde syntax. Here's a non-data ("tag") class:
So we start off by making a "data" class for `InBackpack`, which simply stores the entity at which it points. Then we implement `convert_info` and `convert_from` to satisfy Specs' `ConvertSaveLoad` trait. In `convert_into`, we use the `ids` map to get a saveable ID number for the item, and return an `InBackpackData` using this marker. `convert_from` does the reverse: we get the ID, look up the ID, and return an `InBackpack` method.
376
-
377
-
So that's not *too* bad. If you look at the source, we've done this for all of the types that store `Entity` data - some of which have other data, or multiple `Entity` types.
378
-
379
356
## Actually saving something
380
357
381
358
The code for loading and saving gets large, so we've moved it into `saveload_system.rs`. Then include a `mod saveload_system;` in `main.rs`, and replace the `SaveGame` state with:
1. We start by creating a new component type - `SerializationHelper` that stores a copy of the map (see, we are using the map stuff from above!). It then creates a new entity, and gives it the new component - with a copy of the map (the `clone` command makes a deep copy). This is needed so we don't need to serialize the map separately.
441
418
2. We enter a block to avoid borrow-checker issues.
0 commit comments