summary refs log tree commit diff stats
path: root/src/entity_manager.h
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2018-05-17 15:55:37 -0400
committerGitHub <noreply@github.com>2018-05-17 15:55:37 -0400
commit90aadf3844386824140a20d7fbb847bc16009a94 (patch)
tree6f83fce90e71abb22b1a8f3e09c79963b2a34d5d /src/entity_manager.h
parentbc63fa57ced1c7329f7fdcfd168eaf7e290158bc (diff)
parent86f0106d0523825549f1e74b835688c78a10cf6c (diff)
downloadtherapy-90aadf3844386824140a20d7fbb847bc16009a94.tar.gz
therapy-90aadf3844386824140a20d7fbb847bc16009a94.tar.bz2
therapy-90aadf3844386824140a20d7fbb847bc16009a94.zip
Merge pull request #7 from hatkirby/es-rewrite
The ECS rewrite exceeds the original branch in functionality, so it is time to merge it in.
Diffstat (limited to 'src/entity_manager.h')
-rw-r--r--src/entity_manager.h230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/entity_manager.h b/src/entity_manager.h new file mode 100644 index 0000000..b2ef0de --- /dev/null +++ b/src/entity_manager.h
@@ -0,0 +1,230 @@
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 "util.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::set<id_type> allEntities;
30
31 mutable std::map<std::set<std::type_index>, std::set<id_type>>
32 cachedComponents;
33
34 id_type nextEntityID = 0;
35
36 template <class T, class... R>
37 std::set<id_type> getEntitiesWithComponentsHelper(
38 std::set<std::type_index>& componentTypes) const
39 {
40 componentTypes.insert(typeid(T));
41
42 return getEntitiesWithComponents<R...>(componentTypes);
43 }
44
45 template <class... R>
46 std::set<id_type> getEntitiesWithComponents(
47 std::set<std::type_index>& componentTypes) const
48 {
49 return getEntitiesWithComponentsHelper<R...>(componentTypes);
50 }
51
52public:
53
54 EntityManager() = default;
55
56 EntityManager(const EntityManager& copy) = delete;
57
58 id_type emplaceEntity()
59 {
60 if (nextEntityID >= entities.size())
61 {
62 // If the database is saturated, add a new element for the new entity.
63 entities.emplace_back();
64 slotAvailable.push_back(false);
65 allEntities.insert(nextEntityID);
66
67 return nextEntityID++;
68 } else {
69 // If there is an available slot in the database, use it.
70 id_type id = nextEntityID++;
71 slotAvailable[id] = false;
72 allEntities.insert(id);
73
74 // Fast forward the next available slot pointer to an available slot.
75 while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID])
76 {
77 nextEntityID++;
78 }
79
80 return id;
81 }
82 }
83
84 void deleteEntity(id_type entity)
85 {
86 if ((entity >= entities.size()) || slotAvailable[entity])
87 {
88 throw std::invalid_argument("Cannot delete non-existent entity");
89 }
90
91 // Uncache components
92 for (auto& cache : cachedComponents)
93 {
94 cache.second.erase(entity);
95 }
96
97 allEntities.erase(entity);
98
99 // Destroy the data
100 entities[entity].components.clear();
101
102 // Mark the slot as available
103 slotAvailable[entity] = true;
104
105 if (entity < nextEntityID)
106 {
107 nextEntityID = entity;
108 }
109 }
110
111 template <class T, class... Args>
112 T& emplaceComponent(id_type entity, Args&&... args)
113 {
114 if ((entity >= entities.size()) || slotAvailable[entity])
115 {
116 throw std::invalid_argument("Cannot get non-existent entity");
117 }
118
119 EntityData& data = entities[entity];
120 std::type_index componentType = typeid(T);
121
122 if (data.components.count(componentType))
123 {
124 throw std::invalid_argument("Cannot emplace already-existent component");
125 }
126
127 // Initialize the component
128 std::unique_ptr<T> ptr(new T(std::forward<Args>(args)...));
129 T& component = *ptr;
130 data.components[componentType] = std::move(ptr);
131
132 // Invalidate related caches
133 erase_if(
134 cachedComponents,
135 [&componentType] (
136 std::pair<const std::set<std::type_index>, std::set<id_type>>& cache) {
137 return cache.first.count(componentType) == 1;
138 });
139
140 return component;
141 }
142
143 template <class T>
144 void removeComponent(id_type entity)
145 {
146 if ((entity >= entities.size()) || slotAvailable[entity])
147 {
148 throw std::invalid_argument("Cannot get non-existent entity");
149 }
150
151 EntityData& data = entities[entity];
152 std::type_index componentType = typeid(T);
153
154 if (!data.components.count(componentType))
155 {
156 throw std::invalid_argument("Cannot delete non-existent component");
157 }
158
159 // Destroy the component
160 data.components.erase(componentType);
161
162 // Uncache the component
163 for (auto& cache : cachedComponents)
164 {
165 if (cache.first.count(componentType) == 1)
166 {
167 cache.second.erase(entity);
168 }
169 }
170 }
171
172 template <class T>
173 const T& getComponent(id_type entity) const
174 {
175 if ((entity >= entities.size()) || slotAvailable[entity])
176 {
177 throw std::invalid_argument("Cannot get non-existent entity");
178 }
179
180 const EntityData& data = entities[entity];
181 std::type_index componentType = typeid(T);
182
183 if (!data.components.count(componentType))
184 {
185 throw std::invalid_argument("Cannot get non-existent component");
186 }
187
188 return *dynamic_cast<const T*>(data.components.at(componentType).get());
189 }
190
191 template <class T>
192 T& getComponent(id_type entity)
193 {
194 return const_cast<T&>(
195 static_cast<const EntityManager&>(*this).getComponent<T>(entity));
196 }
197
198 template <class T>
199 bool hasComponent(id_type entity) const
200 {
201 if ((entity >= entities.size()) || slotAvailable[entity])
202 {
203 throw std::invalid_argument("Cannot get non-existent entity");
204 }
205
206 const EntityData& data = entities[entity];
207 std::type_index componentType = typeid(T);
208
209 return data.components.count(componentType);
210 }
211
212 template <class... R>
213 std::set<id_type> getEntitiesWithComponents() const
214 {
215 std::set<std::type_index> componentTypes;
216
217 return getEntitiesWithComponentsHelper<R...>(componentTypes);
218 }
219
220 const std::set<id_type>& getEntities() const
221 {
222 return allEntities;
223 }
224};
225
226template <>
227std::set<EntityManager::id_type> EntityManager::getEntitiesWithComponents<>(
228 std::set<std::type_index>& componentTypes) const;
229
230#endif /* end of include guard: ENTITY_MANAGER_H_C5832F11 */