diff --git a/src/EC/Bitset.hpp b/src/EC/Bitset.hpp index 4317313..786b29c 100644 --- a/src/EC/Bitset.hpp +++ b/src/EC/Bitset.hpp @@ -75,6 +75,28 @@ namespace EC return bitset; } + + template + auto getCombinedBit(const IntegralType& i) { + static_assert(std::is_integral::value, + "Parameter must be an integral type"); + if(i >= Combined::size || i < 0) { + return (*this)[Combined::size]; + } else { + return (*this)[i]; + } + } + + template + auto getCombinedBit(const IntegralType& i) const { + static_assert(std::is_integral::value, + "Parameter must be an integral type"); + if(i >= Combined::size || i < 0) { + return (*this)[Combined::size]; + } else { + return (*this)[i]; + } + } }; } diff --git a/src/EC/Manager.hpp b/src/EC/Manager.hpp index cb4a612..5f10fd6 100644 --- a/src/EC/Manager.hpp +++ b/src/EC/Manager.hpp @@ -1649,6 +1649,73 @@ namespace EC deletedSet.clear(); resize(EC_INIT_ENTITIES_SIZE); } + + typedef void ForMatchingIterableFn(std::size_t, Manager*, void*); + + /*! + * \brief Similar to forMatchingSignature(), but with a collection of Component/Tag indices + * + * This function works like forMatchingSignature(), 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 ForMatchingIterableFn. + */ + template + void forMatchingIterable(Iterable iterable, ForMatchingIterableFn fn, void* userPtr = nullptr, std::size_t threadCount = 1) { + if(threadCount <= 1) { + bool isValid; + for(std::size_t i = 0; i < currentSize; ++i) { + if(!std::get(entities[i])) { + continue; + } + + isValid = true; + for(const auto& integralValue : iterable) { + if(!std::get(entities[i]).getCombinedBit(integralValue)) { + isValid = false; + break; + } + } + if(!isValid) { continue; } + + fn(i, this, userPtr); + } + } else { + std::vector threads(threadCount); + 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 = i == threadCount - 1 ? + currentSize : + s * (i + 1); + threads[i] = std::thread( + [this, &fn, &iterable, userPtr] (std::size_t begin, std::size_t end) { + bool isValid; + for(std::size_t i = begin; i < end; ++i) { + if(!std::get(this->entities[i])) { + continue; + } + + isValid = true; + for(const auto& integralValue : iterable) { + if(!std::get(entities[i]).getCombinedBit(integralValue)) { + isValid = false; + break; + } + } + if(!isValid) { continue; } + + fn(i, this, userPtr); + } + }, + begin, end); + } + for(std::size_t i = 0; i < threadCount; ++i) { + threads[i].join(); + } + } + } }; } diff --git a/src/test/ECTest.cpp b/src/test/ECTest.cpp index f5f63fd..d15cc7a 100644 --- a/src/test/ECTest.cpp +++ b/src/test/ECTest.cpp @@ -39,6 +39,8 @@ using EmptyList = EC::Meta::TypeList<>; using MixedList = EC::Meta::TypeList; +using ListCombinedComponentsTags = EC::Meta::Combine; + typedef std::unique_ptr C0Ptr; struct Base @@ -1074,3 +1076,200 @@ TEST(EC, FunctionStorageOrder) EXPECT_EQ(5, v.at(4)); EXPECT_EQ(6, v.at(5)); } + +TEST(EC, forMatchingIterableFn) +{ + EC::Manager manager; + auto e0 = manager.addEntity(); + manager.addComponent(e0, 0, 1); + + auto e1 = manager.addEntity(); + manager.addComponent(e1, 2, 3); + manager.addTag(e1); + + auto e2 = manager.addEntity(); + manager.addComponent(e2, 4, 5); + manager.addTag(e2); + manager.addTag(e2); + + auto c0Index = EC::Meta::IndexOf::value; + auto c1Index = EC::Meta::IndexOf::value; + auto t0Index = EC::Meta::IndexOf::value; + auto t1Index = EC::Meta::IndexOf::value; + + { + // test valid indices + auto iterable = {c0Index}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto* c = m->getEntityComponent(i); + c->x += 1; + c->y += 1; + }; + manager.forMatchingIterable(iterable, fn, nullptr); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 1); + EXPECT_EQ(c->y, 2); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 3); + EXPECT_EQ(c->y, 4); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 5); + EXPECT_EQ(c->y, 6); + } + + { + // test invalid indices + auto iterable = {c0Index, c1Index}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto* c = m->getEntityComponent(i); + c->x += 1; + c->y += 1; + }; + manager.forMatchingIterable(iterable, fn, nullptr); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 1); + EXPECT_EQ(c->y, 2); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 3); + EXPECT_EQ(c->y, 4); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 5); + EXPECT_EQ(c->y, 6); + } + + { + // test partially valid indices + auto iterable = {c0Index, t1Index}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto* c = m->getEntityComponent(i); + c->x += 1; + c->y += 1; + }; + manager.forMatchingIterable(iterable, fn, nullptr); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 1); + EXPECT_EQ(c->y, 2); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 3); + EXPECT_EQ(c->y, 4); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 6); + EXPECT_EQ(c->y, 7); + } + + { + // test partially valid indices + auto iterable = {c0Index, t0Index}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto* c = m->getEntityComponent(i); + c->x += 10; + c->y += 10; + }; + manager.forMatchingIterable(iterable, fn, nullptr); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 1); + EXPECT_EQ(c->y, 2); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 13); + EXPECT_EQ(c->y, 14); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 16); + EXPECT_EQ(c->y, 17); + } + + { + // test invalid indices + auto iterable = {(unsigned int)c0Index, 1000u}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto* c = m->getEntityComponent(i); + c->x += 1000; + c->y += 1000; + }; + manager.forMatchingIterable(iterable, fn, nullptr); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 1); + EXPECT_EQ(c->y, 2); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 13); + EXPECT_EQ(c->y, 14); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 16); + EXPECT_EQ(c->y, 17); + } + + { + // test concurrent update + auto iterable = {c0Index}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto *c = m->getEntityComponent(i); + c->x += 100; + c->y += 100; + }; + manager.forMatchingIterable(iterable, fn, nullptr, 3); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 101); + EXPECT_EQ(c->y, 102); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 113); + EXPECT_EQ(c->y, 114); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 116); + EXPECT_EQ(c->y, 117); + } + + + { + // test invalid concurrent update + auto iterable = {(unsigned int)c0Index, 1000u}; + auto fn = [] (std::size_t i, decltype(manager)* m, void*) { + auto *c = m->getEntityComponent(i); + c->x += 1000; + c->y += 1000; + }; + manager.forMatchingIterable(iterable, fn, nullptr, 3); + } + + { + auto* c = manager.getEntityComponent(e0); + EXPECT_EQ(c->x, 101); + EXPECT_EQ(c->y, 102); + + c = manager.getEntityComponent(e1); + EXPECT_EQ(c->x, 113); + EXPECT_EQ(c->y, 114); + + c = manager.getEntityComponent(e2); + EXPECT_EQ(c->x, 116); + EXPECT_EQ(c->y, 117); + } +}