Bye-bye typeid
This commit is contained in:
		
							parent
							
								
									7d9e0ddf18
								
							
						
					
					
						commit
						71da59cd75
					
				
					 1 changed files with 152 additions and 158 deletions
				
			
		
							
								
								
									
										316
									
								
								zecsy.hpp
									
									
									
									
									
								
							
							
						
						
									
										316
									
								
								zecsy.hpp
									
									
									
									
									
								
							|  | @ -7,18 +7,16 @@ | ||||||
| #include <queue> | #include <queue> | ||||||
| #include <set> | #include <set> | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
| #include <typeindex> |  | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  | #include <vector> | ||||||
| 
 | 
 | ||||||
| namespace zecsy | namespace zecsy | ||||||
| { | { | ||||||
|     using entity_id = uint64_t; |     using entity_id = uint64_t; | ||||||
| 
 | 
 | ||||||
|     /*
 |     /*
 | ||||||
|      * unordered_set is also an option. But probably sorting ids may be |      * Using std::set for entities_set to maintain sorted order, which can | ||||||
|      * beneficial for queries, because it might increase the chance of reusing |      * improve cache locality during queries and iterations. | ||||||
|      * cpu cache (aka "required components were close to each other"). |  | ||||||
|      * Definitely should play around with it |  | ||||||
|      */ |      */ | ||||||
|     using entities_set = std::set<entity_id>; |     using entities_set = std::set<entity_id>; | ||||||
| 
 | 
 | ||||||
|  | @ -54,26 +52,132 @@ namespace zecsy | ||||||
|         void remove(entity_id e); |         void remove(entity_id e); | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         using comp_index = std::type_index; |         using comp_id = size_t; | ||||||
|         using comp_to_entities_set = |  | ||||||
|             std::unordered_map<comp_index, entities_set>; |  | ||||||
|         using entity_to_index = std::unordered_map<entity_id, size_t>; |  | ||||||
| 
 | 
 | ||||||
|         using comp_to_entity_dict = |         struct component_pool | ||||||
|             std::unordered_map<comp_index, entity_to_index>; |         { | ||||||
| 
 |             std::vector<uint8_t> data; | ||||||
|         using comp_to_storage = |             std::queue<size_t> free_list; // Reusable indices
 | ||||||
|             std::unordered_map<comp_index, std::vector<uint8_t>>; |             std::unordered_map<entity_id, size_t> entity_to_index; | ||||||
| 
 |             std::unordered_map<size_t, entity_id> index_to_entity; | ||||||
|         using comp_to_reusable_ids = |  | ||||||
|             std::unordered_map<comp_index, std::queue<size_t>>; |  | ||||||
| 
 |  | ||||||
|         comp_to_entities_set entities_dict; |  | ||||||
|         comp_to_entity_dict indices_dict; |  | ||||||
|         comp_to_storage storage_dict; |  | ||||||
|         comp_to_reusable_ids reusable_id_queues; |  | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|  |         std::unordered_map<comp_id, component_pool> pools; | ||||||
|  | 
 | ||||||
|  |         template<typename T> | ||||||
|  |         static comp_id get_component_id() | ||||||
|  |         { | ||||||
|  |             static comp_id id = next_component_id++; | ||||||
|  |             return id; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         static comp_id next_component_id; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     inline component_storage::comp_id component_storage::next_component_id = 0; | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     inline bool component_storage::has(entity_id e) const | ||||||
|  |     { | ||||||
|  |         auto id = get_component_id<T>(); | ||||||
|  |         if(pools.contains(id)) | ||||||
|  |         { | ||||||
|  |             return pools.at(id).entity_to_index.contains(e); | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     inline T& component_storage::get(entity_id e) | ||||||
|  |     { | ||||||
|  |         auto id = get_component_id<T>(); | ||||||
|  |         if(!has<T>(e)) | ||||||
|  |         { | ||||||
|  |             throw std::runtime_error( | ||||||
|  |                 std::format("Entity #{} doesn't have {}", e, typeid(T).name())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto& pool = pools.at(id); | ||||||
|  |         auto index = pool.entity_to_index.at(e); | ||||||
|  |         return *reinterpret_cast<T*>(&pool.data[index * sizeof(T)]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     inline void component_storage::set(entity_id e, const T& comp) | ||||||
|  |     { | ||||||
|  |         auto id = get_component_id<T>(); | ||||||
|  |         auto& pool = pools[id]; | ||||||
|  | 
 | ||||||
|  |         size_t index; | ||||||
|  |         if(!pool.free_list.empty()) | ||||||
|  |         { | ||||||
|  |             index = pool.free_list.front(); | ||||||
|  |             pool.free_list.pop(); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             index = pool.data.size() / sizeof(T); | ||||||
|  |             pool.data.resize(pool.data.size() + sizeof(T)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         new(&pool.data[index * sizeof(T)]) T(comp); | ||||||
|  |         pool.entity_to_index[e] = index; | ||||||
|  |         pool.index_to_entity[index] = e; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     inline void component_storage::set(entity_id e) | ||||||
|  |     { | ||||||
|  |         set(e, T{}); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     inline void component_storage::remove(entity_id e) | ||||||
|  |     { | ||||||
|  |         auto id = get_component_id<T>(); | ||||||
|  |         if(!has<T>(e)) | ||||||
|  |         { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto& pool = pools[id]; | ||||||
|  |         auto index = pool.entity_to_index[e]; | ||||||
|  |         pool.free_list.push(index); | ||||||
|  |         pool.entity_to_index.erase(e); | ||||||
|  |         pool.index_to_entity.erase(index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename First, typename Second, typename... Rest> | ||||||
|  |     inline bool component_storage::has(entity_id e) const | ||||||
|  |     { | ||||||
|  |         return has<First>(e) && has<Second>(e) && (has<Rest>(e) && ...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename First, typename Second, typename... Rest> | ||||||
|  |     inline void component_storage::set(entity_id e) | ||||||
|  |     { | ||||||
|  |         set(e, First{}); | ||||||
|  |         set(e, Second{}); | ||||||
|  |         (set(e, Rest{}), ...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename First, typename Second, typename... Rest> | ||||||
|  |     inline void component_storage::set(entity_id e, const First& comp0, | ||||||
|  |                                 const Second& comp1, const Rest&... rest_comps) | ||||||
|  |     { | ||||||
|  |         set(e, comp0); | ||||||
|  |         set(e, comp1); | ||||||
|  |         (set(e, rest_comps), ...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template<typename First, typename Second, typename... Rest> | ||||||
|  |     inline void component_storage::remove(entity_id e) | ||||||
|  |     { | ||||||
|  |         remove<First>(e); | ||||||
|  |         remove<Second>(e); | ||||||
|  |         (remove<Rest>(e), ...); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     class system_scheduler final |     class system_scheduler final | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|  | @ -94,6 +198,32 @@ namespace zecsy | ||||||
|         std::vector<system_handler> systems; |         std::vector<system_handler> systems; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     inline void system_scheduler::add_system(float freq, auto&& func) | ||||||
|  |     { | ||||||
|  |         systems.emplace_back(1.0f / freq, 0.0f, | ||||||
|  |                              std::forward<decltype(func)>(func)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     inline void system_scheduler::add_system(int freq, auto&& func) | ||||||
|  |     { | ||||||
|  |         add_system(float(freq), func); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     inline void system_scheduler::update(float dt) | ||||||
|  |     { | ||||||
|  |         dt = std::max(0.0f, dt); | ||||||
|  | 
 | ||||||
|  |         for(auto& s: systems) | ||||||
|  |         { | ||||||
|  |             s.accumulator += dt; | ||||||
|  |             while(s.accumulator >= s.interval) | ||||||
|  |             { | ||||||
|  |                 s.callback(dt); | ||||||
|  |                 s.accumulator -= s.interval; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     class world final |     class world final | ||||||
|     { |     { | ||||||
|     public: |     public: | ||||||
|  | @ -129,7 +259,6 @@ namespace zecsy | ||||||
|     { |     { | ||||||
|         auto id = ++entity_counter; |         auto id = ++entity_counter; | ||||||
|         alive_entities.emplace(id); |         alive_entities.emplace(id); | ||||||
| 
 |  | ||||||
|         return id; |         return id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -143,31 +272,10 @@ namespace zecsy | ||||||
|         return alive_entities.contains(e); |         return alive_entities.contains(e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<typename T> |     template<typename... T> | ||||||
|     inline bool component_storage::has(entity_id e) const |     inline bool world::has(entity_id e) const | ||||||
|     { |     { | ||||||
|         if(entities_dict.contains(typeid(T))) |         return storage.has<T...>(e); | ||||||
|         { |  | ||||||
|             return entities_dict.at(typeid(T)).contains(e); |  | ||||||
|         } |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename T> |  | ||||||
|     inline T& component_storage::get(entity_id e) |  | ||||||
|     { |  | ||||||
|         if(!has<T>(e)) |  | ||||||
|         { |  | ||||||
|             throw std::runtime_error( |  | ||||||
|                 std::format("Entity #{} doesn't have {}", e, typeid(T).name())); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         auto index = indices_dict[typeid(T)].at(e); |  | ||||||
| 
 |  | ||||||
|         auto* ptr = reinterpret_cast<T*>(&storage_dict[typeid(T)][0]); |  | ||||||
|         ptr += index; |  | ||||||
| 
 |  | ||||||
|         return *ptr; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<typename T> |     template<typename T> | ||||||
|  | @ -176,43 +284,6 @@ namespace zecsy | ||||||
|         return storage.get<T>(e); |         return storage.get<T>(e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<typename T> |  | ||||||
|     inline void component_storage::set(entity_id e, const T& comp) |  | ||||||
|     { |  | ||||||
|         entities_dict[typeid(T)].emplace(e); |  | ||||||
| 
 |  | ||||||
|         auto& storage = storage_dict[typeid(T)]; |  | ||||||
| 
 |  | ||||||
|         auto& reusable_ids = reusable_id_queues[typeid(T)]; |  | ||||||
| 
 |  | ||||||
|         if(reusable_ids.empty()) |  | ||||||
|         { |  | ||||||
|             size_t T_size = sizeof(T); |  | ||||||
|             size_t old_size = storage.size(); |  | ||||||
| 
 |  | ||||||
|             storage.resize(old_size + T_size); |  | ||||||
|             void* ptr = &storage[0] + old_size; |  | ||||||
|             new(ptr) T(comp); |  | ||||||
| 
 |  | ||||||
|             indices_dict[typeid(T)][e] = old_size / T_size; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         auto index = reusable_ids.front(); |  | ||||||
|         reusable_ids.pop(); |  | ||||||
| 
 |  | ||||||
|         auto ptr = reinterpret_cast<T*>(&storage[0]); |  | ||||||
|         new(ptr + index) T(comp); |  | ||||||
| 
 |  | ||||||
|         indices_dict[typeid(T)][e] = index; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename T> |  | ||||||
|     inline void component_storage::set(entity_id e) |  | ||||||
|     { |  | ||||||
|         set(e, T{}); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename... T> |     template<typename... T> | ||||||
|     inline void world::set(entity_id e) |     inline void world::set(entity_id e) | ||||||
|     { |     { | ||||||
|  | @ -225,63 +296,12 @@ namespace zecsy | ||||||
|         storage.set(e, comps...); |         storage.set(e, comps...); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<typename T> |  | ||||||
|     inline void component_storage::remove(entity_id e) |  | ||||||
|     { |  | ||||||
|         if(!has<T>(e)) |  | ||||||
|         { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         entities_dict[typeid(T)].erase(e); |  | ||||||
|         reusable_id_queues[typeid(T)].push(indices_dict[typeid(T)][e]); |  | ||||||
|         indices_dict[typeid(T)].erase(e); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename... T> |     template<typename... T> | ||||||
|     inline void world::remove(entity_id e) |     inline void world::remove(entity_id e) | ||||||
|     { |     { | ||||||
|         storage.remove<T...>(e); |         storage.remove<T...>(e); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     template<typename... T> |  | ||||||
|     inline bool world::has(entity_id e) const |  | ||||||
|     { |  | ||||||
|         return storage.has<T...>(e); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename First, typename Second, typename... Rest> |  | ||||||
|     inline bool component_storage::has(entity_id e) const |  | ||||||
|     { |  | ||||||
|         return has<First>(e) && has<Second>(e) && (has<Rest>(e) && ...); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename First, typename Second, typename... Rest> |  | ||||||
|     inline void component_storage::set(entity_id e) |  | ||||||
|     { |  | ||||||
|         set(e, First{}); |  | ||||||
|         set(e, Second{}); |  | ||||||
|         (set(e, Rest{}), ...); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename First, typename Second, typename... Rest> |  | ||||||
|     inline void component_storage::set(entity_id e, const First& comp0, |  | ||||||
|                                        const Second& comp1, |  | ||||||
|                                        const Rest&... rest_comps) |  | ||||||
|     { |  | ||||||
|         set(e, comp0); |  | ||||||
|         set(e, comp1); |  | ||||||
|         (set(e, rest_comps), ...); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename First, typename Second, typename... Rest> |  | ||||||
|     inline void component_storage::remove(entity_id e) |  | ||||||
|     { |  | ||||||
|         remove<First>(e); |  | ||||||
|         remove<Second>(e); |  | ||||||
|         (remove<Rest>(e), ...); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template<typename... T> |     template<typename... T> | ||||||
|     inline void world::query(auto&& system) |     inline void world::query(auto&& system) | ||||||
|     { |     { | ||||||
|  | @ -293,30 +313,4 @@ namespace zecsy | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     inline void system_scheduler::add_system(float freq, auto&& func) |  | ||||||
|     { |  | ||||||
|         systems.emplace_back(1.0f / freq, 0.0f, |  | ||||||
|                              std::forward<decltype(func)>(func)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     inline void system_scheduler::add_system(int freq, auto&& func) |  | ||||||
|     { |  | ||||||
|         add_system(float(freq), func); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     inline void system_scheduler::update(float dt) |  | ||||||
|     { |  | ||||||
|         dt = std::max(0.0f, dt); |  | ||||||
| 
 |  | ||||||
|         for(auto& s: systems) |  | ||||||
|         { |  | ||||||
|             s.accumulator += dt; |  | ||||||
|             while(s.accumulator >= s.interval) |  | ||||||
|             { |  | ||||||
|                 s.callback(dt); |  | ||||||
|                 s.accumulator -= s.interval; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| }; // namespace zecsy
 | }; // namespace zecsy
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue