Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
skypjack committed Jun 7, 2019
1 parent b3798a4 commit 42d6006
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 86 deletions.
2 changes: 2 additions & 0 deletions src/entt/entity/group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class basic_group;
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
Expand Down Expand Up @@ -433,6 +434,7 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> {
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
Expand Down
218 changes: 209 additions & 9 deletions src/entt/entity/observer.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef ENTT_ENTITY_REACTIVE_HPP
#define ENTT_ENTITY_REACTIVE_HPP
#ifndef ENTT_ENTITY_OBSERVER_HPP
#define ENTT_ENTITY_OBSERVER_HPP


#include <cstddef>
Expand All @@ -17,39 +17,139 @@
namespace entt {


/**
* @brief Matcher.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
struct matcher;


/**
* @brief Observing matcher.
*
* An observing matcher contains the list of types for which changes should be
* detected.<br/>
* Because of the rules of the language, not all changes can be easily detected.
* In order to avoid nasty solutions that could affect performance to an extent,
* the matcher listens only to the `on_replace` signals emitted by a registry
* and is therefore triggered whenever a component is explicitly replaced.
*
* @tparam AnyOf Types of components for which changes should be detected.
*/
template<typename... AnyOf>
struct matcher<type_list<AnyOf...>> {};


/**
* @brief Grouping matcher.
*
* A grouping matcher describes the group to track in terms of accepted and
* excluded types.<br/>
* This kind of matcher is triggered whenever an entity _enters_ the desired
* group because of the components it is assigned.
*
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
*/
template<typename... AllOf, typename... NoneOf>
struct matcher<type_list<AllOf...>, type_list<NoneOf...>> {};


/**
* @brief Collector.
*
* Primary template isn't defined on purpose. All the specializations give a
* compile-time error, but for a few reasonable cases.
*/
template<typename...>
struct basic_collector;


/**
* @brief Collector.
*
* A collector contains a set of rules (literally, matchers) to use to track
* entities.<br/>
* Its main purpose is to generate a descriptor that allows an observer to know
* how to connect to a registry.
*
* @tparam AnyOf Types of components for which changes should be detected.
* @tparam Matcher Types of grouping matchers.
*/
template<typename... AnyOf, typename... Matcher>
struct basic_collector<matcher<type_list<AnyOf...>>, Matcher...> {
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The extended collector.
*/
template<typename... AllOf, typename... NoneOf>
static constexpr auto group(exclude_t<NoneOf...> = {}) {
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
return basic_collector<matcher<type_list<AnyOf...>>, Matcher..., matcher<type_list<AllOf...>, type_list<NoneOf...>>>{};
}

/**
* @brief Adds an observing matcher to the collector.
* @tparam Type Types of components for which changes should be detected.
* @return The extended collector.
*/
template<typename... Type>
static constexpr auto replace() {
static constexpr auto replace() ENTT_NOEXCEPT {
return basic_collector<matcher<type_list<AnyOf..., Type...>>, Matcher...>{};
}
};


/*! @brief Variable template used to ease the definition of collectors. */
constexpr basic_collector<matcher<type_list<>>> collector{};


/**
* @brief Observer.
*
* An observer returns all the entities and only the entities that fit the
* requirements of at least one matcher. Moreover, it's guaranteed that the
* entity list is tightly packed in memory for fast iterations.<br/>
* In general, observers don't stay true to the order of any set of components.
*
* Observers work mainly with two types of matchers, provided through a
* collector:
*
* * Observing matcher: an observer will return at least all the living entities
* for which one or more of the given components have been explicitly
* replaced and not yet destroyed.
* * Grouping matcher: an observer will return at least all the living entities
* that would have entered the given group if it existed and that would have
* not yet left it.
*
* If an entity respects the requirements of multiple matchers, it will be
* returned once and only once by the observer in any case.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
* behavior.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome the one of the
* registry to which it is connected. However, the observer must be disconnected
* from the registry before being destroyed to avoid crashes due to dangling
* pointers.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
class basic_observer {
using traits_type = entt_traits<Entity>;
Expand All @@ -76,10 +176,14 @@ class basic_observer {
}

template<std::size_t... Index>
static void connect(basic_observer &obs, basic_registry<Entity> &reg, std::index_sequence<Index...> = std::make_index_sequence<sizeof...(AnyOf)>{}) {
static void connect(basic_observer &obs, basic_registry<Entity> &reg, std::index_sequence<Index...>) {
(reg.template on_replace<AnyOf>().template connect<&maybe_valid_if<Index>>(&obs), ...);
(reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(&obs), ...);
}

static void connect(basic_observer &obs, basic_registry<Entity> &reg) {
connect(obs, reg, std::make_index_sequence<sizeof...(AnyOf)>{});
}
};

template<typename... AllOf, typename... NoneOf>
Expand Down Expand Up @@ -126,6 +230,36 @@ class basic_observer {
/*! @brief Input iterator type. */
using iterator_type = typename sparse_set<Entity>::iterator_type;

/*! @brief Default constructor. */
basic_observer() ENTT_NOEXCEPT
: target{}, release{}, view{}
{}

/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
/*! @brief Default move constructor, deleted on purpose. */
basic_observer(basic_observer &&) = delete;

/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(const basic_observer &) = delete;

/**
* @brief Default move assignment operator, deleted on purpose.
* @return This observer.
*/
basic_observer & operator=(basic_observer &&) = delete;

/**
* @brief TODO
*
* TODO
*
* @tparam Matcher TODO
* @param reg TODO
*/
template<typename... Matcher>
basic_observer(basic_registry<entity_type> &reg, basic_collector<Matcher...>) ENTT_NOEXCEPT
: target{&reg},
Expand All @@ -135,31 +269,97 @@ class basic_observer {
(matcher_handler<Matcher>::connect(*this, reg), ...);
}

~basic_observer() {
release(*target, *this);
/*! @brief TODO */
~basic_observer() = default;

/**
* @brief TODO
*
* TODO
*
* @tparam Matcher TODO
* @param reg TODO
*/
template<typename... Matcher>
void connect(basic_registry<entity_type> &reg, basic_collector<Matcher...>) {
target = &reg;
release = &basic_observer::disconnect<&matcher_handler<Matcher>::disconnect...>;
(matcher_handler<Matcher>::connect(*this, reg), ...);
view.reset();
}

/**
* @brief TODO
*
* TODO
*/
void disconnect() {
if(release) {
release(*target, *this);
release = nullptr;
}
}

/**
* @brief Returns the number of elements in an observer.
* @return Number of elements.
*/
size_type size() const ENTT_NOEXCEPT {
return view.size();
}

/**
* @brief Checks whether an observer is empty.
* @return True if the observer is empty, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
return view.empty();
}

/**
* @brief Direct access to the list of entities of the observer.
*
* The returned pointer is such that range `[data(), data() + size()]` is
* always a valid range, even if the container is empty.
*
* @note
* There are no guarantees on the order of the entities. Use `begin` and
* `end` if you want to iterate the observer in the expected order.
*
* @return A pointer to the array of entities.
*/
const entity_type * data() const ENTT_NOEXCEPT {
return view.data();
}

/**
* @brief Returns an iterator to the first entity of the observer.
*
* The returned iterator points to the first entity of the observer. If the
* container is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity of the observer.
*/
iterator_type begin() const ENTT_NOEXCEPT {
return view.sparse_set<entity_type>::begin();
}

/**
* @brief Returns an iterator that is past the last entity of the observer.
*
* The returned iterator points to the entity following the last entity of
* the observer. Attempting to dereference the returned iterator results in
* undefined behavior.
*
* @return An iterator to the entity following the last entity of the
* observer.
*/
iterator_type end() const ENTT_NOEXCEPT {
return view.sparse_set<entity_type>::end();
}

void reset() {
/*! @brief Resets the underlying container. */
void clear() {
return view.reset();
}

Expand All @@ -173,4 +373,4 @@ class basic_observer {
}


#endif // ENTT_ENTITY_REACTIVE_HPP
#endif // ENTT_ENTITY_OBSERVER_HPP
1 change: 1 addition & 0 deletions src/entt/entity/runtime_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace entt {
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
Expand Down
2 changes: 2 additions & 0 deletions src/entt/entity/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace entt {
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators and using them results in undefined
Expand Down Expand Up @@ -518,6 +519,7 @@ class basic_view {
* * New instances of the given component are created and assigned to entities.
* * The entity currently pointed is modified (as an example, the given
* component is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pool of the given component in any way
* invalidates all the iterators and using them results in undefined behavior.
Expand Down
Loading

0 comments on commit 42d6006

Please sign in to comment.