#pragma once #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; entity(const entity &) = default; entity(entity &&) = default; entity &operator=(const entity &) = default; entity &operator=(entity &&) = default; operator entity_id() const; bool is_alive() const; template bool has() const; template T& get(); template void set(const T& comp = T{}); 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 T& get(entity_id e); template void set(entity_id e, const T& comp = T{}); 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>; comp_to_bitset bitset_dict; comp_to_entity_dict entity_dict; comp_to_storage storage_dict; }; class world final { public: world() = default; world(const world &) = default; world(world &&) = default; world &operator=(const world &) = default; world &operator=(world &&) = default; 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, const T& comp = T{}); 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 world::has(entity_id e) const { return storage.has(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 component_storage::set(entity_id e, const T& comp) { bitset_dict[typeid(T)].set(e); auto& storage = storage_dict[typeid(T)]; 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; } template inline void world::set(entity_id e, const T& comp) { storage.set(e, comp); } template inline void entity::set(const T& comp) { w->set(id, comp); } };