Prechádzať zdrojové kódy

Components moved and updated from core

Kajetan Johannes Hammerle 2 rokov pred
rodič
commit
9400ff091f

+ 139 - 0
data/Components.h

@@ -0,0 +1,139 @@
+#ifndef CORE_COMPONENTS_H
+#define CORE_COMPONENTS_H
+
+#include "data/HashMap.h"
+
+namespace Core {
+    using Entity = int;
+
+    template<typename T>
+    class Components final {
+        HashMap<Entity, int> entityToIndex;
+        List<Entity> indexToEntity;
+        List<T> components;
+
+    public:
+        template<typename R>
+        struct Node {
+            const Entity& entity;
+            R& component;
+        };
+
+        template<typename C, typename R>
+        class EntityIterator final {
+            C& components;
+            int index;
+
+        public:
+            EntityIterator(C& components_, int index_)
+                : components(components_), index(index_) {
+            }
+
+            EntityIterator& operator++() {
+                index++;
+                return *this;
+            }
+
+            bool operator!=(const EntityIterator& other) const {
+                return index != other.index;
+            }
+
+            Node<R> operator*() const {
+                return {components.indexToEntity[index],
+                        components.components[index]};
+            }
+        };
+
+        template<typename C, typename R>
+        struct EntityIteratorAdapter {
+            C& components;
+
+            EntityIterator<C, R> begin() {
+                return EntityIterator<C, R>(components, 0);
+            }
+
+            EntityIterator<C, R> end() {
+                return EntityIterator<C, R>(components,
+                                            components.components.getLength());
+            }
+        };
+
+        // returns a nullptr on error
+        template<typename... Args>
+        check_return T* add(Entity e, Args&&... args) {
+            int index = components.getLength();
+            if(entityToIndex.tryEmplace(e, index) == nullptr) {
+                return nullptr;
+            } else if(indexToEntity.add(e) == nullptr) {
+                (void)entityToIndex.remove(e);
+                return nullptr;
+            }
+            T* c = components.add(Core::forward<Args>(args)...);
+            if(c == nullptr) {
+                (void)entityToIndex.remove(e);
+                (void)indexToEntity.removeLast();
+            }
+            return c;
+        }
+
+        // returns true on error
+        check_return bool remove(Entity e) {
+            int* indexP = entityToIndex.search(e);
+            if(indexP == nullptr) {
+                return true;
+            }
+            int lastIndex = components.getLength() - 1;
+            int index = *indexP;
+            bool r = entityToIndex.remove(e);
+            r |= components.removeBySwap(index);
+            if(index == lastIndex) {
+                return r | indexToEntity.removeBySwap(index);
+            }
+            Entity other = indexToEntity[lastIndex];
+            r |= indexToEntity.removeBySwap(index);
+            return r | (entityToIndex.add(other, index) == nullptr);
+        }
+
+        T* search(Entity e) {
+            int* index = entityToIndex.search(e);
+            if(index == nullptr) {
+                return nullptr;
+            }
+            return &(components[*index]);
+        }
+
+        const T* search(Entity e) const {
+            const int* index = entityToIndex.search(e);
+            if(index == nullptr) {
+                return nullptr;
+            }
+            return &(components[*index]);
+        }
+
+        auto begin() {
+            return components.begin();
+        }
+
+        auto begin() const {
+            return components.begin();
+        }
+
+        auto end() {
+            return components.end();
+        }
+
+        auto end() const {
+            return components.end();
+        }
+
+        EntityIteratorAdapter<Components, T> entities() {
+            return {*this};
+        }
+
+        EntityIteratorAdapter<const Components, const T> entities() const {
+            return {*this};
+        }
+    };
+}
+
+#endif

+ 3 - 3
data/HashMap.h

@@ -193,7 +193,7 @@ namespace Core {
             return &(np->data.value);
         }
 
-        // returns true when a value was removed
+        // returns true on error
         check_return bool remove(const K& key) {
             NodePointerList& list = nodePointers[hashIndex(key)];
             for(int i = 0; i < list.getLength(); i++) {
@@ -201,10 +201,10 @@ namespace Core {
                     NodePointer np = list[i];
                     bool r = list.removeBySwap(i);
                     nodes.remove(np);
-                    return !r;
+                    return r;
                 }
             }
-            return false;
+            return true;
         }
 
         const V* search(const K& key) const {

+ 5 - 0
data/List.h

@@ -193,6 +193,11 @@ namespace Core {
             return false;
         }
 
+        // returns true on error
+        check_return bool removeLast() {
+            return removeBySwap(length - 1);
+        }
+
         // returns true on error
         template<int L>
         check_return bool toString(ArrayString<L>& s) const {

+ 1 - 0
meson.build

@@ -22,6 +22,7 @@ src_tests = [
     'tests/HashMapTests.cpp',
     'tests/StackTests.cpp',
     'tests/RingBufferTests.cpp',
+    'tests/ComponentsTests.cpp',
 ]
 
 compiler = meson.get_compiler('cpp')

+ 2 - 0
test/Main.cpp

@@ -5,6 +5,7 @@
 #include "tests/ArrayStringTests.h"
 #include "tests/ArrayTests.h"
 #include "tests/BitArrayTests.h"
+#include "tests/ComponentsTests.h"
 #include "tests/HashMapTests.h"
 #include "tests/LinkedListTests.h"
 #include "tests/ListTests.h"
@@ -25,6 +26,7 @@ int main() {
     Core::ArrayStringTests::test();
     Core::ArrayTests::test();
     Core::BitArrayTests::test();
+    Core::ComponentsTests::test();
     Core::HashMapTests::test();
     Core::LinkedListTests::test();
     Core::ListTests::test();

+ 152 - 0
tests/ComponentsTests.cpp

@@ -0,0 +1,152 @@
+#include "tests/ComponentsTests.h"
+
+#include "data/Components.h"
+#include "test/Test.h"
+
+using IntComponent = Core::Components<int>;
+
+static void testAddForEach() {
+    IntComponent c;
+    int* i1 = c.add(1, 10);
+    int* i2 = c.add(5, 20);
+    int* i3 = c.add(10, 30);
+
+    CORE_TEST_NOT_NULL(i1);
+    CORE_TEST_NOT_NULL(i2);
+    CORE_TEST_NOT_NULL(i3);
+    if(i1 != nullptr && i2 != nullptr && i3 != nullptr) {
+        CORE_TEST_EQUAL(10, *i1);
+        CORE_TEST_EQUAL(20, *i2);
+        CORE_TEST_EQUAL(30, *i3);
+    }
+
+    auto iter = c.entities();
+    auto pos = iter.begin();
+    auto end = iter.end();
+
+    CORE_TEST_EQUAL(1, (*pos).entity);
+    CORE_TEST_EQUAL(10, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_EQUAL(5, (*pos).entity);
+    CORE_TEST_EQUAL(20, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_EQUAL(10, (*pos).entity);
+    CORE_TEST_EQUAL(30, (*pos).component);
+    CORE_TEST_TRUE(pos != end);
+
+    ++pos;
+
+    CORE_TEST_FALSE(pos != end);
+}
+
+static void testAddComponentForEach() {
+    IntComponent c;
+    int* i1 = c.add(1, 10);
+    int* i2 = c.add(5, 20);
+    int* i3 = c.add(10, 30);
+
+    CORE_TEST_NOT_NULL(i1);
+    CORE_TEST_NOT_NULL(i2);
+    CORE_TEST_NOT_NULL(i3);
+    if(i1 != nullptr && i2 != nullptr && i3 != nullptr) {
+        CORE_TEST_EQUAL(10, *i1);
+        CORE_TEST_EQUAL(20, *i2);
+        CORE_TEST_EQUAL(30, *i3);
+    }
+
+    auto iter = c.begin();
+    CORE_TEST_EQUAL(10, *iter);
+    CORE_TEST_TRUE(iter != c.end());
+
+    ++iter;
+
+    CORE_TEST_EQUAL(20, *iter);
+    CORE_TEST_TRUE(iter != c.end());
+
+    ++iter;
+
+    CORE_TEST_EQUAL(30, *iter);
+    CORE_TEST_TRUE(iter != c.end());
+
+    ++iter;
+
+    CORE_TEST_FALSE(iter != c.end());
+
+    const IntComponent c2 = Core::move(c);
+    auto iter2 = c2.begin();
+    CORE_TEST_EQUAL(10, *iter2);
+    CORE_TEST_TRUE(iter2 != c2.end());
+
+    ++iter2;
+
+    CORE_TEST_EQUAL(20, *iter2);
+    CORE_TEST_TRUE(iter2 != c2.end());
+
+    ++iter2;
+
+    CORE_TEST_EQUAL(30, *iter2);
+    CORE_TEST_TRUE(iter2 != c2.end());
+
+    ++iter2;
+
+    CORE_TEST_FALSE(iter2 != c2.end());
+}
+
+static void testRemove() {
+    IntComponent c;
+    CORE_TEST_NOT_NULL(c.add(1, 10));
+    CORE_TEST_NOT_NULL(c.add(5, 20));
+    CORE_TEST_NOT_NULL(c.add(10, 30));
+
+    CORE_TEST_TRUE(c.remove(20));
+    CORE_TEST_FALSE(c.remove(5));
+    CORE_TEST_TRUE(c.remove(30));
+
+    CORE_TEST_NOT_NULL(c.add(20, 40));
+    CORE_TEST_FALSE(c.remove(20));
+
+    int* i1 = c.search(1);
+    int* i2 = c.search(5);
+    int* i3 = c.search(10);
+
+    CORE_TEST_NOT_NULL(i1);
+    CORE_TEST_NULL(i2);
+    CORE_TEST_NOT_NULL(i3);
+
+    if(i1 != nullptr && i3 != nullptr) {
+        CORE_TEST_EQUAL(10, *i1);
+        CORE_TEST_EQUAL(30, *i3);
+    }
+
+    CORE_TEST_FALSE(c.remove(10));
+
+    i1 = c.search(1);
+    i2 = c.search(5);
+    i3 = c.search(10);
+
+    CORE_TEST_NOT_NULL(i1);
+    CORE_TEST_NULL(i2);
+    CORE_TEST_NULL(i3);
+
+    if(i1 != nullptr) {
+        CORE_TEST_EQUAL(10, *i1);
+    }
+
+    CORE_TEST_FALSE(c.remove(1));
+
+    CORE_TEST_NULL(c.search(1));
+    CORE_TEST_NULL(c.search(5));
+    CORE_TEST_NULL(c.search(10));
+}
+
+void Core::ComponentsTests::test() {
+    testAddForEach();
+    testAddComponentForEach();
+    testRemove();
+}

+ 8 - 0
tests/ComponentsTests.h

@@ -0,0 +1,8 @@
+#ifndef CORE_COMPONENTS_TESTS_H
+#define CORE_COMPONENTS_TESTS_H
+
+namespace Core::ComponentsTests {
+    void test();
+}
+
+#endif

+ 2 - 2
tests/HashMapTests.cpp

@@ -229,8 +229,8 @@ static void testRemove() {
     CORE_TEST_NULL(b);
     CORE_TEST_NOT_NULL(c);
 
-    CORE_TEST_TRUE(remove1);
-    CORE_TEST_FALSE(remove2);
+    CORE_TEST_FALSE(remove1);
+    CORE_TEST_TRUE(remove2);
 
     if(a != nullptr && c != nullptr) {
         CORE_TEST_EQUAL(3, *a);