|
|
@@ -12,7 +12,6 @@
|
|
|
* [Pay per use](#pay-per-use)
|
|
|
* [All or nothing](#all-or-nothing)
|
|
|
* [Vademecum](#vademecum)
|
|
|
-* [Storage](#storage)
|
|
|
* [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
|
|
|
* [Observe changes](#observe-changes)
|
|
|
* [Listeners disconnection](#listeners-disconnection)
|
|
|
@@ -29,18 +28,20 @@
|
|
|
* [Organizer](#organizer)
|
|
|
* [Context variables](#context-variables)
|
|
|
* [Aliased properties](#aliased-properties)
|
|
|
- * [Component traits](#component-traits)
|
|
|
- * [Pointer stability](#pointer-stability)
|
|
|
- * [In-place delete](#in-place-delete)
|
|
|
- * [Hierarchies and the like](#hierarchies-and-the-like)
|
|
|
- * [Meet the runtime](#meet-the-runtime)
|
|
|
- * [A base class to rule them all](#a-base-class-to-rule-them-all)
|
|
|
- * [Beam me up, registry](#beam-me-up-registry)
|
|
|
* [Snapshot: complete vs continuous](#snapshot-complete-vs-continuous)
|
|
|
* [Snapshot loader](#snapshot-loader)
|
|
|
* [Continuous loader](#continuous-loader)
|
|
|
* [Archives](#archives)
|
|
|
* [One example to rule them all](#one-example-to-rule-them-all)
|
|
|
+* [Storage](#storage)
|
|
|
+ * [Component traits](#component-traits)
|
|
|
+ * [Empty type optimization](#empty-type-optimization)
|
|
|
+ * [Pointer stability](#pointer-stability)
|
|
|
+ * [In-place delete](#in-place-delete)
|
|
|
+ * [Hierarchies and the like](#hierarchies-and-the-like)
|
|
|
+* [Meet the runtime](#meet-the-runtime)
|
|
|
+ * [A base class to rule them all](#a-base-class-to-rule-them-all)
|
|
|
+ * [Beam me up, registry](#beam-me-up-registry)
|
|
|
* [Views and Groups](#views-and-groups)
|
|
|
* [Views](#views)
|
|
|
* [View pack](#view-pack)
|
|
|
@@ -54,7 +55,6 @@
|
|
|
* [Give me everything](#give-me-everything)
|
|
|
* [What is allowed and what is not](#what-is-allowed-and-what-is-not)
|
|
|
* [More performance, more constraints](#more-performance-more-constraints)
|
|
|
-* [Empty type optimization](#empty-type-optimization)
|
|
|
* [Multithreading](#multithreading)
|
|
|
* [Iterators](#iterators)
|
|
|
* [Const registry](#const-registry)
|
|
|
@@ -142,20 +142,9 @@ of the `EnTT` library.<br/>
|
|
|
This module is likely larger than what is described below. For more details,
|
|
|
please refer to the inline documentation.
|
|
|
|
|
|
-# Storage
|
|
|
-
|
|
|
-Pools of components are a sort of _specialized version_ of a sparse set. Each
|
|
|
-pool contains all the instances of a single component type and all the entities
|
|
|
-to which it's assigned.<br/>
|
|
|
-Sparse arrays are _paged_ to avoid wasting memory. Packed arrays of components
|
|
|
-are also paged to have pointer stability upon additions. Packed arrays of
|
|
|
-entities are not instead.<br/>
|
|
|
-All pools rearranges their items in order to keep the internal arrays tightly
|
|
|
-packed and maximize performance, unless pointer stability is enabled.
|
|
|
-
|
|
|
# The Registry, the Entity and the Component
|
|
|
|
|
|
-A registry stores and manages entities (or better, identifiers) and pools.<br/>
|
|
|
+A registry stores and manages entities (or _identifiers_) and components.<br/>
|
|
|
The class template `basic_registry` lets users decide what the preferred type to
|
|
|
represent an entity is. Because `std::uint32_t` is large enough for almost any
|
|
|
case, there also exists the enum class `entt::entity` that _wraps_ it and the
|
|
|
@@ -965,9 +954,223 @@ const my_type &var = registry.ctx().get<const my_type>();
|
|
|
Aliased properties are erased as it happens with any other variable. Similarly,
|
|
|
it's also possible to assign them a _name_.
|
|
|
|
|
|
+## Snapshot: complete vs continuous
|
|
|
+
|
|
|
+This module comes with bare minimum support to serialization.<br/>
|
|
|
+It doesn't convert components to bytes directly, there wasn't the need of
|
|
|
+another tool for serialization out there. Instead, it accepts an opaque object
|
|
|
+with a suitable interface (namely an _archive_) to serialize its internal data
|
|
|
+structures and restore them later. The way types and instances are converted to
|
|
|
+a bunch of bytes is completely in charge to the archive and thus to final users.
|
|
|
+
|
|
|
+The goal of the serialization part is to allow users to make both a dump of the
|
|
|
+entire registry or a narrower snapshot, that is to select only the components in
|
|
|
+which they are interested.<br/>
|
|
|
+Intuitively, the use cases are different. As an example, the first approach is
|
|
|
+suitable for local save/restore functionalities while the latter is suitable for
|
|
|
+creating client-server applications and for transferring somehow parts of the
|
|
|
+representation side to side.
|
|
|
+
|
|
|
+To take a snapshot of a registry, use the `snapshot` class:
|
|
|
+
|
|
|
+```cpp
|
|
|
+output_archive output;
|
|
|
+
|
|
|
+entt::snapshot{registry}
|
|
|
+ .entities(output)
|
|
|
+ .component<a_component, another_component>(output);
|
|
|
+```
|
|
|
+
|
|
|
+It isn't necessary to invoke all functions each and every time. What functions
|
|
|
+to use in which case mostly depends on the goal.
|
|
|
+
|
|
|
+The `entities` member function makes the snapshot serialize all entities (both
|
|
|
+those still alive and those released) along with their versions.<br/>
|
|
|
+On the other hand, the `component` member function template is meant to store
|
|
|
+aside components.<br/>
|
|
|
+There exists also another version of the `component` member function that
|
|
|
+accepts a range of entities to serialize. This version is a bit slower than the
|
|
|
+other one, mainly because it iterates the range of entities more than once for
|
|
|
+internal purposes. However, it can be used to filter out those entities that
|
|
|
+shouldn't be serialized for some reasons:
|
|
|
+
|
|
|
+```cpp
|
|
|
+const auto view = registry.view<serialize>();
|
|
|
+output_archive output;
|
|
|
+
|
|
|
+entt::snapshot{registry}.component<a_component, another_component>(output, view.begin(), view.end());
|
|
|
+```
|
|
|
+
|
|
|
+Note that `component` stores items along with entities. It means that it works
|
|
|
+properly without a call to the `entities` member function.
|
|
|
+
|
|
|
+Once a snapshot is created, there exist mainly two _ways_ to load it: as a whole
|
|
|
+and in a kind of _continuous mode_.<br/>
|
|
|
+The following sections describe both loaders and archives in details.
|
|
|
+
|
|
|
+### Snapshot loader
|
|
|
+
|
|
|
+A snapshot loader requires that the destination registry be empty. It loads all
|
|
|
+the data at once while keeping intact the identifiers that the entities
|
|
|
+originally had:
|
|
|
+
|
|
|
+```cpp
|
|
|
+input_archive input;
|
|
|
+
|
|
|
+entt::snapshot_loader{registry}
|
|
|
+ .entities(input)
|
|
|
+ .component<a_component, another_component>(input)
|
|
|
+ .orphans();
|
|
|
+```
|
|
|
+
|
|
|
+It isn't necessary to invoke all functions each and every time. What functions
|
|
|
+to use in which case mostly depends on the goal.<br/>
|
|
|
+For obvious reasons, what is important is that the data are restored in exactly
|
|
|
+the same order in which they were serialized.
|
|
|
+
|
|
|
+The `entities` member function restores the sets of entities and the versions
|
|
|
+that they originally had at the source.<br/>
|
|
|
+The `component` member function restores all and only the components specified
|
|
|
+and assigns them to the right entities. The template parameter list must be the
|
|
|
+same used during the serialization.<br/>
|
|
|
+The `orphans` member function releases the entities that have no components, if
|
|
|
+any.
|
|
|
+
|
|
|
+### Continuous loader
|
|
|
+
|
|
|
+A continuous loader is designed to load data from a source registry to a
|
|
|
+(possibly) non-empty destination. The loader accommodates in a registry more
|
|
|
+than one snapshot in a sort of _continuous loading_ that updates the destination
|
|
|
+one step at a time.<br/>
|
|
|
+Identifiers that entities originally had are not transferred to the target.
|
|
|
+Instead, the loader maps remote identifiers to local ones while restoring a
|
|
|
+snapshot. Because of that, this kind of loader offers a way to update
|
|
|
+automatically identifiers that are part of components (as an example, as data
|
|
|
+members or gathered in a container).<br/>
|
|
|
+Another difference with the snapshot loader is that the continuous loader has an
|
|
|
+internal state that must persist over time. Therefore, there is no reason to
|
|
|
+limit its lifetime to that of a temporary object:
|
|
|
+
|
|
|
+```cpp
|
|
|
+entt::continuous_loader loader{registry};
|
|
|
+input_archive input;
|
|
|
+
|
|
|
+loader.entities(input)
|
|
|
+ .component<a_component, another_component, dirty_component>(input, &dirty_component::parent, &dirty_component::child)
|
|
|
+ .orphans()
|
|
|
+ .shrink();
|
|
|
+```
|
|
|
+
|
|
|
+It isn't necessary to invoke all functions each and every time. What functions
|
|
|
+to use in which case mostly depends on the goal.<br/>
|
|
|
+For obvious reasons, what is important is that the data are restored in exactly
|
|
|
+the same order in which they were serialized.
|
|
|
+
|
|
|
+The `entities` member function restores groups of entities and maps each entity
|
|
|
+to a local counterpart when required. For each remote entity identifier not yet
|
|
|
+registered by the loader, a local identifier is created so as to keep the local
|
|
|
+entity in sync with the remote one.<br/>
|
|
|
+The `component` member function restores all and only the components specified
|
|
|
+and assigns them to the right entities. In case the component contains entities
|
|
|
+itself (either as data members of type `entt::entity` or in a container), the
|
|
|
+loader can update them automatically. To do that, it's enough to specify the
|
|
|
+data members to update as shown in the example.<br/>
|
|
|
+The `orphans` member function releases the entities that have no components
|
|
|
+after a restore.<br/>
|
|
|
+Finally, `shrink` helps to purge local entities that no longer have a remote
|
|
|
+conterpart. Users should invoke this member function after restoring each
|
|
|
+snapshot, unless they know exactly what they are doing.
|
|
|
+
|
|
|
+### Archives
|
|
|
+
|
|
|
+Archives must publicly expose a predefined set of member functions. The API is
|
|
|
+straightforward and consists only of a group of function call operators that
|
|
|
+are invoked by the snapshot class and the loaders.
|
|
|
+
|
|
|
+In particular:
|
|
|
+
|
|
|
+* An output archive (the one used when creating a snapshot) exposes a function
|
|
|
+ call operator with the following signature to store entities:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(entt::entity);
|
|
|
+ ```
|
|
|
+
|
|
|
+ Where `entt::entity` is the type of the entities used by the registry.<br/>
|
|
|
+ Note that all member functions of the snapshot class also make an initial call
|
|
|
+ to store aside the _size_ of the set they are going to store. In this case,
|
|
|
+ the expected function type for the function call operator is:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(std::underlying_type_t<entt::entity>);
|
|
|
+ ```
|
|
|
+
|
|
|
+ In addition, an archive accepts a pair of entity and component for each type
|
|
|
+ to serialize. Therefore, given a type `T`, the archive offers a function call
|
|
|
+ operator with the following signature:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(entt::entity, const T &);
|
|
|
+ ```
|
|
|
+
|
|
|
+ The output archive can freely decide how to serialize the data. The registry
|
|
|
+ isn't affected at all by the decision.
|
|
|
+
|
|
|
+* An input archive (the one used when restoring a snapshot) exposes a function
|
|
|
+ call operator with the following signature to load entities:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(entt::entity &);
|
|
|
+ ```
|
|
|
+
|
|
|
+ Where `entt::entity` is the type of the entities used by the registry. Each
|
|
|
+ time the function is invoked, the archive reads the next element from the
|
|
|
+ underlying storage and copies it in the given variable.<br/>
|
|
|
+ All member functions of a loader class also make an initial call to read the
|
|
|
+ _size_ of the set they are going to load. In this case, the expected function
|
|
|
+ type for the function call operator is:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(std::underlying_type_t<entt::entity> &);
|
|
|
+ ```
|
|
|
+
|
|
|
+ In addition, the archive accepts a pair of references to an entity and its
|
|
|
+ component for each type to restore. Therefore, given a type `T`, the archive
|
|
|
+ contains a function call operator with the following signature:
|
|
|
+
|
|
|
+ ```cpp
|
|
|
+ void operator()(entt::entity &, T &);
|
|
|
+ ```
|
|
|
+
|
|
|
+ Every time this operator is invoked, the archive reads the next elements from
|
|
|
+ the underlying storage and copies them in the given variables.
|
|
|
+
|
|
|
+### One example to rule them all
|
|
|
+
|
|
|
+`EnTT` comes with some examples (actually some tests) that show how to integrate
|
|
|
+a well known library for serialization as an archive. It uses
|
|
|
+[`Cereal C++`](https://uscilab.github.io/cereal/) under the hood, mainly
|
|
|
+because I wanted to learn how it works at the time I was writing the code.
|
|
|
+
|
|
|
+The code **isn't** production-ready and it isn't neither the only nor (probably)
|
|
|
+the best way to do it. However, feel free to use it at your own risk.<br/>
|
|
|
+The basic idea is to store everything in a group of queues in memory, then bring
|
|
|
+everything back to the registry with different loaders.
|
|
|
+
|
|
|
+# Storage
|
|
|
+
|
|
|
+Pools of components are _specialized versions_ of the sparse set class. Each
|
|
|
+pool contains all the instances of a single component type and all the entities
|
|
|
+to which it's assigned.<br/>
|
|
|
+Sparse arrays are _paged_ to avoid wasting memory. Packed arrays of components
|
|
|
+are also paged to have pointer stability upon additions. Packed arrays of
|
|
|
+entities are not instead.<br/>
|
|
|
+All pools rearranges their items in order to keep the internal arrays tightly
|
|
|
+packed and maximize performance, unless pointer stability is enabled.
|
|
|
+
|
|
|
## Component traits
|
|
|
|
|
|
-In `EnTT`, almost everything is customizable. Components are no exception.<br/>
|
|
|
+In `EnTT`, almost everything is customizable. Pools are no exception.<br/>
|
|
|
In this case, the _standardized_ way to access all component properties is the
|
|
|
`component_traits` class.
|
|
|
|
|
|
@@ -997,6 +1200,30 @@ The `component_traits` class template takes care of _extracting_ the properties
|
|
|
from the supplied type.<br/>
|
|
|
Plus, it's _sfinae-friendly_ and also supports feature-based specializations.
|
|
|
|
|
|
+## Empty type optimization
|
|
|
+
|
|
|
+An empty type `T` is such that `std::is_empty_v<T>` returns true. They also are
|
|
|
+the same types for which _empty base optimization_ (EBO) is possible.<br/>
|
|
|
+`EnTT` handles these types in a special way, optimizing both in terms of
|
|
|
+performance and memory usage. However, this also has consequences that are worth
|
|
|
+mentioning.
|
|
|
+
|
|
|
+When an empty type is detected, it's not instantiated by default. Therefore,
|
|
|
+only the entities to which it's assigned are made available. There doesn't exist
|
|
|
+a way to _get_ empty types from a storage or a registry. Views and groups never
|
|
|
+return their instances too (for example, during a call to `each`).<br/>
|
|
|
+On the other hand, iterations are faster because only the entities to which the
|
|
|
+type is assigned are considered. Moreover, less memory is used, mainly because
|
|
|
+there doesn't exist any instance of the component, no matter how many entities
|
|
|
+it is assigned to.
|
|
|
+
|
|
|
+More in general, none of the feature offered by the library is affected, but for
|
|
|
+the ones that require to return actual instances.<br/>
|
|
|
+This optimization is disabled by defining the `ENTT_NO_ETO` macro. In this case,
|
|
|
+empty types are treated like all other types. Setting a page size at component
|
|
|
+level via the `component_traits` class template is another way to disable this
|
|
|
+optimization selectively rather than globally.
|
|
|
+
|
|
|
## Pointer stability
|
|
|
|
|
|
The ability to achieve pointer stability for one, several or all components is a
|
|
|
@@ -1071,7 +1298,7 @@ time and therefore fallback into adjacent positions, thus favoring locality even
|
|
|
on random accesses. Locality that isn't sacrificed over time given the stability
|
|
|
of storage positions, with undoubted performance advantages.
|
|
|
|
|
|
-## Meet the runtime
|
|
|
+# Meet the runtime
|
|
|
|
|
|
`EnTT` takes advantage of what the language offers at compile-time. However,
|
|
|
this can have its downsides (well known to those familiar with type erasure
|
|
|
@@ -1079,7 +1306,7 @@ techniques).<br/>
|
|
|
To fill the gap, the library also provides a bunch of utilities and feature that
|
|
|
are very useful to handle types and pools at runtime.
|
|
|
|
|
|
-### A base class to rule them all
|
|
|
+## A base class to rule them all
|
|
|
|
|
|
Storage classes are fully self-contained types. They are _extended_ via mixins
|
|
|
to add more functionalities (generic or type specific). In addition, they offer
|
|
|
@@ -1146,7 +1373,7 @@ This is particularly useful to clone entities in an opaque way. In addition, the
|
|
|
decoupling of features allows for filtering or use of different copying policies
|
|
|
depending on the type.
|
|
|
|
|
|
-### Beam me up, registry
|
|
|
+## Beam me up, registry
|
|
|
|
|
|
`EnTT` allows the user to assign a _name_ (or rather, a numeric identifier) to a
|
|
|
type and then create multiple pools of the same type:
|
|
|
@@ -1196,209 +1423,6 @@ The possibility of direct use of storage combined with the freedom of being able
|
|
|
to create and use more than one of the same type opens the door to the use of
|
|
|
`EnTT` _at runtime_, which was previously quite limited.
|
|
|
|
|
|
-## Snapshot: complete vs continuous
|
|
|
-
|
|
|
-This module comes with bare minimum support to serialization.<br/>
|
|
|
-It doesn't convert components to bytes directly, there wasn't the need of
|
|
|
-another tool for serialization out there. Instead, it accepts an opaque object
|
|
|
-with a suitable interface (namely an _archive_) to serialize its internal data
|
|
|
-structures and restore them later. The way types and instances are converted to
|
|
|
-a bunch of bytes is completely in charge to the archive and thus to final users.
|
|
|
-
|
|
|
-The goal of the serialization part is to allow users to make both a dump of the
|
|
|
-entire registry or a narrower snapshot, that is to select only the components in
|
|
|
-which they are interested.<br/>
|
|
|
-Intuitively, the use cases are different. As an example, the first approach is
|
|
|
-suitable for local save/restore functionalities while the latter is suitable for
|
|
|
-creating client-server applications and for transferring somehow parts of the
|
|
|
-representation side to side.
|
|
|
-
|
|
|
-To take a snapshot of a registry, use the `snapshot` class:
|
|
|
-
|
|
|
-```cpp
|
|
|
-output_archive output;
|
|
|
-
|
|
|
-entt::snapshot{registry}
|
|
|
- .entities(output)
|
|
|
- .component<a_component, another_component>(output);
|
|
|
-```
|
|
|
-
|
|
|
-It isn't necessary to invoke all functions each and every time. What functions
|
|
|
-to use in which case mostly depends on the goal.
|
|
|
-
|
|
|
-The `entities` member function makes the snapshot serialize all entities (both
|
|
|
-those still alive and those released) along with their versions.<br/>
|
|
|
-On the other hand, the `component` member function template is meant to store
|
|
|
-aside components.<br/>
|
|
|
-There exists also another version of the `component` member function that
|
|
|
-accepts a range of entities to serialize. This version is a bit slower than the
|
|
|
-other one, mainly because it iterates the range of entities more than once for
|
|
|
-internal purposes. However, it can be used to filter out those entities that
|
|
|
-shouldn't be serialized for some reasons:
|
|
|
-
|
|
|
-```cpp
|
|
|
-const auto view = registry.view<serialize>();
|
|
|
-output_archive output;
|
|
|
-
|
|
|
-entt::snapshot{registry}.component<a_component, another_component>(output, view.begin(), view.end());
|
|
|
-```
|
|
|
-
|
|
|
-Note that `component` stores items along with entities. It means that it works
|
|
|
-properly without a call to the `entities` member function.
|
|
|
-
|
|
|
-Once a snapshot is created, there exist mainly two _ways_ to load it: as a whole
|
|
|
-and in a kind of _continuous mode_.<br/>
|
|
|
-The following sections describe both loaders and archives in details.
|
|
|
-
|
|
|
-### Snapshot loader
|
|
|
-
|
|
|
-A snapshot loader requires that the destination registry be empty. It loads all
|
|
|
-the data at once while keeping intact the identifiers that the entities
|
|
|
-originally had:
|
|
|
-
|
|
|
-```cpp
|
|
|
-input_archive input;
|
|
|
-
|
|
|
-entt::snapshot_loader{registry}
|
|
|
- .entities(input)
|
|
|
- .component<a_component, another_component>(input)
|
|
|
- .orphans();
|
|
|
-```
|
|
|
-
|
|
|
-It isn't necessary to invoke all functions each and every time. What functions
|
|
|
-to use in which case mostly depends on the goal.<br/>
|
|
|
-For obvious reasons, what is important is that the data are restored in exactly
|
|
|
-the same order in which they were serialized.
|
|
|
-
|
|
|
-The `entities` member function restores the sets of entities and the versions
|
|
|
-that they originally had at the source.<br/>
|
|
|
-The `component` member function restores all and only the components specified
|
|
|
-and assigns them to the right entities. The template parameter list must be the
|
|
|
-same used during the serialization.<br/>
|
|
|
-The `orphans` member function releases the entities that have no components, if
|
|
|
-any.
|
|
|
-
|
|
|
-### Continuous loader
|
|
|
-
|
|
|
-A continuous loader is designed to load data from a source registry to a
|
|
|
-(possibly) non-empty destination. The loader accommodates in a registry more
|
|
|
-than one snapshot in a sort of _continuous loading_ that updates the destination
|
|
|
-one step at a time.<br/>
|
|
|
-Identifiers that entities originally had are not transferred to the target.
|
|
|
-Instead, the loader maps remote identifiers to local ones while restoring a
|
|
|
-snapshot. Because of that, this kind of loader offers a way to update
|
|
|
-automatically identifiers that are part of components (as an example, as data
|
|
|
-members or gathered in a container).<br/>
|
|
|
-Another difference with the snapshot loader is that the continuous loader has an
|
|
|
-internal state that must persist over time. Therefore, there is no reason to
|
|
|
-limit its lifetime to that of a temporary object:
|
|
|
-
|
|
|
-```cpp
|
|
|
-entt::continuous_loader loader{registry};
|
|
|
-input_archive input;
|
|
|
-
|
|
|
-loader.entities(input)
|
|
|
- .component<a_component, another_component, dirty_component>(input, &dirty_component::parent, &dirty_component::child)
|
|
|
- .orphans()
|
|
|
- .shrink();
|
|
|
-```
|
|
|
-
|
|
|
-It isn't necessary to invoke all functions each and every time. What functions
|
|
|
-to use in which case mostly depends on the goal.<br/>
|
|
|
-For obvious reasons, what is important is that the data are restored in exactly
|
|
|
-the same order in which they were serialized.
|
|
|
-
|
|
|
-The `entities` member function restores groups of entities and maps each entity
|
|
|
-to a local counterpart when required. For each remote entity identifier not yet
|
|
|
-registered by the loader, a local identifier is created so as to keep the local
|
|
|
-entity in sync with the remote one.<br/>
|
|
|
-The `component` member function restores all and only the components specified
|
|
|
-and assigns them to the right entities. In case the component contains entities
|
|
|
-itself (either as data members of type `entt::entity` or in a container), the
|
|
|
-loader can update them automatically. To do that, it's enough to specify the
|
|
|
-data members to update as shown in the example.<br/>
|
|
|
-The `orphans` member function releases the entities that have no components
|
|
|
-after a restore.<br/>
|
|
|
-Finally, `shrink` helps to purge local entities that no longer have a remote
|
|
|
-conterpart. Users should invoke this member function after restoring each
|
|
|
-snapshot, unless they know exactly what they are doing.
|
|
|
-
|
|
|
-### Archives
|
|
|
-
|
|
|
-Archives must publicly expose a predefined set of member functions. The API is
|
|
|
-straightforward and consists only of a group of function call operators that
|
|
|
-are invoked by the snapshot class and the loaders.
|
|
|
-
|
|
|
-In particular:
|
|
|
-
|
|
|
-* An output archive (the one used when creating a snapshot) exposes a function
|
|
|
- call operator with the following signature to store entities:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(entt::entity);
|
|
|
- ```
|
|
|
-
|
|
|
- Where `entt::entity` is the type of the entities used by the registry.<br/>
|
|
|
- Note that all member functions of the snapshot class also make an initial call
|
|
|
- to store aside the _size_ of the set they are going to store. In this case,
|
|
|
- the expected function type for the function call operator is:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(std::underlying_type_t<entt::entity>);
|
|
|
- ```
|
|
|
-
|
|
|
- In addition, an archive accepts a pair of entity and component for each type
|
|
|
- to serialize. Therefore, given a type `T`, the archive offers a function call
|
|
|
- operator with the following signature:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(entt::entity, const T &);
|
|
|
- ```
|
|
|
-
|
|
|
- The output archive can freely decide how to serialize the data. The registry
|
|
|
- isn't affected at all by the decision.
|
|
|
-
|
|
|
-* An input archive (the one used when restoring a snapshot) exposes a function
|
|
|
- call operator with the following signature to load entities:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(entt::entity &);
|
|
|
- ```
|
|
|
-
|
|
|
- Where `entt::entity` is the type of the entities used by the registry. Each
|
|
|
- time the function is invoked, the archive reads the next element from the
|
|
|
- underlying storage and copies it in the given variable.<br/>
|
|
|
- All member functions of a loader class also make an initial call to read the
|
|
|
- _size_ of the set they are going to load. In this case, the expected function
|
|
|
- type for the function call operator is:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(std::underlying_type_t<entt::entity> &);
|
|
|
- ```
|
|
|
-
|
|
|
- In addition, the archive accepts a pair of references to an entity and its
|
|
|
- component for each type to restore. Therefore, given a type `T`, the archive
|
|
|
- contains a function call operator with the following signature:
|
|
|
-
|
|
|
- ```cpp
|
|
|
- void operator()(entt::entity &, T &);
|
|
|
- ```
|
|
|
-
|
|
|
- Every time this operator is invoked, the archive reads the next elements from
|
|
|
- the underlying storage and copies them in the given variables.
|
|
|
-
|
|
|
-### One example to rule them all
|
|
|
-
|
|
|
-`EnTT` comes with some examples (actually some tests) that show how to integrate
|
|
|
-a well known library for serialization as an archive. It uses
|
|
|
-[`Cereal C++`](https://uscilab.github.io/cereal/) under the hood, mainly
|
|
|
-because I wanted to learn how it works at the time I was writing the code.
|
|
|
-
|
|
|
-The code **isn't** production-ready and it isn't neither the only nor (probably)
|
|
|
-the best way to do it. However, feel free to use it at your own risk.<br/>
|
|
|
-The basic idea is to store everything in a group of queues in memory, then bring
|
|
|
-everything back to the registry with different loaders.
|
|
|
-
|
|
|
# Views and Groups
|
|
|
|
|
|
Views are a non-intrusive tool for working with entities and components without
|
|
|
@@ -1975,30 +1999,6 @@ data internally to maximize performance. Because of that, full consistency for
|
|
|
owned components is guaranteed only when they are iterated as part of their
|
|
|
groups or as free types with multi type views and groups in general.
|
|
|
|
|
|
-# Empty type optimization
|
|
|
-
|
|
|
-An empty type `T` is such that `std::is_empty_v<T>` returns true. They also are
|
|
|
-the same types for which _empty base optimization_ (EBO) is possible.<br/>
|
|
|
-`EnTT` handles these types in a special way, optimizing both in terms of
|
|
|
-performance and memory usage. However, this also has consequences that are worth
|
|
|
-mentioning.
|
|
|
-
|
|
|
-When an empty type is detected, it's not instantiated by default. Therefore,
|
|
|
-only the entities to which it's assigned are made available. There doesn't exist
|
|
|
-a way to _get_ empty types from a registry. Views and groups never return their
|
|
|
-instances (for example, during a call to `each`).<br/>
|
|
|
-On the other hand, iterations are faster because only the entities to which the
|
|
|
-type is assigned are considered. Moreover, less memory is used, mainly because
|
|
|
-there doesn't exist any instance of the component, no matter how many entities
|
|
|
-it is assigned to.
|
|
|
-
|
|
|
-More in general, none of the feature offered by the library is affected, but for
|
|
|
-the ones that require to return actual instances.<br/>
|
|
|
-This optimization is disabled by defining the `ENTT_NO_ETO` macro. In this case,
|
|
|
-empty types are treated like all other types. Setting a page size at component
|
|
|
-level via the `component_traits` class template is another way to disable this
|
|
|
-optimization selectively rather than globally.
|
|
|
-
|
|
|
# Multithreading
|
|
|
|
|
|
In general, the entire registry isn't thread safe as it is. Thread safety isn't
|