2016-03-04 13:59:43 +00:00
|
|
|
|
2017-09-20 08:16:26 +00:00
|
|
|
// This work derives from Vittorio Romeo's code used for cppcon 2015 licensed
|
|
|
|
// under the Academic Free License.
|
2016-03-15 10:29:13 +00:00
|
|
|
// His code is available here: https://github.com/SuperV1234/cppcon2015
|
|
|
|
|
|
|
|
|
2016-03-04 13:59:43 +00:00
|
|
|
#ifndef EC_MANAGER_HPP
|
|
|
|
#define EC_MANAGER_HPP
|
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
#define EC_INIT_ENTITIES_SIZE 256
|
|
|
|
#define EC_GROW_SIZE_AMOUNT 256
|
2016-03-05 14:33:24 +00:00
|
|
|
|
2021-09-07 09:28:44 +00:00
|
|
|
#include <array>
|
2016-03-05 14:33:24 +00:00
|
|
|
#include <cstddef>
|
2016-04-20 13:18:25 +00:00
|
|
|
#include <vector>
|
2019-11-06 06:47:16 +00:00
|
|
|
#include <deque>
|
2016-03-05 14:33:24 +00:00
|
|
|
#include <tuple>
|
2016-03-13 09:07:49 +00:00
|
|
|
#include <utility>
|
2016-09-20 11:07:28 +00:00
|
|
|
#include <functional>
|
2016-09-21 12:01:48 +00:00
|
|
|
#include <map>
|
2017-07-13 07:13:37 +00:00
|
|
|
#include <unordered_map>
|
2016-09-21 12:01:48 +00:00
|
|
|
#include <set>
|
|
|
|
#include <unordered_set>
|
2017-07-13 07:13:37 +00:00
|
|
|
#include <algorithm>
|
2017-10-06 03:47:05 +00:00
|
|
|
#include <thread>
|
2017-11-09 12:10:01 +00:00
|
|
|
#include <mutex>
|
2018-09-11 03:16:04 +00:00
|
|
|
#include <type_traits>
|
2016-03-05 14:33:24 +00:00
|
|
|
|
2017-11-10 04:19:50 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
#include <iostream>
|
|
|
|
#endif
|
|
|
|
|
2016-03-05 14:33:24 +00:00
|
|
|
#include "Meta/Combine.hpp"
|
2016-04-20 12:59:47 +00:00
|
|
|
#include "Meta/Matching.hpp"
|
2017-11-15 06:36:04 +00:00
|
|
|
#include "Meta/ForEachWithIndex.hpp"
|
|
|
|
#include "Meta/ForEachDoubleTuple.hpp"
|
2018-05-17 08:05:49 +00:00
|
|
|
#include "Meta/IndexOf.hpp"
|
2016-03-05 14:33:24 +00:00
|
|
|
#include "Bitset.hpp"
|
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
#include "ThreadPool.hpp"
|
|
|
|
|
2016-03-04 13:59:43 +00:00
|
|
|
namespace EC
|
|
|
|
{
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Manages an EntityComponent system.
|
|
|
|
|
2017-09-20 08:16:26 +00:00
|
|
|
EC::Manager must be created with a list of all used Components and all
|
|
|
|
used tags.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
Note that all components must have a default constructor.
|
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
An optional third template parameter may be given, which is the size of
|
2021-09-07 03:04:42 +00:00
|
|
|
the number of threads in the internal ThreadPool, and should be at
|
|
|
|
least 2. If ThreadCount is 1 or less, then the ThreadPool will not be
|
|
|
|
created and it will never be used, even if the "true" parameter is given
|
|
|
|
for functions that enable its usage.
|
2021-09-06 10:52:23 +00:00
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
EC::Manager<TypeList<C0, C1, C2>, TypeList<T0, T1>> manager;
|
|
|
|
\endcode
|
|
|
|
*/
|
2021-09-07 08:03:08 +00:00
|
|
|
template <typename ComponentsList,
|
|
|
|
typename TagsList,
|
|
|
|
unsigned int ThreadCount = 4>
|
2016-03-04 13:59:43 +00:00
|
|
|
struct Manager
|
|
|
|
{
|
|
|
|
public:
|
2018-05-16 05:48:33 +00:00
|
|
|
using Components = ComponentsList;
|
|
|
|
using Tags = TagsList;
|
2016-03-04 13:59:43 +00:00
|
|
|
using Combined = EC::Meta::Combine<ComponentsList, TagsList>;
|
2016-03-05 14:33:24 +00:00
|
|
|
using BitsetType = EC::Bitset<ComponentsList, TagsList>;
|
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
private:
|
2018-09-11 03:16:04 +00:00
|
|
|
using ComponentsTuple = EC::Meta::Morph<ComponentsList, std::tuple<> >;
|
|
|
|
static_assert(std::is_default_constructible<ComponentsTuple>::value,
|
|
|
|
"All components must be default constructible");
|
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename... Types>
|
|
|
|
struct Storage
|
|
|
|
{
|
2019-11-06 06:47:16 +00:00
|
|
|
using type = std::tuple<std::deque<Types>..., std::deque<char> >;
|
2016-03-13 09:07:49 +00:00
|
|
|
};
|
2017-07-13 07:13:37 +00:00
|
|
|
using ComponentsStorage =
|
|
|
|
typename EC::Meta::Morph<ComponentsList, Storage<> >::type;
|
2018-09-11 03:16:04 +00:00
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
// Entity: isAlive, ComponentsTags Info
|
|
|
|
using EntitiesTupleType = std::tuple<bool, BitsetType>;
|
2019-11-06 06:47:16 +00:00
|
|
|
using EntitiesType = std::deque<EntitiesTupleType>;
|
2016-03-13 09:07:49 +00:00
|
|
|
|
|
|
|
EntitiesType entities;
|
|
|
|
ComponentsStorage componentsStorage;
|
|
|
|
std::size_t currentCapacity = 0;
|
|
|
|
std::size_t currentSize = 0;
|
2017-12-01 05:00:49 +00:00
|
|
|
std::unordered_set<std::size_t> deletedSet;
|
2016-03-13 09:07:49 +00:00
|
|
|
|
2021-09-07 02:46:38 +00:00
|
|
|
std::unique_ptr<ThreadPool<ThreadCount> > threadPool;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
public:
|
2021-09-07 09:11:06 +00:00
|
|
|
// section for "temporary" structures {{{
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructZero {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
EntitiesType *entities;
|
|
|
|
const BitsetType *signature;
|
|
|
|
void *userData;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
template <typename Function>
|
|
|
|
struct TPFnDataStructOne {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
EntitiesType *entities;
|
|
|
|
BitsetType *signature;
|
|
|
|
void *userData;
|
|
|
|
Function *fn;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructTwo {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
EntitiesType *entities;
|
|
|
|
void *userData;
|
|
|
|
const std::vector<std::size_t> *matching;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructThree {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
std::vector<std::vector<std::size_t> > *matchingV;
|
|
|
|
const std::vector<BitsetType*> *bitsets;
|
|
|
|
EntitiesType *entities;
|
|
|
|
std::mutex *mutex;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructFour {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
std::vector<std::vector<std::size_t> >*
|
|
|
|
multiMatchingEntities;
|
|
|
|
BitsetType *signatures;
|
|
|
|
std::mutex *mutex;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructFive {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
std::size_t index;
|
|
|
|
Manager *manager;
|
|
|
|
void *userData;
|
|
|
|
std::vector<std::vector<std::size_t> >*
|
|
|
|
multiMatchingEntities;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
struct TPFnDataStructSix {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
std::vector<std::vector<std::size_t> > *
|
|
|
|
multiMatchingEntities;
|
|
|
|
BitsetType *bitsets;
|
|
|
|
std::mutex *mutex;
|
|
|
|
};
|
2021-09-08 05:58:41 +00:00
|
|
|
/// Temporary struct used internally by ThreadPool
|
2021-09-07 09:11:06 +00:00
|
|
|
template <typename Iterable>
|
|
|
|
struct TPFnDataStructSeven {
|
|
|
|
std::array<std::size_t, 2> range;
|
|
|
|
Manager *manager;
|
|
|
|
EntitiesType *entities;
|
|
|
|
Iterable *iterable;
|
|
|
|
void *userData;
|
|
|
|
};
|
|
|
|
// end section for "temporary" structures }}}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Initializes the manager with a default capacity.
|
|
|
|
|
|
|
|
The default capacity is set with macro EC_INIT_ENTITIES_SIZE,
|
|
|
|
and will grow by amounts of EC_GROW_SIZE_AMOUNT when needed.
|
|
|
|
*/
|
2016-03-05 14:33:24 +00:00
|
|
|
Manager()
|
|
|
|
{
|
2016-03-13 09:07:49 +00:00
|
|
|
resize(EC_INIT_ENTITIES_SIZE);
|
2021-09-07 02:46:38 +00:00
|
|
|
if(ThreadCount >= 2) {
|
|
|
|
threadPool = std::make_unique<ThreadPool<ThreadCount> >();
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void resize(std::size_t newCapacity)
|
|
|
|
{
|
|
|
|
if(currentCapacity >= newCapacity)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
|
2019-11-06 06:47:16 +00:00
|
|
|
std::get<std::deque<decltype(t)> >(
|
2017-07-13 07:13:37 +00:00
|
|
|
this->componentsStorage).resize(newCapacity);
|
2016-03-13 09:07:49 +00:00
|
|
|
});
|
2016-03-05 14:33:24 +00:00
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
entities.resize(newCapacity);
|
|
|
|
for(std::size_t i = currentCapacity; i < newCapacity; ++i)
|
2016-03-05 14:33:24 +00:00
|
|
|
{
|
2017-12-01 05:00:49 +00:00
|
|
|
entities[i] = std::make_tuple(false, BitsetType{});
|
2016-03-05 14:33:24 +00:00
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
|
|
|
|
currentCapacity = newCapacity;
|
2016-03-05 14:33:24 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 09:07:49 +00:00
|
|
|
public:
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Adds an entity to the system, returning the ID of the entity.
|
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
Note: The ID of an entity is guaranteed to not change.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
2016-03-05 14:33:24 +00:00
|
|
|
std::size_t addEntity()
|
|
|
|
{
|
2017-12-01 05:00:49 +00:00
|
|
|
if(deletedSet.empty())
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2017-12-01 05:00:49 +00:00
|
|
|
if(currentSize == currentCapacity)
|
|
|
|
{
|
|
|
|
resize(currentCapacity + EC_GROW_SIZE_AMOUNT);
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
std::get<bool>(entities[currentSize]) = true;
|
2016-03-13 09:07:49 +00:00
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
return currentSize++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::size_t id;
|
|
|
|
{
|
|
|
|
auto iter = deletedSet.begin();
|
|
|
|
id = *iter;
|
|
|
|
deletedSet.erase(iter);
|
|
|
|
}
|
|
|
|
std::get<bool>(entities[id]) = true;
|
|
|
|
return id;
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Marks an entity for deletion.
|
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
A deleted Entity's id is stored to be reclaimed later when
|
|
|
|
addEntity is called. Thus calling addEntity may return an id of
|
|
|
|
a previously deleted Entity.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
|
|
|
void deleteEntity(const std::size_t& index)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2017-12-01 05:00:49 +00:00
|
|
|
if(hasEntity(index))
|
|
|
|
{
|
|
|
|
std::get<bool>(entities.at(index)) = false;
|
2017-12-01 10:20:59 +00:00
|
|
|
std::get<BitsetType>(entities.at(index)).reset();
|
2017-12-01 05:00:49 +00:00
|
|
|
deletedSet.insert(index);
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
\brief Checks if the Entity with the given ID is in the system.
|
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
Note that deleted Entities are still considered in the system.
|
|
|
|
Consider using isAlive().
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
|
|
|
bool hasEntity(const std::size_t& index) const
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return index < currentSize;
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
\brief Checks if the Entity is not marked as deleted.
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
Note that invalid Entities (Entities where calls to hasEntity()
|
|
|
|
returns false) will return false.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
|
|
|
bool isAlive(const std::size_t& index) const
|
2016-03-14 09:25:38 +00:00
|
|
|
{
|
2016-03-14 11:01:55 +00:00
|
|
|
return hasEntity(index) && std::get<bool>(entities.at(index));
|
2016-03-14 09:25:38 +00:00
|
|
|
}
|
|
|
|
|
2017-11-03 08:11:20 +00:00
|
|
|
/*!
|
|
|
|
\brief Returns the current size or number of entities in the system.
|
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
Note this function will only count entities where isAlive() returns
|
|
|
|
true.
|
2017-11-03 08:11:20 +00:00
|
|
|
*/
|
|
|
|
std::size_t getCurrentSize() const
|
|
|
|
{
|
2017-12-01 05:00:49 +00:00
|
|
|
return currentSize - deletedSet.size();
|
2017-11-03 08:11:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
\brief Returns the current capacity or number of entities the system
|
|
|
|
can hold.
|
|
|
|
|
|
|
|
Note that when capacity is exceeded, the capacity is increased by
|
|
|
|
EC_GROW_SIZE_AMOUNT.
|
|
|
|
*/
|
|
|
|
std::size_t getCurrentCapacity() const
|
|
|
|
{
|
|
|
|
return currentCapacity;
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Returns a const reference to an Entity's info.
|
|
|
|
|
2017-12-01 05:00:49 +00:00
|
|
|
An Entity's info is a std::tuple with a bool, and a
|
2017-07-13 07:13:37 +00:00
|
|
|
bitset.
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
\n The bool determines if the Entity is alive.
|
|
|
|
\n The bitset shows what Components and Tags belong to the Entity.
|
|
|
|
*/
|
|
|
|
const EntitiesTupleType& getEntityInfo(const std::size_t& index) const
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return entities.at(index);
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
2018-05-18 11:47:34 +00:00
|
|
|
\brief Returns a pointer to a component belonging to the given
|
2017-07-13 07:13:37 +00:00
|
|
|
Entity.
|
|
|
|
|
2018-05-18 11:47:34 +00:00
|
|
|
This function will return a pointer to a Component regardless of
|
|
|
|
whether or not the Entity actually owns the Component. If the Entity
|
2017-07-13 07:13:37 +00:00
|
|
|
doesn't own the Component, changes to the Component will not affect
|
|
|
|
any Entity. It is recommended to use hasComponent() to determine if
|
|
|
|
the Entity actually owns that Component.
|
2018-05-18 11:47:34 +00:00
|
|
|
|
|
|
|
If the given Component is unknown to the Manager, then this function
|
|
|
|
will return a nullptr.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Component>
|
2018-05-17 08:05:49 +00:00
|
|
|
Component* getEntityData(const std::size_t& index)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2018-05-17 08:05:49 +00:00
|
|
|
constexpr auto componentIndex = EC::Meta::IndexOf<
|
|
|
|
Component, Components>::value;
|
|
|
|
if(componentIndex < Components::size)
|
|
|
|
{
|
|
|
|
// Cast required due to compiler thinking that an invalid
|
|
|
|
// Component is needed even though the enclosing if statement
|
|
|
|
// prevents this from ever happening.
|
|
|
|
return (Component*) &std::get<componentIndex>(
|
|
|
|
componentsStorage).at(index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
2018-05-18 11:47:34 +00:00
|
|
|
\brief Returns a pointer to a component belonging to the given
|
2017-07-13 07:13:37 +00:00
|
|
|
Entity.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
Note that this function is the same as getEntityData().
|
|
|
|
|
2018-05-18 11:47:34 +00:00
|
|
|
This function will return a pointer to a Component regardless of
|
|
|
|
whether or not the Entity actually owns the Component. If the Entity
|
2017-07-13 07:13:37 +00:00
|
|
|
doesn't own the Component, changes to the Component will not affect
|
|
|
|
any Entity. It is recommended to use hasComponent() to determine if
|
|
|
|
the Entity actually owns that Component.
|
2018-05-18 11:47:34 +00:00
|
|
|
|
|
|
|
If the given Component is unknown to the Manager, then this function
|
|
|
|
will return a nullptr.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
2016-03-14 09:16:09 +00:00
|
|
|
template <typename Component>
|
2018-05-17 08:05:49 +00:00
|
|
|
Component* getEntityComponent(const std::size_t& index)
|
2016-09-20 11:31:56 +00:00
|
|
|
{
|
|
|
|
return getEntityData<Component>(index);
|
|
|
|
}
|
|
|
|
|
2017-09-20 08:16:26 +00:00
|
|
|
/*!
|
2018-05-18 11:47:34 +00:00
|
|
|
\brief Returns a const pointer to a component belonging to the
|
2017-09-20 08:16:26 +00:00
|
|
|
given Entity.
|
|
|
|
|
2018-05-18 11:47:34 +00:00
|
|
|
This function will return a const pointer to a Component
|
|
|
|
regardless of whether or not the Entity actually owns the Component.
|
2017-09-20 08:16:26 +00:00
|
|
|
If the Entity doesn't own the Component, changes to the Component
|
|
|
|
will not affect any Entity. It is recommended to use hasComponent()
|
|
|
|
to determine if the Entity actually owns that Component.
|
2018-05-18 11:47:34 +00:00
|
|
|
|
|
|
|
If the given Component is unknown to the Manager, then this function
|
|
|
|
will return a nullptr.
|
2017-09-20 08:16:26 +00:00
|
|
|
*/
|
|
|
|
template <typename Component>
|
2018-05-17 08:05:49 +00:00
|
|
|
const Component* getEntityData(const std::size_t& index) const
|
2017-09-20 08:16:26 +00:00
|
|
|
{
|
2018-05-17 08:05:49 +00:00
|
|
|
constexpr auto componentIndex = EC::Meta::IndexOf<
|
|
|
|
Component, Components>::value;
|
|
|
|
if(componentIndex < Components::size)
|
|
|
|
{
|
|
|
|
// Cast required due to compiler thinking that an invalid
|
|
|
|
// Component is needed even though the enclosing if statement
|
|
|
|
// prevents this from ever happening.
|
|
|
|
return (Component*) &std::get<componentIndex>(
|
|
|
|
componentsStorage).at(index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-09-20 08:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2018-05-18 11:47:34 +00:00
|
|
|
\brief Returns a const pointer to a component belonging to the
|
2017-09-20 08:16:26 +00:00
|
|
|
given Entity.
|
|
|
|
|
|
|
|
Note that this function is the same as getEntityData() (const).
|
|
|
|
|
2018-05-18 11:47:34 +00:00
|
|
|
This function will return a const pointer to a Component
|
|
|
|
regardless of whether or not the Entity actually owns the Component.
|
2017-09-20 08:16:26 +00:00
|
|
|
If the Entity doesn't own the Component, changes to the Component
|
|
|
|
will not affect any Entity. It is recommended to use hasComponent()
|
|
|
|
to determine if the Entity actually owns that Component.
|
2018-05-18 11:47:34 +00:00
|
|
|
|
|
|
|
If the given Component is unknown to the Manager, then this function
|
|
|
|
will return a nullptr.
|
2017-09-20 08:16:26 +00:00
|
|
|
*/
|
|
|
|
template <typename Component>
|
2018-05-17 08:05:49 +00:00
|
|
|
const Component* getEntityComponent(const std::size_t& index) const
|
2017-09-20 08:16:26 +00:00
|
|
|
{
|
|
|
|
return getEntityData<Component>(index);
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
2017-07-13 07:13:37 +00:00
|
|
|
\brief Checks whether or not the given Entity has the given
|
|
|
|
Component.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
manager.hasComponent<C0>(entityID);
|
|
|
|
\endcode
|
|
|
|
*/
|
|
|
|
template <typename Component>
|
|
|
|
bool hasComponent(const std::size_t& index) const
|
2016-03-14 09:16:09 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
return std::get<BitsetType>(
|
|
|
|
entities.at(index)).template getComponentBit<Component>();
|
2016-03-14 09:16:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Checks whether or not the given Entity has the given Tag.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
manager.hasTag<T0>(entityID);
|
|
|
|
\endcode
|
|
|
|
*/
|
2016-03-14 09:16:09 +00:00
|
|
|
template <typename Tag>
|
2016-09-20 11:31:56 +00:00
|
|
|
bool hasTag(const std::size_t& index) const
|
2016-03-14 09:16:09 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
return std::get<BitsetType>(
|
|
|
|
entities.at(index)).template getTagBit<Tag>();
|
2016-03-14 09:16:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Adds a component to the given Entity.
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
Additional parameters given to this function will construct the
|
|
|
|
Component with those parameters.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
Note that if the Entity already has the same component, then it
|
|
|
|
will be overwritten by the newly created Component with the given
|
|
|
|
arguments.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2018-05-18 11:47:34 +00:00
|
|
|
If the Entity is not alive or the given Component is not known to
|
|
|
|
the Manager, then nothing will change.
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
struct C0
|
|
|
|
{
|
|
|
|
// constructor is compatible as a default constructor
|
|
|
|
C0(int a = 0, char b = 'b') :
|
|
|
|
a(a), b(b)
|
|
|
|
{}
|
|
|
|
|
|
|
|
int a;
|
|
|
|
char b;
|
|
|
|
}
|
|
|
|
|
|
|
|
manager.addComponent<C0>(entityID, 10, 'd');
|
|
|
|
\endcode
|
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Component, typename... Args>
|
2016-09-20 11:31:56 +00:00
|
|
|
void addComponent(const std::size_t& entityID, Args&&... args)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2018-05-19 07:09:31 +00:00
|
|
|
if(!EC::Meta::Contains<Component, Components>::value
|
|
|
|
|| !isAlive(entityID))
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-06 10:32:30 +00:00
|
|
|
Component component(std::forward<Args>(args)...);
|
2016-03-13 09:07:49 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
std::get<BitsetType>(
|
|
|
|
entities[entityID]
|
|
|
|
).template getComponentBit<Component>() = true;
|
|
|
|
|
2018-05-17 08:05:49 +00:00
|
|
|
constexpr auto index =
|
|
|
|
EC::Meta::IndexOf<Component, Components>::value;
|
|
|
|
|
2019-11-06 06:47:16 +00:00
|
|
|
// Cast required due to compiler thinking that deque<char> at
|
2018-05-17 08:05:49 +00:00
|
|
|
// index = Components::size is being used, even if the previous
|
|
|
|
// if statement will prevent this from ever happening.
|
2019-11-06 06:47:16 +00:00
|
|
|
(*((std::deque<Component>*)(&std::get<index>(
|
2017-07-13 07:13:37 +00:00
|
|
|
componentsStorage
|
2018-05-17 08:05:49 +00:00
|
|
|
))))[entityID] = std::move(component);
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Removes the given Component from the given Entity.
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
If the Entity does not have the Component given, nothing will
|
|
|
|
change.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
manager.removeComponent<C0>(entityID);
|
|
|
|
\endcode
|
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Component>
|
2016-09-20 11:31:56 +00:00
|
|
|
void removeComponent(const std::size_t& entityID)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2018-05-19 07:09:31 +00:00
|
|
|
if(!EC::Meta::Contains<Component, Components>::value
|
|
|
|
|| !isAlive(entityID))
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
std::get<BitsetType>(
|
|
|
|
entities[entityID]
|
|
|
|
).template getComponentBit<Component>() = false;
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Adds the given Tag to the given Entity.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
manager.addTag<T0>(entityID);
|
|
|
|
\endcode
|
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Tag>
|
2016-09-20 11:31:56 +00:00
|
|
|
void addTag(const std::size_t& entityID)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2018-05-19 07:09:31 +00:00
|
|
|
if(!EC::Meta::Contains<Tag, Tags>::value
|
|
|
|
|| !isAlive(entityID))
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
std::get<BitsetType>(
|
|
|
|
entities[entityID]
|
|
|
|
).template getTagBit<Tag>() = true;
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
|
2021-09-07 03:04:42 +00:00
|
|
|
/*!
|
|
|
|
\brief Removes the given Tag from the given Entity.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2021-09-07 03:04:42 +00:00
|
|
|
If the Entity does not have the Tag given, nothing will change.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2021-09-07 03:04:42 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
|
|
|
manager.removeTag<T0>(entityID);
|
|
|
|
\endcode
|
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Tag>
|
2016-09-20 11:31:56 +00:00
|
|
|
void removeTag(const std::size_t& entityID)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2018-05-19 07:09:31 +00:00
|
|
|
if(!EC::Meta::Contains<Tag, Tags>::value
|
|
|
|
|| !isAlive(entityID))
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
std::get<BitsetType>(
|
|
|
|
entities[entityID]
|
|
|
|
).template getTagBit<Tag>() = false;
|
2016-03-05 14:33:24 +00:00
|
|
|
}
|
2016-03-04 13:59:43 +00:00
|
|
|
|
2019-11-06 07:33:12 +00:00
|
|
|
/*!
|
|
|
|
\brief Resets the Manager, removing all entities.
|
|
|
|
|
|
|
|
Some data may persist but will be overwritten when new entities
|
|
|
|
are added. Thus, do not depend on data to persist after a call to
|
|
|
|
reset().
|
|
|
|
*/
|
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
clearForMatchingFunctions();
|
|
|
|
|
|
|
|
currentSize = 0;
|
|
|
|
currentCapacity = 0;
|
|
|
|
deletedSet.clear();
|
|
|
|
resize(EC_INIT_ENTITIES_SIZE);
|
|
|
|
}
|
|
|
|
|
2016-03-04 13:59:43 +00:00
|
|
|
private:
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename... Types>
|
|
|
|
struct ForMatchingSignatureHelper
|
|
|
|
{
|
|
|
|
template <typename CType, typename Function>
|
2017-07-13 07:13:37 +00:00
|
|
|
static void call(
|
|
|
|
const std::size_t& entityID,
|
|
|
|
CType& ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
Function&& function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
|
|
|
function(
|
|
|
|
entityID,
|
2021-09-06 10:52:23 +00:00
|
|
|
userData,
|
2016-03-13 09:07:49 +00:00
|
|
|
ctype.template getEntityData<Types>(entityID)...
|
|
|
|
);
|
|
|
|
}
|
2016-09-20 11:07:28 +00:00
|
|
|
|
2017-11-14 04:11:32 +00:00
|
|
|
template <typename CType, typename Function>
|
|
|
|
static void callPtr(
|
|
|
|
const std::size_t& entityID,
|
|
|
|
CType& ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
Function* function,
|
2021-09-06 11:57:13 +00:00
|
|
|
void* userData = nullptr)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
(*function)(
|
|
|
|
entityID,
|
2021-09-06 10:52:23 +00:00
|
|
|
userData,
|
2017-11-14 04:11:32 +00:00
|
|
|
ctype.template getEntityData<Types>(entityID)...
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:07:28 +00:00
|
|
|
template <typename CType, typename Function>
|
2017-07-13 07:13:37 +00:00
|
|
|
void callInstance(
|
|
|
|
const std::size_t& entityID,
|
|
|
|
CType& ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
Function&& function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr) const
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
ForMatchingSignatureHelper<Types...>::call(
|
|
|
|
entityID,
|
|
|
|
ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
std::forward<Function>(function),
|
2021-09-06 10:52:23 +00:00
|
|
|
userData);
|
2016-09-20 11:07:28 +00:00
|
|
|
}
|
2017-11-14 04:11:32 +00:00
|
|
|
|
|
|
|
template <typename CType, typename Function>
|
|
|
|
void callInstancePtr(
|
|
|
|
const std::size_t& entityID,
|
|
|
|
CType& ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
Function* function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr) const
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
ForMatchingSignatureHelper<Types...>::callPtr(
|
|
|
|
entityID,
|
|
|
|
ctype,
|
2018-08-08 07:52:12 +00:00
|
|
|
function,
|
2021-09-06 10:52:23 +00:00
|
|
|
userData);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
2017-07-13 07:13:37 +00:00
|
|
|
\brief Calls the given function on all Entities matching the given
|
|
|
|
Signature.
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
The function object given to this function must accept std::size_t
|
2018-08-08 07:52:12 +00:00
|
|
|
as its first parameter, void* as its second parameter, and Component
|
|
|
|
pointers for the rest of the parameters. Tags specified in the
|
|
|
|
Signature are only used as filters and will not be given as a
|
|
|
|
parameter to the function.
|
|
|
|
|
|
|
|
The second parameter is default nullptr and will be passed to the
|
|
|
|
function call as the second parameter as a means of providing
|
2021-09-06 10:52:23 +00:00
|
|
|
context (useful when the function is not a lambda function).
|
|
|
|
|
|
|
|
The third parameter is default false (not multi-threaded).
|
|
|
|
Otherwise, if true, then the thread pool will be used to call the
|
|
|
|
given function in parallel across all entities. Note that
|
|
|
|
multi-threading is based on splitting the task of calling the
|
|
|
|
function across sections of entities. Thus if there are only a small
|
|
|
|
amount of entities in the manager, then using multiple threads may
|
|
|
|
not have as great of a speed-up.
|
2017-10-06 03:47:05 +00:00
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2018-08-08 07:52:12 +00:00
|
|
|
Context c; // some class/struct with data
|
|
|
|
manager.forMatchingSignature<TypeList<C0, C1, T0>>([]
|
|
|
|
(std::size_t ID,
|
|
|
|
void* context,
|
|
|
|
C0* component0, C1* component1)
|
|
|
|
{
|
2016-09-20 11:31:56 +00:00
|
|
|
// Lambda function contents here
|
2017-10-06 03:47:05 +00:00
|
|
|
},
|
2018-08-08 07:52:12 +00:00
|
|
|
&c, // "Context" object passed to the function
|
2021-09-08 05:58:41 +00:00
|
|
|
true // enable use of internal ThreadPool
|
2017-10-06 03:47:05 +00:00
|
|
|
);
|
2016-09-20 11:31:56 +00:00
|
|
|
\endcode
|
2017-07-13 07:13:37 +00:00
|
|
|
Note, the ID given to the function is not permanent. An entity's ID
|
|
|
|
may change when cleanup() is called.
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
2016-03-13 09:07:49 +00:00
|
|
|
template <typename Signature, typename Function>
|
2017-10-06 03:47:05 +00:00
|
|
|
void forMatchingSignature(Function&& function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr,
|
|
|
|
const bool useThreadPool = false)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
using SignatureComponents =
|
|
|
|
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
|
|
|
using Helper =
|
|
|
|
EC::Meta::Morph<
|
|
|
|
SignatureComponents,
|
|
|
|
ForMatchingSignatureHelper<> >;
|
|
|
|
|
|
|
|
BitsetType signatureBitset =
|
|
|
|
BitsetType::template generateBitset<Signature>();
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2017-10-06 03:47:05 +00:00
|
|
|
for(std::size_t i = 0; i < currentSize; ++i)
|
2016-03-13 09:07:49 +00:00
|
|
|
{
|
2017-10-06 03:47:05 +00:00
|
|
|
if(!std::get<bool>(entities[i]))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-13 09:07:49 +00:00
|
|
|
|
2017-10-06 03:47:05 +00:00
|
|
|
if((signatureBitset & std::get<BitsetType>(entities[i]))
|
|
|
|
== signatureBitset)
|
|
|
|
{
|
|
|
|
Helper::call(i, *this,
|
2021-09-06 10:52:23 +00:00
|
|
|
std::forward<Function>(function), userData);
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructZero, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2017-10-06 03:47:05 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-10-06 03:47:05 +00:00
|
|
|
end = currentSize;
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-10-06 03:47:05 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].signature = &signatureBitset;
|
|
|
|
fnDataAr[i].userData = userData;
|
|
|
|
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([&function] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructZero*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2017-10-06 03:47:05 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-07 08:03:08 +00:00
|
|
|
if(((*data->signature)
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->entities->at(i)))
|
|
|
|
== *data->signature) {
|
|
|
|
Helper::call(i,
|
|
|
|
*data->manager,
|
|
|
|
std::forward<Function>(function),
|
|
|
|
data->userData);
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2016-03-13 09:07:49 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-04 13:59:43 +00:00
|
|
|
|
2017-11-14 04:11:32 +00:00
|
|
|
/*!
|
|
|
|
\brief Calls the given function on all Entities matching the given
|
|
|
|
Signature.
|
|
|
|
|
|
|
|
The function pointer given to this function must accept std::size_t
|
2018-08-08 07:52:12 +00:00
|
|
|
as its first parameter, void* as its second parameter, and
|
|
|
|
Component pointers for the rest of the parameters. Tags specified in
|
|
|
|
the Signature are only used as filters and will not be given as a
|
|
|
|
parameter to the function.
|
|
|
|
|
|
|
|
The second parameter is default nullptr and will be passed to the
|
|
|
|
function call as the second parameter as a means of providing
|
2021-09-06 10:52:23 +00:00
|
|
|
context (useful when the function is not a lambda function).
|
|
|
|
|
|
|
|
The third parameter is default false (not multi-threaded).
|
|
|
|
Otherwise, if true, then the thread pool will be used to call the
|
|
|
|
given function in parallel across all entities. Note that
|
|
|
|
multi-threading is based on splitting the task of calling the
|
|
|
|
function across sections of entities. Thus if there are only a small
|
|
|
|
amount of entities in the manager, then using multiple threads may
|
|
|
|
not have as great of a speed-up.
|
2017-11-14 04:11:32 +00:00
|
|
|
|
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2018-08-08 07:52:12 +00:00
|
|
|
Context c; // some class/struct with data
|
|
|
|
auto function = []
|
|
|
|
(std::size_t ID,
|
|
|
|
void* context,
|
|
|
|
C0* component0, C1* component1)
|
|
|
|
{
|
2017-11-14 04:11:32 +00:00
|
|
|
// Lambda function contents here
|
|
|
|
};
|
|
|
|
manager.forMatchingSignaturePtr<TypeList<C0, C1, T0>>(
|
|
|
|
&function, // ptr
|
2018-08-08 07:52:12 +00:00
|
|
|
&c, // "Context" object passed to the function
|
2021-09-08 05:58:41 +00:00
|
|
|
true // enable use of ThreadPool
|
2017-11-14 04:11:32 +00:00
|
|
|
);
|
|
|
|
\endcode
|
|
|
|
Note, the ID given to the function is not permanent. An entity's ID
|
|
|
|
may change when cleanup() is called.
|
|
|
|
*/
|
|
|
|
template <typename Signature, typename Function>
|
|
|
|
void forMatchingSignaturePtr(Function* function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr,
|
|
|
|
const bool useThreadPool = false)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
using SignatureComponents =
|
|
|
|
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
|
|
|
using Helper =
|
|
|
|
EC::Meta::Morph<
|
|
|
|
SignatureComponents,
|
|
|
|
ForMatchingSignatureHelper<> >;
|
|
|
|
|
|
|
|
BitsetType signatureBitset =
|
|
|
|
BitsetType::template generateBitset<Signature>();
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
for(std::size_t i = 0; i < currentSize; ++i)
|
|
|
|
{
|
|
|
|
if(!std::get<bool>(entities[i]))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((signatureBitset & std::get<BitsetType>(entities[i]))
|
|
|
|
== signatureBitset)
|
|
|
|
{
|
2021-09-06 10:52:23 +00:00
|
|
|
Helper::callPtr(i, *this, function, userData);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructOne<Function>, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2017-11-14 04:11:32 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-14 04:11:32 +00:00
|
|
|
end = currentSize;
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-11-14 04:11:32 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].signature = &signatureBitset;
|
|
|
|
fnDataAr[i].userData = userData;
|
|
|
|
fnDataAr[i].fn = function;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructOne<Function>*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2017-11-14 04:11:32 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-07 08:03:08 +00:00
|
|
|
if(((*data->signature)
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->entities->at(i)))
|
|
|
|
== *data->signature) {
|
|
|
|
Helper::callPtr(i,
|
|
|
|
*data->manager,
|
|
|
|
data->fn,
|
|
|
|
data->userData);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-20 11:07:28 +00:00
|
|
|
private:
|
2018-11-22 05:28:31 +00:00
|
|
|
std::map<std::size_t, std::tuple<
|
2017-11-14 04:51:24 +00:00
|
|
|
BitsetType,
|
2018-08-08 07:52:12 +00:00
|
|
|
void*,
|
2017-11-14 04:51:24 +00:00
|
|
|
std::function<void(
|
|
|
|
std::size_t,
|
2018-08-08 07:52:12 +00:00
|
|
|
std::vector<std::size_t>,
|
|
|
|
void*)> > >
|
2017-09-20 08:16:26 +00:00
|
|
|
forMatchingFunctions;
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t functionIndex = 0;
|
2016-09-20 11:07:28 +00:00
|
|
|
|
|
|
|
public:
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Stores a function in the manager to be called later.
|
|
|
|
|
|
|
|
As an alternative to calling functions directly with
|
|
|
|
forMatchingSignature(), functions can be stored in the manager to
|
2017-07-12 13:02:02 +00:00
|
|
|
be called later with callForMatchingFunctions() and
|
2017-07-13 07:13:37 +00:00
|
|
|
callForMatchingFunction, and removed with
|
|
|
|
clearForMatchingFunctions() and removeForMatchingFunction().
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
The syntax for the Function is the same as with
|
|
|
|
forMatchingSignature().
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
Note that functions will be called in the same order they are
|
|
|
|
inserted if called by callForMatchingFunctions() unless the
|
|
|
|
internal functionIndex counter has wrapped around (is a
|
|
|
|
std::size_t). Calling clearForMatchingFunctions() will reset this
|
|
|
|
counter to zero.
|
2016-09-21 12:01:48 +00:00
|
|
|
|
2018-08-08 07:52:12 +00:00
|
|
|
Note that the context pointer provided here (default nullptr) will
|
|
|
|
be provided to the stored function when called.
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2018-08-08 07:52:12 +00:00
|
|
|
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
|
|
|
|
(std::size_t ID,
|
|
|
|
void* context,
|
|
|
|
C0* component0, C1* component1)
|
|
|
|
{
|
2016-09-20 11:31:56 +00:00
|
|
|
// Lambda function contents here
|
|
|
|
});
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// call all stored functions
|
|
|
|
manager.callForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// remove all stored functions
|
|
|
|
manager.clearForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
\endcode
|
2016-09-21 12:01:48 +00:00
|
|
|
|
|
|
|
\return The index of the function, used for deletion with
|
2021-09-08 05:14:14 +00:00
|
|
|
removeForMatchingFunction() or filtering with
|
2017-07-13 07:13:37 +00:00
|
|
|
keepSomeMatchingFunctions() or removeSomeMatchingFunctions(),
|
|
|
|
or calling with callForMatchingFunction().
|
2016-09-20 11:31:56 +00:00
|
|
|
*/
|
2016-09-20 11:07:28 +00:00
|
|
|
template <typename Signature, typename Function>
|
2018-08-08 07:52:12 +00:00
|
|
|
std::size_t addForMatchingFunction(
|
|
|
|
Function&& function,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr)
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
while(forMatchingFunctions.find(functionIndex)
|
|
|
|
!= forMatchingFunctions.end())
|
|
|
|
{
|
|
|
|
++functionIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
using SignatureComponents =
|
|
|
|
typename EC::Meta::Matching<Signature, ComponentsList>::type;
|
|
|
|
using Helper =
|
|
|
|
EC::Meta::Morph<
|
|
|
|
SignatureComponents,
|
|
|
|
ForMatchingSignatureHelper<> >;
|
2016-09-20 11:07:28 +00:00
|
|
|
|
|
|
|
Helper helper;
|
2017-07-13 07:13:37 +00:00
|
|
|
BitsetType signatureBitset =
|
|
|
|
BitsetType::template generateBitset<Signature>();
|
2016-09-20 11:07:28 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
forMatchingFunctions.emplace(std::make_pair(
|
|
|
|
functionIndex,
|
2017-11-14 04:51:24 +00:00
|
|
|
std::make_tuple(
|
|
|
|
signatureBitset,
|
2021-09-06 10:52:23 +00:00
|
|
|
userData,
|
2019-07-11 10:47:39 +00:00
|
|
|
[function, helper, this]
|
2021-09-06 10:52:23 +00:00
|
|
|
(const bool useThreadPool,
|
2018-08-08 07:52:12 +00:00
|
|
|
std::vector<std::size_t> matching,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData)
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2017-11-14 04:51:24 +00:00
|
|
|
for(auto eid : matching)
|
2017-10-06 03:47:05 +00:00
|
|
|
{
|
2017-12-01 10:20:59 +00:00
|
|
|
if(isAlive(eid))
|
|
|
|
{
|
2018-08-08 07:52:12 +00:00
|
|
|
helper.callInstancePtr(
|
2021-09-06 10:52:23 +00:00
|
|
|
eid, *this, &function, userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructTwo, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::size_t s = matching.size() / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2017-11-14 04:51:24 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-14 04:51:24 +00:00
|
|
|
end = matching.size();
|
2021-05-16 11:07:14 +00:00
|
|
|
} else {
|
2017-11-14 04:51:24 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].userData = userData;
|
|
|
|
fnDataAr[i].matching = &matching;
|
|
|
|
threadPool->queueFn([&function, helper] (void* ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructTwo*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0];
|
|
|
|
i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(data->manager->isAlive(
|
|
|
|
data->matching->at(i))) {
|
2018-08-08 07:52:12 +00:00
|
|
|
helper.callInstancePtr(
|
2021-09-07 08:03:08 +00:00
|
|
|
data->matching->at(i),
|
|
|
|
*data->manager,
|
2021-09-06 10:52:23 +00:00
|
|
|
&function,
|
2021-09-07 08:03:08 +00:00
|
|
|
data->userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-07 08:03:08 +00:00
|
|
|
std::this_thread::sleep_for(
|
|
|
|
std::chrono::microseconds(200));
|
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
})));
|
|
|
|
|
|
|
|
return functionIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<std::vector<std::size_t> > getMatchingEntities(
|
2021-09-06 10:52:23 +00:00
|
|
|
std::vector<BitsetType*> bitsets, const bool useThreadPool = false)
|
2017-11-14 04:51:24 +00:00
|
|
|
{
|
|
|
|
std::vector<std::vector<std::size_t> > matchingV(bitsets.size());
|
|
|
|
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2017-11-14 04:51:24 +00:00
|
|
|
{
|
|
|
|
for(std::size_t i = 0; i < currentSize; ++i)
|
2017-10-06 03:47:05 +00:00
|
|
|
{
|
2017-11-14 04:51:24 +00:00
|
|
|
if(!isAlive(i))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for(std::size_t j = 0; j < bitsets.size(); ++j)
|
2017-10-06 03:47:05 +00:00
|
|
|
{
|
2017-11-14 04:51:24 +00:00
|
|
|
if(((*bitsets[j]) & std::get<BitsetType>(entities[i]))
|
|
|
|
== (*bitsets[j]))
|
2017-10-06 03:47:05 +00:00
|
|
|
{
|
2017-11-14 04:51:24 +00:00
|
|
|
matchingV[j].push_back(i);
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructThree, ThreadCount> fnDataAr;
|
2021-05-16 11:07:14 +00:00
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
std::mutex mutex;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
|
|
|
if(i == ThreadCount - 1) {
|
|
|
|
end = currentSize;
|
|
|
|
} else {
|
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].matchingV = &matchingV;
|
|
|
|
fnDataAr[i].bitsets = &bitsets;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].mutex = &mutex;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructThree*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2021-09-06 10:52:23 +00:00
|
|
|
continue;
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t j = 0; j < data->bitsets->size();
|
2021-09-06 10:52:23 +00:00
|
|
|
++j) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if((*data->bitsets->at(j)
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->entities->at(i)))
|
|
|
|
== *data->bitsets->at(j)) {
|
|
|
|
std::lock_guard<std::mutex> lock(
|
|
|
|
*data->mutex);
|
|
|
|
data->matchingV->at(j).push_back(i);
|
2017-10-06 03:47:05 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2021-09-06 10:52:23 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-14 04:51:24 +00:00
|
|
|
}
|
2016-09-21 12:01:48 +00:00
|
|
|
|
2017-11-14 04:51:24 +00:00
|
|
|
return matchingV;
|
2016-09-20 11:07:28 +00:00
|
|
|
}
|
|
|
|
|
2017-11-14 04:51:24 +00:00
|
|
|
public:
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Call all stored functions.
|
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
The first (and only) parameter can be optionally used to enable the
|
|
|
|
use of the internal ThreadPool to call all stored functions in
|
|
|
|
parallel. Using the value false (which is the default) will not use
|
|
|
|
the ThreadPool and run all stored functions sequentially on the main
|
|
|
|
thread. Note that multi-threading is based on splitting the task of
|
|
|
|
calling the functions across sections of entities. Thus if there are
|
|
|
|
only a small amount of entities in the manager, then using multiple
|
2017-10-06 03:47:05 +00:00
|
|
|
threads may not have as great of a speed-up.
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2018-08-08 07:52:12 +00:00
|
|
|
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
|
|
|
|
(std::size_t ID,
|
|
|
|
void* context,
|
|
|
|
C0* component0, C1* component1) {
|
2016-09-20 11:31:56 +00:00
|
|
|
// Lambda function contents here
|
|
|
|
});
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// call all stored functions
|
|
|
|
manager.callForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2021-09-08 05:58:41 +00:00
|
|
|
// call all stored functions with ThreadPool enabled
|
|
|
|
manager.callForMatchingFunctions(true);
|
2017-10-06 03:47:05 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// remove all stored functions
|
|
|
|
manager.clearForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
\endcode
|
|
|
|
*/
|
2021-09-06 10:52:23 +00:00
|
|
|
void callForMatchingFunctions(const bool useThreadPool = false)
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2017-11-14 04:51:24 +00:00
|
|
|
std::vector<BitsetType*> bitsets;
|
|
|
|
for(auto iter = forMatchingFunctions.begin();
|
|
|
|
iter != forMatchingFunctions.end();
|
|
|
|
++iter)
|
|
|
|
{
|
|
|
|
bitsets.push_back(&std::get<BitsetType>(iter->second));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::vector<std::size_t> > matching =
|
2021-09-06 10:52:23 +00:00
|
|
|
getMatchingEntities(bitsets, useThreadPool);
|
2017-11-14 04:51:24 +00:00
|
|
|
|
|
|
|
std::size_t i = 0;
|
|
|
|
for(auto iter = forMatchingFunctions.begin();
|
|
|
|
iter != forMatchingFunctions.end();
|
|
|
|
++iter)
|
2016-09-20 11:07:28 +00:00
|
|
|
{
|
2018-08-08 07:52:12 +00:00
|
|
|
std::get<2>(iter->second)(
|
2021-09-06 10:52:23 +00:00
|
|
|
useThreadPool, matching[i++], std::get<1>(iter->second));
|
2016-09-20 11:07:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-12 13:02:02 +00:00
|
|
|
/*!
|
|
|
|
\brief Call a specific stored function.
|
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
The second parameter can be optionally used to enable the use of the
|
|
|
|
internal ThreadPool to call the stored function in parallel. Using
|
|
|
|
the value false (which is the default) will not use the ThreadPool
|
|
|
|
and run the stored function sequentially on the main thread. Note
|
|
|
|
that multi-threading is based on splitting the task of calling the
|
|
|
|
functions across sections of entities. Thus if there are only a
|
|
|
|
small amount of entities in the manager, then using multiple threads
|
|
|
|
may not have as great of a speed-up.
|
2017-10-06 03:47:05 +00:00
|
|
|
|
2017-07-12 13:02:02 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t id =
|
|
|
|
manager.addForMatchingFunction<TypeList<C0, C1, T0>>(
|
2018-08-08 07:52:12 +00:00
|
|
|
[] (std::size_t ID, void* context, C0* c0, C1* c1) {
|
2017-07-12 13:02:02 +00:00
|
|
|
// Lambda function contents here
|
|
|
|
});
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// call the previously added function
|
|
|
|
manager.callForMatchingFunction(id);
|
2017-10-06 03:47:05 +00:00
|
|
|
|
2021-09-08 05:58:41 +00:00
|
|
|
// call the previously added function with ThreadPool enabled
|
|
|
|
manager.callForMatchingFunction(id, true);
|
2017-07-12 13:02:02 +00:00
|
|
|
\endcode
|
|
|
|
|
|
|
|
\return False if a function with the given id does not exist.
|
|
|
|
*/
|
2017-10-06 03:47:05 +00:00
|
|
|
bool callForMatchingFunction(std::size_t id,
|
2021-09-06 10:52:23 +00:00
|
|
|
const bool useThreadPool = false)
|
2017-07-12 13:02:02 +00:00
|
|
|
{
|
|
|
|
auto iter = forMatchingFunctions.find(id);
|
|
|
|
if(iter == forMatchingFunctions.end())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2017-11-14 04:51:24 +00:00
|
|
|
std::vector<std::vector<std::size_t> > matching =
|
|
|
|
getMatchingEntities(std::vector<BitsetType*>{
|
2021-09-06 10:52:23 +00:00
|
|
|
&std::get<BitsetType>(iter->second)}, useThreadPool);
|
2018-08-08 07:52:12 +00:00
|
|
|
std::get<2>(iter->second)(
|
2021-09-06 10:52:23 +00:00
|
|
|
useThreadPool, matching[0], std::get<1>(iter->second));
|
2017-07-12 13:02:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
/*!
|
|
|
|
\brief Remove all stored functions.
|
|
|
|
|
2016-09-21 12:01:48 +00:00
|
|
|
Also resets the index counter of stored functions to 0.
|
|
|
|
|
2016-09-20 11:31:56 +00:00
|
|
|
Example:
|
|
|
|
\code{.cpp}
|
2018-08-08 07:52:12 +00:00
|
|
|
manager.addForMatchingFunction<TypeList<C0, C1, T0>>([]
|
|
|
|
(std::size_t ID,
|
|
|
|
void* context,
|
|
|
|
C0* component0, C1* component1)
|
|
|
|
{
|
2016-09-20 11:31:56 +00:00
|
|
|
// Lambda function contents here
|
|
|
|
});
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// call all stored functions
|
|
|
|
manager.callForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
// remove all stored functions
|
|
|
|
manager.clearForMatchingFunctions();
|
2016-09-20 11:31:56 +00:00
|
|
|
\endcode
|
|
|
|
*/
|
2016-09-20 11:07:28 +00:00
|
|
|
void clearForMatchingFunctions()
|
|
|
|
{
|
|
|
|
forMatchingFunctions.clear();
|
2016-09-21 12:01:48 +00:00
|
|
|
functionIndex = 0;
|
|
|
|
}
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
/*!
|
|
|
|
\brief Removes a function that has the given id.
|
|
|
|
|
|
|
|
\return True if a function was erased.
|
|
|
|
*/
|
|
|
|
bool removeForMatchingFunction(std::size_t id)
|
|
|
|
{
|
|
|
|
return forMatchingFunctions.erase(id) == 1;
|
|
|
|
}
|
|
|
|
|
2016-09-21 12:01:48 +00:00
|
|
|
/*!
|
|
|
|
\brief Removes all functions that do not have the index specified
|
|
|
|
in argument "list".
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
The given List must be iterable.
|
|
|
|
This is the only requirement, so a set could also be given.
|
|
|
|
|
|
|
|
\return The number of functions deleted.
|
2016-09-21 12:01:48 +00:00
|
|
|
*/
|
|
|
|
template <typename List>
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t keepSomeMatchingFunctions(List list)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t deletedCount = 0;
|
2017-09-20 08:16:26 +00:00
|
|
|
for(auto iter = forMatchingFunctions.begin();
|
|
|
|
iter != forMatchingFunctions.end();)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-09-20 08:16:26 +00:00
|
|
|
if(std::find(list.begin(), list.end(), iter->first)
|
|
|
|
== list.end())
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 08:28:45 +00:00
|
|
|
iter = forMatchingFunctions.erase(iter);
|
|
|
|
++deletedCount;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++iter;
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
}
|
2017-07-13 07:13:37 +00:00
|
|
|
|
|
|
|
return deletedCount;
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\brief Removes all functions that do not have the index specified
|
|
|
|
in argument "list".
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
This function allows for passing an initializer list.
|
|
|
|
|
|
|
|
\return The number of functions deleted.
|
2016-09-21 12:01:48 +00:00
|
|
|
*/
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t keepSomeMatchingFunctions(
|
|
|
|
std::initializer_list<std::size_t> list)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
return keepSomeMatchingFunctions<decltype(list)>(list);
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
2017-07-12 13:02:02 +00:00
|
|
|
/*!
|
2017-07-13 07:13:37 +00:00
|
|
|
\brief Removes all functions that do have the index specified
|
|
|
|
in argument "list".
|
2017-07-12 13:02:02 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
The given List must be iterable.
|
|
|
|
This is the only requirement, so a set could also be given.
|
2017-07-12 13:02:02 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
\return The number of functions deleted.
|
|
|
|
*/
|
|
|
|
template <typename List>
|
|
|
|
std::size_t removeSomeMatchingFunctions(List list)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t deletedCount = 0;
|
|
|
|
for(auto listIter = list.begin();
|
|
|
|
listIter != list.end();
|
|
|
|
++listIter)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
deletedCount += forMatchingFunctions.erase(*listIter);
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
return deletedCount;
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2017-07-13 07:13:37 +00:00
|
|
|
\brief Removes all functions that do have the index specified
|
|
|
|
in argument "list".
|
|
|
|
|
|
|
|
This function allows for passing an initializer list.
|
2016-09-21 12:01:48 +00:00
|
|
|
|
2017-07-13 07:13:37 +00:00
|
|
|
\return The number of functions deleted.
|
2016-09-21 12:01:48 +00:00
|
|
|
*/
|
2017-07-13 07:13:37 +00:00
|
|
|
std::size_t removeSomeMatchingFunctions(
|
|
|
|
std::initializer_list<std::size_t> list)
|
2016-09-21 12:01:48 +00:00
|
|
|
{
|
2017-07-13 07:13:37 +00:00
|
|
|
return removeSomeMatchingFunctions<decltype(list)>(list);
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-08 07:52:12 +00:00
|
|
|
/*!
|
|
|
|
\brief Sets the context pointer of a stored function
|
|
|
|
|
|
|
|
\return True if id is valid and context was updated
|
|
|
|
*/
|
2021-09-06 10:52:23 +00:00
|
|
|
bool changeForMatchingFunctionContext(std::size_t id, void* userData)
|
2018-08-08 07:52:12 +00:00
|
|
|
{
|
|
|
|
auto f = forMatchingFunctions.find(id);
|
|
|
|
if(f != forMatchingFunctions.end())
|
|
|
|
{
|
2021-09-06 10:52:23 +00:00
|
|
|
std::get<1>(f->second) = userData;
|
2018-08-08 07:52:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-09 12:10:01 +00:00
|
|
|
/*!
|
|
|
|
\brief Call multiple functions with mulitple signatures on all
|
|
|
|
living entities.
|
|
|
|
|
|
|
|
(Living entities as in entities that have not been marked for
|
|
|
|
deletion.)
|
|
|
|
|
|
|
|
This function requires the first template parameter to be a
|
|
|
|
EC::Meta::TypeList of signatures. Note that a signature is a
|
|
|
|
EC::Meta::TypeList of components and tags, meaning that SigList
|
|
|
|
is a TypeList of TypeLists.
|
|
|
|
|
|
|
|
The second template parameter can be inferred from the function
|
|
|
|
parameter which should be a tuple of functions. The function
|
|
|
|
at any index in the tuple should match with a signature of the
|
|
|
|
same index in the SigList. Behavior is undefined if there are
|
|
|
|
less functions than signatures.
|
|
|
|
|
|
|
|
See the Unit Test of this function in src/test/ECTest.cpp for
|
|
|
|
usage examples.
|
|
|
|
|
2018-08-08 07:52:12 +00:00
|
|
|
The second parameter (default nullptr) will be provided to every
|
|
|
|
function call as a void* (context).
|
|
|
|
|
2021-09-07 03:04:42 +00:00
|
|
|
The third parameter is default false (not multi-threaded).
|
|
|
|
Otherwise, if true, then the thread pool will be used to call the
|
|
|
|
given function in parallel across all entities. Note that
|
|
|
|
multi-threading is based on splitting the task of calling the
|
|
|
|
function across sections of entities. Thus if there are only a small
|
|
|
|
amount of entities in the manager, then using multiple threads may
|
|
|
|
not have as great of a speed-up.
|
|
|
|
|
2017-11-09 12:10:01 +00:00
|
|
|
This function was created for the use case where there are many
|
|
|
|
entities in the system which can cause multiple calls to
|
|
|
|
forMatchingSignature to be slow due to the overhead of iterating
|
|
|
|
through the entire list of entities on each invocation.
|
|
|
|
This function instead iterates through all entities once,
|
|
|
|
storing matching entities in a vector of vectors (for each
|
|
|
|
signature and function pair) and then calling functions with
|
|
|
|
the matching list of entities.
|
|
|
|
|
|
|
|
Note that multi-threaded or not, functions will be called in order
|
|
|
|
of signatures. The first function signature pair will be called
|
|
|
|
first, then the second, third, and so on.
|
|
|
|
If this function is called with more than 1 thread specified, then
|
|
|
|
the order of entities called is not guaranteed. Otherwise entities
|
|
|
|
will be called in consecutive order by their ID.
|
|
|
|
*/
|
2017-11-15 06:36:04 +00:00
|
|
|
template <typename SigList, typename FTuple>
|
|
|
|
void forMatchingSignatures(
|
2018-08-08 07:52:12 +00:00
|
|
|
FTuple fTuple,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr,
|
|
|
|
const bool useThreadPool = false)
|
2017-11-09 12:10:01 +00:00
|
|
|
{
|
2021-09-06 10:52:23 +00:00
|
|
|
std::vector<std::vector<std::size_t> >
|
|
|
|
multiMatchingEntities(SigList::size);
|
2017-11-09 12:10:01 +00:00
|
|
|
BitsetType signatureBitsets[SigList::size];
|
|
|
|
|
|
|
|
// generate bitsets for each signature
|
2017-11-15 06:36:04 +00:00
|
|
|
EC::Meta::forEachWithIndex<SigList>(
|
2018-05-19 07:09:31 +00:00
|
|
|
[&signatureBitsets] (auto signature, const auto index) {
|
2017-11-15 06:36:04 +00:00
|
|
|
signatureBitsets[index] =
|
|
|
|
BitsetType::template generateBitset
|
|
|
|
<decltype(signature)>();
|
2017-11-09 12:10:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// find and store entities matching signatures
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2017-11-09 12:10:01 +00:00
|
|
|
{
|
|
|
|
for(std::size_t eid = 0; eid < currentSize; ++eid)
|
|
|
|
{
|
|
|
|
if(!isAlive(eid))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for(std::size_t i = 0; i < SigList::size; ++i)
|
|
|
|
{
|
|
|
|
if((signatureBitsets[i]
|
|
|
|
& std::get<BitsetType>(entities[eid]))
|
|
|
|
== signatureBitsets[i])
|
|
|
|
{
|
|
|
|
multiMatchingEntities[i].push_back(eid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructFour, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::mutex mutex;
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2017-11-09 12:10:01 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-09 12:10:01 +00:00
|
|
|
end = currentSize;
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-11-09 12:10:01 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].multiMatchingEntities = &multiMatchingEntities;
|
|
|
|
fnDataAr[i].signatures = signatureBitsets;
|
|
|
|
fnDataAr[i].mutex = &mutex;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructFour*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2017-11-09 12:10:01 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
for(std::size_t j = 0; j < SigList::size; ++j) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if((data->signatures[j]
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->manager->entities[i]))
|
|
|
|
== data->signatures[j]) {
|
|
|
|
std::lock_guard<std::mutex> lock(
|
|
|
|
*data->mutex);
|
|
|
|
data->multiMatchingEntities->at(j)
|
|
|
|
.push_back(i);
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
2019-07-11 10:47:39 +00:00
|
|
|
|
2017-11-09 12:10:01 +00:00
|
|
|
// call functions on matching entities
|
2017-11-15 06:36:04 +00:00
|
|
|
EC::Meta::forEachDoubleTuple(
|
|
|
|
EC::Meta::Morph<SigList, std::tuple<> >{},
|
|
|
|
fTuple,
|
2021-09-06 10:52:23 +00:00
|
|
|
[this, &multiMatchingEntities, useThreadPool, &userData]
|
2017-11-15 06:36:04 +00:00
|
|
|
(auto sig, auto func, auto index)
|
2017-11-09 12:10:01 +00:00
|
|
|
{
|
2017-11-15 06:36:04 +00:00
|
|
|
using SignatureComponents =
|
|
|
|
typename EC::Meta::Matching<
|
|
|
|
decltype(sig), ComponentsList>::type;
|
|
|
|
using Helper =
|
|
|
|
EC::Meta::Morph<
|
|
|
|
SignatureComponents,
|
|
|
|
ForMatchingSignatureHelper<> >;
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool) {
|
2021-09-06 10:52:23 +00:00
|
|
|
for(const auto& id : multiMatchingEntities[index]) {
|
|
|
|
if(isAlive(id)) {
|
|
|
|
Helper::call(id, *this, func, userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructFive, ThreadCount> fnDataAr;
|
2017-11-15 06:36:04 +00:00
|
|
|
std::size_t s = multiMatchingEntities[index].size()
|
2021-09-06 10:52:23 +00:00
|
|
|
/ ThreadCount;
|
|
|
|
for(unsigned int i = 0; i < ThreadCount; ++i) {
|
2017-11-15 06:36:04 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-15 06:36:04 +00:00
|
|
|
end = multiMatchingEntities[index].size();
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-11-15 06:36:04 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].index = index;
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].userData = userData;
|
|
|
|
fnDataAr[i].multiMatchingEntities =
|
|
|
|
&multiMatchingEntities;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([&func] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructFive*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0];
|
|
|
|
i < data->range[1]; ++i) {
|
|
|
|
if(data->manager->isAlive(
|
|
|
|
data->multiMatchingEntities
|
|
|
|
->at(data->index).at(i))) {
|
2017-12-01 10:20:59 +00:00
|
|
|
Helper::call(
|
2021-09-07 08:03:08 +00:00
|
|
|
data->multiMatchingEntities
|
|
|
|
->at(data->index).at(i),
|
|
|
|
*data->manager,
|
2018-08-08 07:52:12 +00:00
|
|
|
func,
|
2021-09-07 08:03:08 +00:00
|
|
|
data->userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-07 08:03:08 +00:00
|
|
|
std::this_thread::sleep_for(
|
|
|
|
std::chrono::microseconds(200));
|
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
);
|
2017-11-09 12:10:01 +00:00
|
|
|
}
|
|
|
|
|
2017-11-14 04:11:32 +00:00
|
|
|
/*!
|
|
|
|
\brief Call multiple functions with mulitple signatures on all
|
|
|
|
living entities.
|
|
|
|
|
|
|
|
(Living entities as in entities that have not been marked for
|
|
|
|
deletion.)
|
|
|
|
|
|
|
|
Note that this function requires the tuple of functions to hold
|
|
|
|
pointers to functions, not just functions.
|
|
|
|
|
|
|
|
This function requires the first template parameter to be a
|
|
|
|
EC::Meta::TypeList of signatures. Note that a signature is a
|
|
|
|
EC::Meta::TypeList of components and tags, meaning that SigList
|
|
|
|
is a TypeList of TypeLists.
|
|
|
|
|
|
|
|
The second template parameter can be inferred from the function
|
|
|
|
parameter which should be a tuple of functions. The function
|
|
|
|
at any index in the tuple should match with a signature of the
|
|
|
|
same index in the SigList. Behavior is undefined if there are
|
|
|
|
less functions than signatures.
|
|
|
|
|
|
|
|
See the Unit Test of this function in src/test/ECTest.cpp for
|
|
|
|
usage examples.
|
|
|
|
|
2018-08-08 07:52:12 +00:00
|
|
|
The second parameter (default nullptr) will be provided to every
|
|
|
|
function call as a void* (context).
|
|
|
|
|
2021-09-07 03:04:42 +00:00
|
|
|
The third parameter is default false (not multi-threaded).
|
|
|
|
Otherwise, if true, then the thread pool will be used to call the
|
|
|
|
given function in parallel across all entities. Note that
|
|
|
|
multi-threading is based on splitting the task of calling the
|
|
|
|
function across sections of entities. Thus if there are only a small
|
|
|
|
amount of entities in the manager, then using multiple threads may
|
|
|
|
not have as great of a speed-up.
|
|
|
|
|
2017-11-14 04:11:32 +00:00
|
|
|
This function was created for the use case where there are many
|
|
|
|
entities in the system which can cause multiple calls to
|
|
|
|
forMatchingSignature to be slow due to the overhead of iterating
|
|
|
|
through the entire list of entities on each invocation.
|
|
|
|
This function instead iterates through all entities once,
|
|
|
|
storing matching entities in a vector of vectors (for each
|
|
|
|
signature and function pair) and then calling functions with
|
|
|
|
the matching list of entities.
|
|
|
|
|
|
|
|
Note that multi-threaded or not, functions will be called in order
|
|
|
|
of signatures. The first function signature pair will be called
|
|
|
|
first, then the second, third, and so on.
|
|
|
|
If this function is called with more than 1 thread specified, then
|
|
|
|
the order of entities called is not guaranteed. Otherwise entities
|
|
|
|
will be called in consecutive order by their ID.
|
|
|
|
*/
|
2017-11-15 06:36:04 +00:00
|
|
|
template <typename SigList, typename FTuple>
|
|
|
|
void forMatchingSignaturesPtr(FTuple fTuple,
|
2021-09-06 10:52:23 +00:00
|
|
|
void* userData = nullptr,
|
|
|
|
const bool useThreadPool = false)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
std::vector<std::vector<std::size_t> > multiMatchingEntities(
|
|
|
|
SigList::size);
|
|
|
|
BitsetType signatureBitsets[SigList::size];
|
|
|
|
|
|
|
|
// generate bitsets for each signature
|
2017-11-15 06:36:04 +00:00
|
|
|
EC::Meta::forEachWithIndex<SigList>(
|
2018-05-16 07:18:11 +00:00
|
|
|
[&signatureBitsets] (auto signature, const auto index) {
|
2017-11-15 06:36:04 +00:00
|
|
|
signatureBitsets[index] =
|
|
|
|
BitsetType::template generateBitset
|
|
|
|
<decltype(signature)>();
|
2017-11-14 04:11:32 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// find and store entities matching signatures
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
|
|
|
for(std::size_t eid = 0; eid < currentSize; ++eid)
|
|
|
|
{
|
|
|
|
if(!isAlive(eid))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for(std::size_t i = 0; i < SigList::size; ++i)
|
|
|
|
{
|
|
|
|
if((signatureBitsets[i]
|
|
|
|
& std::get<BitsetType>(entities[eid]))
|
|
|
|
== signatureBitsets[i])
|
|
|
|
{
|
|
|
|
multiMatchingEntities[i].push_back(eid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructSix, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::mutex mutex;
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2017-11-14 04:11:32 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-14 04:11:32 +00:00
|
|
|
end = currentSize;
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-11-14 04:11:32 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].multiMatchingEntities = &multiMatchingEntities;
|
|
|
|
fnDataAr[i].bitsets = signatureBitsets;
|
|
|
|
fnDataAr[i].mutex = &mutex;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructSix*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2017-11-14 04:11:32 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
for(std::size_t j = 0; j < SigList::size; ++j) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if((data->bitsets[j]
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->manager->entities[i]))
|
|
|
|
== data->bitsets[j]) {
|
|
|
|
std::lock_guard<std::mutex> lock(
|
|
|
|
*data->mutex);
|
|
|
|
data->multiMatchingEntities->at(j)
|
|
|
|
.push_back(i);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2019-07-11 10:47:39 +00:00
|
|
|
|
2017-11-14 04:11:32 +00:00
|
|
|
// call functions on matching entities
|
2017-11-15 06:36:04 +00:00
|
|
|
EC::Meta::forEachDoubleTuple(
|
|
|
|
EC::Meta::Morph<SigList, std::tuple<> >{},
|
|
|
|
fTuple,
|
2021-09-06 10:52:23 +00:00
|
|
|
[this, &multiMatchingEntities, useThreadPool, &userData]
|
2017-11-15 06:36:04 +00:00
|
|
|
(auto sig, auto func, auto index)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
2017-11-15 06:36:04 +00:00
|
|
|
using SignatureComponents =
|
|
|
|
typename EC::Meta::Matching<
|
|
|
|
decltype(sig), ComponentsList>::type;
|
|
|
|
using Helper =
|
|
|
|
EC::Meta::Morph<
|
|
|
|
SignatureComponents,
|
|
|
|
ForMatchingSignatureHelper<> >;
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool)
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
2017-11-15 06:36:04 +00:00
|
|
|
for(const auto& id : multiMatchingEntities[index])
|
|
|
|
{
|
2017-12-01 10:20:59 +00:00
|
|
|
if(isAlive(id))
|
|
|
|
{
|
2021-09-06 10:52:23 +00:00
|
|
|
Helper::callPtr(id, *this, func, userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
}
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
else
|
2017-11-14 04:11:32 +00:00
|
|
|
{
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructFive, ThreadCount> fnDataAr;
|
2017-11-15 06:36:04 +00:00
|
|
|
std::size_t s = multiMatchingEntities[index].size()
|
2021-09-06 10:52:23 +00:00
|
|
|
/ ThreadCount;
|
|
|
|
for(unsigned int i = 0; i < ThreadCount; ++i) {
|
2017-11-15 06:36:04 +00:00
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
2021-09-06 10:52:23 +00:00
|
|
|
if(i == ThreadCount - 1) {
|
2017-11-15 06:36:04 +00:00
|
|
|
end = multiMatchingEntities[index].size();
|
2021-09-06 10:52:23 +00:00
|
|
|
} else {
|
2017-11-15 06:36:04 +00:00
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].index = index;
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].userData = userData;
|
|
|
|
fnDataAr[i].multiMatchingEntities =
|
|
|
|
&multiMatchingEntities;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([&func] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructFive*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0];
|
|
|
|
i < data->range[1]; ++i) {
|
|
|
|
if(data->manager->isAlive(
|
|
|
|
data->multiMatchingEntities
|
|
|
|
->at(data->index).at(i))) {
|
2017-12-01 10:20:59 +00:00
|
|
|
Helper::callPtr(
|
2021-09-07 08:03:08 +00:00
|
|
|
data->multiMatchingEntities
|
|
|
|
->at(data->index).at(i),
|
|
|
|
*data->manager,
|
2018-08-08 07:52:12 +00:00
|
|
|
func,
|
2021-09-07 08:03:08 +00:00
|
|
|
data->userData);
|
2017-12-01 10:20:59 +00:00
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-07 08:03:08 +00:00
|
|
|
std::this_thread::sleep_for(
|
|
|
|
std::chrono::microseconds(200));
|
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-15 06:36:04 +00:00
|
|
|
);
|
2017-11-14 04:11:32 +00:00
|
|
|
}
|
|
|
|
|
2021-09-07 08:03:08 +00:00
|
|
|
typedef void ForMatchingFn(std::size_t,
|
|
|
|
Manager<ComponentsList, TagsList>*,
|
|
|
|
void*);
|
2016-09-21 12:01:48 +00:00
|
|
|
|
2019-11-06 07:33:12 +00:00
|
|
|
/*!
|
2021-09-07 03:04:42 +00:00
|
|
|
\brief A simple version of forMatchingSignature()
|
|
|
|
|
|
|
|
This function behaves like forMatchingSignature(), but instead of
|
|
|
|
providing a function with each requested component as a parameter,
|
|
|
|
the function receives a pointer to the manager itself, with which to
|
|
|
|
query component/tag data.
|
|
|
|
|
|
|
|
The third parameter can be optionally used to enable the use of the
|
|
|
|
internal ThreadPool to call the function in parallel. Using the
|
|
|
|
value false (which is the default) will not use the ThreadPool and
|
|
|
|
run the function sequentially on all entities on the main thread.
|
|
|
|
Note that multi-threading is based on splitting the task of calling
|
|
|
|
the functions across sections of entities. Thus if there are only a
|
|
|
|
small amount of entities in the manager, then using multiple threads
|
|
|
|
may not have as great of a speed-up.
|
2019-11-06 07:33:12 +00:00
|
|
|
*/
|
|
|
|
template <typename Signature>
|
2021-09-07 08:03:08 +00:00
|
|
|
void forMatchingSimple(ForMatchingFn fn,
|
|
|
|
void *userData = nullptr,
|
|
|
|
const bool useThreadPool = false) {
|
|
|
|
const BitsetType signatureBitset =
|
|
|
|
BitsetType::template generateBitset<Signature>();
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool) {
|
2019-11-06 07:33:12 +00:00
|
|
|
for(std::size_t i = 0; i < currentSize; ++i) {
|
|
|
|
if(!std::get<bool>(entities[i])) {
|
|
|
|
continue;
|
2021-09-07 08:03:08 +00:00
|
|
|
} else if((signatureBitset
|
|
|
|
& std::get<BitsetType>(entities[i]))
|
|
|
|
== signatureBitset) {
|
2019-11-06 07:33:12 +00:00
|
|
|
fn(i, this, userData);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructZero, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
|
|
|
std::size_t begin = s * i;
|
|
|
|
std::size_t end;
|
|
|
|
if(i == ThreadCount - 1) {
|
|
|
|
end = currentSize;
|
|
|
|
} else {
|
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].signature = &signatureBitset;
|
|
|
|
fnDataAr[i].userData = userData;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([&fn] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructZero*>(ud);
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2021-09-06 10:52:23 +00:00
|
|
|
continue;
|
2021-09-07 08:03:08 +00:00
|
|
|
} else if((*data->signature
|
|
|
|
& std::get<BitsetType>(
|
|
|
|
data->entities->at(i)))
|
|
|
|
== *data->signature) {
|
|
|
|
fn(i, data->manager, data->userData);
|
2019-11-06 07:33:12 +00:00
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2019-11-06 07:33:12 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2019-11-06 07:33:12 +00:00
|
|
|
}
|
2016-09-21 12:01:48 +00:00
|
|
|
}
|
2019-07-11 12:11:03 +00:00
|
|
|
|
|
|
|
/*!
|
2021-09-07 03:04:42 +00:00
|
|
|
\brief Similar to forMatchingSimple(), but with a collection of Component/Tag indices
|
|
|
|
|
|
|
|
This function works like forMatchingSimple(), but instead of
|
|
|
|
providing template types that filter out non-matching entities, an
|
|
|
|
iterable of indices must be provided which correlate to matching
|
|
|
|
Component/Tag indices. The function given must match the previously
|
|
|
|
defined typedef of type ForMatchingFn.
|
|
|
|
|
|
|
|
The fourth parameter can be optionally used to enable the use of the
|
|
|
|
internal ThreadPool to call the function in parallel. Using the
|
|
|
|
value false (which is the default) will not use the ThreadPool and
|
|
|
|
run the function sequentially on all entities on the main thread.
|
|
|
|
Note that multi-threading is based on splitting the task of calling
|
|
|
|
the functions across sections of entities. Thus if there are only a
|
|
|
|
small amount of entities in the manager, then using multiple threads
|
|
|
|
may not have as great of a speed-up.
|
2019-07-11 12:11:03 +00:00
|
|
|
*/
|
|
|
|
template <typename Iterable>
|
2021-09-07 08:03:08 +00:00
|
|
|
void forMatchingIterable(Iterable iterable,
|
|
|
|
ForMatchingFn fn,
|
|
|
|
void* userData = nullptr,
|
|
|
|
const bool useThreadPool = false) {
|
2021-09-07 02:46:38 +00:00
|
|
|
if(!useThreadPool || !threadPool) {
|
2019-07-11 12:11:03 +00:00
|
|
|
bool isValid;
|
|
|
|
for(std::size_t i = 0; i < currentSize; ++i) {
|
|
|
|
if(!std::get<bool>(entities[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
isValid = true;
|
|
|
|
for(const auto& integralValue : iterable) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!std::get<BitsetType>(entities[i]).getCombinedBit(
|
|
|
|
integralValue)) {
|
2019-07-11 12:11:03 +00:00
|
|
|
isValid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!isValid) { continue; }
|
|
|
|
|
2021-09-06 10:52:23 +00:00
|
|
|
fn(i, this, userData);
|
2019-07-11 12:11:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-09-07 09:11:06 +00:00
|
|
|
std::array<TPFnDataStructSeven<Iterable>, ThreadCount> fnDataAr;
|
2021-09-06 10:52:23 +00:00
|
|
|
|
|
|
|
std::size_t s = currentSize / ThreadCount;
|
|
|
|
for(std::size_t i = 0; i < ThreadCount; ++i) {
|
2019-07-11 12:11:03 +00:00
|
|
|
std::size_t begin = s * i;
|
2021-09-06 10:52:23 +00:00
|
|
|
std::size_t end;
|
|
|
|
if(i == ThreadCount - 1) {
|
|
|
|
end = currentSize;
|
|
|
|
} else {
|
|
|
|
end = s * (i + 1);
|
|
|
|
}
|
|
|
|
if(begin == end) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
fnDataAr[i].range = {begin, end};
|
|
|
|
fnDataAr[i].manager = this;
|
|
|
|
fnDataAr[i].entities = &entities;
|
|
|
|
fnDataAr[i].iterable = &iterable;
|
|
|
|
fnDataAr[i].userData = userData;
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->queueFn([&fn] (void *ud) {
|
2021-09-07 09:11:06 +00:00
|
|
|
auto *data = static_cast<TPFnDataStructSeven<Iterable>*>(ud);
|
2021-09-06 10:52:23 +00:00
|
|
|
bool isValid;
|
2021-09-07 08:03:08 +00:00
|
|
|
for(std::size_t i = data->range[0]; i < data->range[1];
|
2021-09-06 10:52:23 +00:00
|
|
|
++i) {
|
2021-09-07 08:03:08 +00:00
|
|
|
if(!data->manager->isAlive(i)) {
|
2021-09-06 10:52:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
isValid = true;
|
2021-09-07 08:03:08 +00:00
|
|
|
for(const auto& integralValue : *data->iterable) {
|
|
|
|
if(!std::get<BitsetType>(data->entities->at(i))
|
|
|
|
.getCombinedBit(integralValue)) {
|
2021-09-06 10:52:23 +00:00
|
|
|
isValid = false;
|
|
|
|
break;
|
2019-07-11 12:11:03 +00:00
|
|
|
}
|
2021-09-06 10:52:23 +00:00
|
|
|
}
|
|
|
|
if(!isValid) { continue; }
|
2019-07-11 12:11:03 +00:00
|
|
|
|
2021-09-07 08:03:08 +00:00
|
|
|
fn(i, data->manager, data->userData);
|
2021-09-06 10:52:23 +00:00
|
|
|
}
|
2021-09-07 08:03:08 +00:00
|
|
|
}, &fnDataAr[i]);
|
2019-07-11 12:11:03 +00:00
|
|
|
}
|
2021-09-07 02:46:38 +00:00
|
|
|
threadPool->wakeThreads();
|
2021-09-06 11:57:13 +00:00
|
|
|
do {
|
2021-09-06 12:01:01 +00:00
|
|
|
std::this_thread::sleep_for(std::chrono::microseconds(200));
|
2021-09-07 08:03:08 +00:00
|
|
|
} while(!threadPool->isQueueEmpty()
|
|
|
|
|| !threadPool->isAllThreadsWaiting());
|
2019-07-11 12:11:03 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-04 13:59:43 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|