Custom Component Serialisation

Using the AETHER_SERDE_DERIVE_TRIVIALmacro is useful for serializing simple components, but insufficient for more complex components that may contain dynamically allocated data structures or members that cannot be byte-copied. Simulate contains serialization functionality that lives in the namespace aether::serde.
For serializing user-defined types, it is possible to add a single function, serde_visit that aether::serde will call to both serialize and deserialize a value. The sd parameter may be a serializer or deserializer and the &operator is used to visit each component. serde_visit may either be defined as a member function, or outside the type:
struct component {
int a;
float b;
// Member function form
template<typename SD>
void serde_visit(SD &sd) {
sd & a & b;
// Non-member function form
template<typename SD>
void serde_visit(SD &sd, component &c) {
sd & c.a & c.b;
This works if the serialization and deserialization operations are inverses of each other, but insufficient if the decision about what data to send and receive is only known at run-time. In this case, the definition of serde_save for serializing and serde_load for deserializing are needed. Like serde_visit, they have both member and non-member forms. Let’s look at how to implement serialization for C++'s std::optional:
template<typename SD, typename T>
void serde_save(SD &sd, std::optional<T> &opt) {
const bool present = opt.has_value();
serde_save(sd, present);
if (present) {
serde_save(sd, opt.value());
In this case, a bool is first serialized to indicate if the value is present, then serialize the value. Similarly, for deserializing, we only attempt to deserialize a value once we know the optional was non-empty.
template<typename SD, typename T>
void serde_load(SD &sd, std::optional<T> &opt) {
bool present;
serde_load(sd, present);
if (present) {
T value;
serde_load(sd, value);
Fortunately, aether::serde already provides implementations for std::optional and std::vector.

Custom Stateful Serialization

For even more advanced serialization, defining a custom serialization context may be necessary. This is a struct which can hold state related to the serialization of multiple entities. For example to serialize multiple entities at once in a handover step. This is part of how the PhysX integration works, relying on PhysX' own serialization method, batching entities to be sent to the same process.