#pragma once #include #include #include #include #include #include #include #include #ifndef MAX_ZECSY_ENTITIES #define MAX_ZECSY_ENTITIES 65536 #endif // !MAX_ZECSY_ENTITIES namespace zecsy { using entity_id = uint64_t; class entity final { public: entity(class world* w, entity_id id); entity() = default; operator entity_id() const; bool is_alive() const; template bool has() const; template T& get(); template void set(); template void set(const T&... comps); template void remove(); private: entity_id id = 0; class world* w = nullptr; }; using zecsy_bits = std::bitset; class component_storage { public: template bool has(entity_id e) const; template bool has(entity_id e) const; template T& get(entity_id e); template void set(entity_id e); template void set(entity_id e, const T& comp); template void set(entity_id e); template void set(entity_id e, const First& comp0, const Second& comp1, const Rest&... rest_comps); template void remove(entity_id e); template void remove(entity_id e); private: using comp_index = std::type_index; using comp_to_bitset = std::unordered_map; using entity_to_index = std::unordered_map; using comp_to_entity_dict = std::unordered_map; using comp_to_storage = std::unordered_map>; using comp_to_reusable_ids = std::unordered_map>; comp_to_bitset bitset_dict; comp_to_entity_dict entity_dict; comp_to_storage storage_dict; comp_to_reusable_ids reusable_id_queues; }; class world final { public: entity make_entity(); void destroy_entity(entity_id e); bool is_alive(entity_id e) const; template bool has(entity_id e) const; template T& get(entity_id e); template void set(entity_id e); template void set(entity_id e, const T&... comps); template void remove(entity_id e); private: zecsy_bits entities_bitset; entity_id entity_counter = 0; component_storage storage; }; inline entity::entity(class world* w, entity_id id): w(w), id(id) { } inline entity::operator entity_id() const { return id; } inline bool entity::is_alive() const { return w && w->is_alive(id); } inline entity world::make_entity() { auto id = ++entity_counter; if(id > MAX_ZECSY_ENTITIES) { throw std::runtime_error(std::format("Entity id {} exceeds " "MAX_ZECSY_ENTITIES ({})", id, MAX_ZECSY_ENTITIES)); } entities_bitset.set(id); return entity(this, id); } inline void world::destroy_entity(entity_id e) { entities_bitset.reset(e); } inline bool world::is_alive(entity_id e) const { return entities_bitset.test(e); } template inline bool component_storage::has(entity_id e) const { if(bitset_dict.contains(typeid(T))) { return bitset_dict.at(typeid(T)).test(e); } return false; } template inline T& component_storage::get(entity_id e) { if(!has(e)) { throw std::runtime_error(std::format("Entity #{} doesn't have {}", e, typeid(T).name())); } auto index = entity_dict[typeid(T)].at(e); auto* ptr = reinterpret_cast(&storage_dict[typeid(T)][0]); ptr += index; return *ptr; } template inline bool entity::has() const { return w->has(id); } template inline T& world::get(entity_id e) { return storage.get(e); } template inline T& entity::get() { return w->get(id); } template inline void entity::set() { (set(T{}), ...); } template inline void component_storage::set(entity_id e, const T& comp) { bitset_dict[typeid(T)].set(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); entity_dict[typeid(T)][e] = old_size / T_size; return; } auto index = reusable_ids.front(); reusable_ids.pop(); auto ptr = reinterpret_cast(&storage[0]); new(ptr + index) T(comp); entity_dict[typeid(T)][e] = index; } template inline void world::set(entity_id e) { storage.set(e); } template inline void world::set(entity_id e, const T&... comps) { storage.set(e, comps...); } template inline void entity::set(const T&... comps) { w->set(id, comps...); } template inline void component_storage::remove(entity_id e) { if(!has(e)) { return; } bitset_dict[typeid(T)].reset(e); reusable_id_queues[typeid(T)].push(entity_dict[typeid(T)][e]); entity_dict[typeid(T)].erase(e); } template inline void world::remove(entity_id e) { storage.remove(e); } template inline void entity::remove() { w->remove(id); } template inline bool world::has(entity_id e) const { return storage.has(e); } template inline bool component_storage::has(entity_id e) const { return has(e) && has(e) && (has(e) && ...); } template inline void component_storage::set(entity_id e) { set(e, First{}); set(e, Second{}); (set(e, Rest{}), ...); } template 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 inline void component_storage::remove(entity_id e) { remove(e); remove(e); (remove(e), ...); } };