summary refs log tree commit diff stats
path: root/src/entity_manager.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/entity_manager.h')
-rw-r--r--src/entity_manager.h197
1 files changed, 197 insertions, 0 deletions
diff --git a/src/entity_manager.h b/src/entity_manager.h new file mode 100644 index 0000000..9068fe3 --- /dev/null +++ b/src/entity_manager.h
@@ -0,0 +1,197 @@
1#ifndef ENTITY_MANAGER_H_C5832F11
2#define ENTITY_MANAGER_H_C5832F11
3
4#include <map>
5#include <vector>
6#include <typeindex>
7#include <set>
8#include <stdexcept>
9#include "component.h"
10#include "algorithms.h"
11
12class EntityManager {
13private:
14
15 struct EntityData {
16 std::map<std::type_index, std::unique_ptr<Component>> components;
17 };
18
19 using database_type = std::vector<EntityData>;
20
21public:
22
23 using id_type = database_type::size_type;
24
25private:
26
27 database_type entities;
28 std::vector<bool> slotAvailable;
29 std::map<std::set<std::type_index>, std::set<id_type>> cachedComponents;
30
31 id_type nextEntityID = 0;
32
33 template <class T, class... R>
34 std::set<id_type> getEntitiesWithComponentsHelper(
35 std::set<std::type_index>& componentTypes)
36 {
37 componentTypes.insert(typeid(T));
38
39 return getEntitiesWithComponents<R...>(componentTypes);
40 }
41
42 template <class... R>
43 std::set<id_type> getEntitiesWithComponents(
44 std::set<std::type_index>& componentTypes)
45 {
46 return getEntitiesWithComponentsHelper<R...>(componentTypes);
47 }
48
49public:
50
51 EntityManager() = default;
52
53 EntityManager(const EntityManager& copy) = delete;
54
55 id_type emplaceEntity()
56 {
57 if (nextEntityID >= entities.size())
58 {
59 // If the database is saturated, add a new element for the new entity.
60 entities.emplace_back();
61 slotAvailable.push_back(false);
62
63 return nextEntityID++;
64 } else {
65 // If there is an available slot in the database, use it.
66 id_type id = nextEntityID++;
67 slotAvailable[id] = false;
68
69 // Fast forward the next available slot pointer to an available slot.
70 while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID])
71 {
72 nextEntityID++;
73 }
74
75 return id;
76 }
77 }
78
79 void deleteEntity(id_type entity)
80 {
81 if ((entity >= entities.size()) || slotAvailable[entity])
82 {
83 throw std::invalid_argument("Cannot delete non-existent entity");
84 }
85
86 // Uncache components
87 for (auto& cache : cachedComponents)
88 {
89 cache.second.erase(entity);
90 }
91
92 // Destroy the data
93 entities[entity].components.clear();
94
95 // Mark the slot as available
96 slotAvailable[entity] = true;
97
98 if (entity < nextEntityID)
99 {
100 nextEntityID = entity;
101 }
102 }
103
104 template <class T, class... Args>
105 T& emplaceComponent(id_type entity, Args&&... args)
106 {
107 if ((entity >= entities.size()) || slotAvailable[entity])
108 {
109 throw std::invalid_argument("Cannot delete non-existent entity");
110 }
111
112 EntityData& data = entities[entity];
113 std::type_index componentType = typeid(T);
114
115 if (data.components.count(componentType))
116 {
117 throw std::invalid_argument("Cannot emplace already-existent component");
118 }
119
120 // Initialize the component
121 std::unique_ptr<T> ptr(new T(std::forward<Args>(args)...));
122 T& component = *ptr;
123 data.components[componentType] = std::move(ptr);
124
125 // Invalidate related caches
126 erase_if(
127 cachedComponents,
128 [&componentType] (
129 std::pair<const std::set<std::type_index>, std::set<id_type>>& cache) {
130 return cache.first.count(componentType) == 1;
131 });
132
133 return component;
134 }
135
136 template <class T>
137 void removeComponent(id_type entity)
138 {
139 if ((entity >= entities.size()) || slotAvailable[entity])
140 {
141 throw std::invalid_argument("Cannot delete non-existent entity");
142 }
143
144 EntityData& data = entities[entity];
145 std::type_index componentType = typeid(T);
146
147 if (!data.components.count(componentType))
148 {
149 throw std::invalid_argument("Cannot delete non-existent component");
150 }
151
152 // Destroy the component
153 data.components.erase(componentType);
154
155 // Uncache the component
156 for (auto& cache : cachedComponents)
157 {
158 if (cache.first.count(componentType) == 1)
159 {
160 cache.second.erase(entity);
161 }
162 }
163 }
164
165 template <class T>
166 T& getComponent(id_type entity)
167 {
168 if ((entity >= entities.size()) || slotAvailable[entity])
169 {
170 throw std::invalid_argument("Cannot delete non-existent entity");
171 }
172
173 EntityData& data = entities[entity];
174 std::type_index componentType = typeid(T);
175
176 if (!data.components.count(componentType))
177 {
178 throw std::invalid_argument("Cannot get non-existent component");
179 }
180
181 return *dynamic_cast<T*>(data.components[componentType].get());
182 }
183
184 template <class... R>
185 std::set<id_type> getEntitiesWithComponents()
186 {
187 std::set<std::type_index> componentTypes;
188
189 return getEntitiesWithComponentsHelper<R...>(componentTypes);
190 }
191};
192
193template <>
194std::set<EntityManager::id_type> EntityManager::getEntitiesWithComponents<>(
195 std::set<std::type_index>& componentTypes);
196
197#endif /* end of include guard: ENTITY_MANAGER_H_C5832F11 */