├── .gitmodules ├── .gitignore ├── yacs_utils.h ├── examples ├── settings.cpp ├── events.cpp ├── components.cpp └── basic.cpp ├── README.md ├── yacs_entity.h ├── yacs_component.h ├── yacs_pool.h ├── yacs_type_traits.h ├── LICENSE └── yacs.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "include/parallel-hashmap"] 2 | path = include/parallel-hashmap 3 | url = https://github.com/greg7mdp/parallel-hashmap.git 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .kde4/* 2 | *.kdev4 3 | .old/ 4 | build/ 5 | libs/ 6 | include_old/ 7 | *.gch 8 | */*.gch 9 | *.kate-swp 10 | */*.kate-swp 11 | CMakeLists.txt.user 12 | cmake-build-debug/ 13 | .idea 14 | exclude/ 15 | *.out 16 | yacs/ 17 | -------------------------------------------------------------------------------- /yacs_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_UTILS_H 2 | #define YACS_UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | #if __cplusplus >= 201703L 8 | #include 9 | #include 10 | #if defined(__GNUC__) && defined(__MINGW32__) && (__GNUC__ < 6) 11 | extern "C" { 12 | #include 13 | } 14 | #endif // MinGW is on some stuff 15 | #include 16 | #endif 17 | 18 | #include "yacs_component.h" 19 | 20 | namespace yacs { 21 | class world; 22 | class entity; 23 | 24 | class system { 25 | public: 26 | virtual ~system() = default; 27 | virtual void update(size_t time) = 0; 28 | }; 29 | 30 | template 31 | class event_subscriber { 32 | public: 33 | virtual ~event_subscriber() = default; 34 | virtual void receive(world* world, const T& event) = 0; 35 | }; 36 | 37 | struct entity_created { 38 | handle ent; 39 | }; 40 | 41 | struct entity_destroyed { 42 | handle ent; 43 | }; 44 | 45 | template 46 | struct component_created { 47 | handle ent; 48 | handle component; 49 | }; 50 | 51 | template 52 | struct component_destroyed { 53 | handle ent; 54 | handle component; 55 | }; 56 | 57 | inline size_t castd(double d) { 58 | union { size_t l; double d; } local_u; 59 | local_u.d = d; 60 | return local_u.l; 61 | } 62 | 63 | inline double castl(size_t l) { 64 | union { size_t l; double d; } local_u; 65 | local_u.l = l; 66 | return local_u.d; 67 | } 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /examples/settings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // control default memory pool memory block for components: block = count * sizeof(component_type) 7 | #define YACS_DEFAULT_COMPONENTS_COUNT 200 8 | // control default memory pool memory block for entities: block = count * sizeof(entity_type) 9 | #define YACS_DEFAULT_ENTITY_COUNT 200 10 | // control default memory pool memory block for additional entity component container: block = count * sizeof(entity_component_container_type) 11 | #define YACS_DEFAULT_ENTITY_COMPONENT_CONTAINER_COUNT 200 12 | 13 | // remove yacs asserts 14 | #define _NDEBUG 15 | 16 | #define YACS_IMPLEMENTATION 17 | #include "../yacs.h" 18 | 19 | struct component1 { 20 | uint64_t data; 21 | }; 22 | 23 | struct component2 { 24 | uint64_t data; 25 | }; 26 | 27 | struct component3 { 28 | uint64_t data; 29 | }; 30 | 31 | struct component4 { 32 | uint64_t data; 33 | }; 34 | 35 | struct component5 { 36 | uint64_t data; 37 | }; 38 | 39 | int main() { 40 | yacs::world world; 41 | auto ent = world.create_entity(); 42 | // use this to compute real component type size 43 | world.create_allocator(yacs::size_of()*10); 44 | world.create_allocator(yacs::size_of()*10); 45 | world.create_allocator(yacs::size_of()*10); 46 | world.create_allocator(yacs::size_of()*10); 47 | 48 | // does not create new component allocator 49 | ent->add(); 50 | // creates new component allocator 51 | ent->add(); 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yet another component system 2 | Header-only c++11 entity component system 3 | 4 | Depends on [Parallel hashmap](https://github.com/greg7mdp/parallel-hashmap) for fast pointer container 5 | 6 | Compile time type name as bonus for using c++17 7 | 8 | Example: 9 | ```c++ 10 | #define YACS_IMPLEMENTATION // for some non-template code 11 | #include "yacs.h" 12 | 13 | // component size MUST be equal or more than sizeof(void*) 14 | 15 | struct pickable { 16 | int attribute1; 17 | int attribute2; 18 | }; 19 | 20 | struct graphics { 21 | int attribute1; 22 | int attribute2; 23 | }; 24 | 25 | int main() { 26 | yacs::world world; 27 | 28 | // entity creates this way 29 | auto ent = world.create_entity(); 30 | // more precise control of memory pool memory size block 31 | world.create_allocator(sizeof(pickable)*100); 32 | 33 | // adding new component to entity 34 | auto pickable_h = ent->add(); 35 | // getting component handle 36 | auto h2 = ent->get(); 37 | // removing and destroying component 38 | ent->remove(); 39 | 40 | // world can be used as general data container 41 | auto graphics_h = world.create_component(); 42 | // we can set any manualy created components 43 | ent->set(graphics_h); 44 | // entity destruction automatically destroes graphics_h, thus we need to manualy unset component from entity 45 | ent->unset(); 46 | 47 | return 0; 48 | } 49 | ``` 50 | 51 | More examples in "examples" folder. 52 | 53 | ## TODO 54 | 55 | 1. Better systems or remove systems ? 56 | 2. More examples 57 | 3. Tests 58 | 4. Benchmark 59 | 5. Multitheading 60 | 6. Remove id() from entity 61 | 7. ??? 62 | 63 | ## License 64 | 65 | Apache License 2.0 66 | -------------------------------------------------------------------------------- /examples/events.cpp: -------------------------------------------------------------------------------- 1 | #define YACS_IMPLEMENTATION 2 | #include "../yacs.h" 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | struct event_data1 { 9 | int data; 10 | }; 11 | 12 | struct event_data2 { 13 | void* data; 14 | }; 15 | 16 | struct pod_component { 17 | uint32_t abc; 18 | uint64_t def; 19 | uint64_t ghi; 20 | }; 21 | 22 | // yacs event, could be yacs::component_created, yacs::component_destroyed, yacs::entity_created, yacs::entity_destroyed 23 | // see yacs_utils.h 24 | class graphics_component_event : public yacs::event_subscriber> { 25 | public: 26 | void receive(yacs::world* world, const yacs::component_created &event) override { 27 | (void)world; 28 | (void)event; 29 | std::cout << "Received component_created event" << "\n"; 30 | } 31 | }; 32 | 33 | // userdefined events 34 | class event_receiver1 : public yacs::event_subscriber { 35 | public: 36 | void receive(yacs::world* world, const event_data1 &event) override { 37 | (void)world; 38 | (void)event; 39 | std::cout << "Received event1" << "\n"; 40 | assert(event.data == 24); 41 | } 42 | }; 43 | 44 | class event_receiver2 : public yacs::event_subscriber { 45 | public: 46 | void receive(yacs::world* world, const event_data2 &event) override { 47 | (void)world; 48 | (void)event; 49 | std::cout << "Received event2" << "\n"; 50 | assert(event.data == nullptr); 51 | } 52 | }; 53 | 54 | int main() { 55 | graphics_component_event gev; 56 | event_receiver1 er1; 57 | event_receiver2 er2; 58 | 59 | yacs::world world; 60 | world.subscribe(&gev); 61 | world.subscribe(&er1); 62 | world.subscribe(&er2); 63 | 64 | auto ent = world.create_entity(); 65 | ent->add(); // component_created event 66 | 67 | event_data1 d1{ 68 | 24 69 | }; 70 | 71 | event_data2 d2{ 72 | nullptr 73 | }; 74 | 75 | world.emit(d1); // event event_data1 76 | world.emit(d2); // event event_data2 77 | 78 | // if we get tired of events 79 | world.unsubscribe(&er1); 80 | 81 | world.emit(d1); // dont do anything 82 | } 83 | -------------------------------------------------------------------------------- /examples/components.cpp: -------------------------------------------------------------------------------- 1 | #define YACS_IMPLEMENTATION 2 | #include "../yacs.h" 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | static size_t allocated_complex_component = 0; 9 | 10 | struct memory_checker { 11 | ~memory_checker() { 12 | if (allocated_complex_component == 0) std::cout << "All complex components is freed" << "\n"; 13 | else std::cout << "There are " << allocated_complex_component << " leaked components" << "\n"; 14 | } 15 | }; 16 | 17 | struct pod_component { 18 | uint32_t abc; 19 | uint64_t def; 20 | uint64_t ghi; 21 | }; 22 | 23 | struct complex_component { 24 | char* array; 25 | std::vector vector; 26 | 27 | complex_component(const size_t &size1, const size_t &size2) : array(new char[size1]), vector(size2, 0) { 28 | ++allocated_complex_component; 29 | } 30 | 31 | ~complex_component() { 32 | delete [] array; 33 | array = nullptr; 34 | 35 | --allocated_complex_component; 36 | } 37 | }; 38 | 39 | const size_t entity_count = 1000; 40 | 41 | int main() { 42 | memory_checker ch; 43 | 44 | yacs::world world; 45 | 46 | // create shared component 47 | auto handle = world.create_component(); 48 | 49 | for (size_t i = 0; i < entity_count; ++i) { 50 | auto ent = world.create_entity(); 51 | // set shared object to entity 52 | ent->set(handle); 53 | // using perfect forwarding to construct the object 54 | ent->add(i*10+1, i+1); 55 | } 56 | 57 | const size_t random_index = 352; 58 | auto itr = world.get_entities_view().begin(); 59 | for (size_t i = 0; i < random_index; ++i) ++itr; 60 | 61 | std::cout << "Random complex_component destruction" << "\n"; 62 | itr->remove(); 63 | 64 | for (size_t i = 0; i < entity_count; ++i) { 65 | auto ent = world.create_entity(); 66 | ent->set(handle); 67 | ent->add(i*10+1, i+1); 68 | } 69 | 70 | // component destroyes within entity's destruction, if we need continue using 71 | // the pod_component, we need to manually unset shared component from every entity 72 | for (auto &ent : world.get_entities_view()) { 73 | ent.unset(); 74 | } 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /yacs_entity.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_ENTITY_H 2 | #define YACS_ENTITY_H 3 | 4 | #include 5 | #include 6 | // ugly 7 | #include "include/parallel-hashmap/parallel_hashmap/phmap.h" 8 | #include "yacs_component.h" 9 | 10 | namespace yacs { 11 | class world; 12 | 13 | class entity { 14 | public: 15 | using component_container_t = phmap::flat_hash_map; // мож так оставить? 16 | 17 | struct id { 18 | uint32_t index; 19 | uint32_t counter; 20 | 21 | id() noexcept = default; 22 | inline id(uint32_t index, uint32_t counter) noexcept : index(index), counter(counter) {} 23 | id(const id ©) noexcept = default; 24 | id(id &&move) noexcept = default; 25 | id & operator=(const id ©) noexcept = default; 26 | id & operator=(id &&move) noexcept = default; 27 | inline bool operator==(const id &b) const noexcept { return index == b.index && counter == b.counter; } 28 | inline bool operator!=(const id &b) const noexcept { return !operator==(b); } 29 | size_t make_id() const noexcept { return (size_t(counter) << 32) | size_t(index); } 30 | }; 31 | 32 | entity(world* world) noexcept; 33 | ~entity() noexcept; 34 | 35 | entity(const entity ©) noexcept = delete; 36 | entity(entity &&move) noexcept = default; 37 | entity & operator=(const entity ©) noexcept = delete; 38 | entity & operator=(entity &&move) noexcept = default; 39 | 40 | template 41 | handle add(Args&& ...args); 42 | 43 | template 44 | bool remove(); 45 | 46 | template 47 | bool has() const noexcept; 48 | 49 | template 50 | handle get() const noexcept; 51 | 52 | template 53 | bool set(handle h); 54 | 55 | template 56 | bool unset(); 57 | 58 | void* raw_get(const size_t &component_type) const noexcept; 59 | 60 | size_t id() const noexcept; 61 | size_t components_count() const noexcept; 62 | private: 63 | //size_t m_id; 64 | world* m_world; 65 | protected: 66 | bool has(const std::initializer_list &list) const noexcept; 67 | void set_component(const size_t &type, void* comp) noexcept; 68 | void remove_component(const size_t &type) noexcept; 69 | void* get_component(const size_t &type) const noexcept; 70 | size_t find_type_index(const size_t &type) const noexcept; 71 | bool raw_has(const size_t &type) const noexcept; 72 | 73 | component_container_t m_components; 74 | }; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /yacs_component.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_COMPONENT_H 2 | #define YACS_COMPONENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "yacs_type_traits.h" 8 | 9 | #ifndef _NDEBUG 10 | #include 11 | #define YACS_ASSERT(expr) assert(expr) 12 | #else 13 | #define YACS_ASSERT(expr) 14 | #endif 15 | 16 | namespace yacs { 17 | class entity; 18 | 19 | template 20 | struct component_storage { 21 | T obj; 22 | size_t id; 23 | 24 | template 25 | component_storage(const size_t &id, Args&& ...args) : obj(std::forward(args)...), id(id) {} 26 | ~component_storage() { id = SIZE_MAX; } 27 | T* ptr() { return &obj; } 28 | const T* ptr() const { return &obj; } 29 | }; 30 | 31 | template 32 | size_t get_component_storage_id(const T* comp) { 33 | //static_assert(offsetof(component_storage, obj) == 0); 34 | auto mem = reinterpret_cast*>(comp); 35 | return mem->id; 36 | } 37 | 38 | template 39 | struct event_container { 40 | T data; 41 | 42 | template 43 | event_container(Args&& ...args) : data(std::forward(args)...) {} 44 | event_container(const T &data) : data(data) {} 45 | event_container(T &&data) : data(std::move(data)) {} 46 | }; 47 | 48 | template 49 | class handle { 50 | public: 51 | handle() noexcept : id(SIZE_MAX), ptr(nullptr) {} 52 | handle(const size_t &id, T* ptr) noexcept : id(id), ptr(ptr) {} 53 | ~handle() noexcept = default; 54 | handle(const handle ©) noexcept = default; 55 | handle(handle &&move) noexcept = default; 56 | handle & operator=(const handle ©) noexcept = default; 57 | handle & operator=(handle &&move) noexcept = default; 58 | 59 | bool valid() const noexcept { return id != SIZE_MAX && id == get_component_storage_id(ptr); } 60 | T* get() noexcept { return valid() ? ptr : nullptr; } 61 | const T* get() const noexcept { return valid() ? ptr : nullptr; } 62 | T* operator->() { not_valid_throw(); return ptr; } 63 | const T* operator->() const { not_valid_throw(); return ptr; } 64 | T & operator*() { not_valid_throw(); return *ptr; } 65 | const T & operator*() const { not_valid_throw(); return *ptr; } 66 | bool operator==(const handle &handle) const noexcept { return ptr == handle.ptr && id == handle.id; } 67 | bool operator!=(const handle &handle) const noexcept { return !operator==(handle); } 68 | void not_valid_throw() const { if (!valid()) throw std::runtime_error("Handle contains invalid data of type '" + std::string(type_name()) + "'"); } 69 | private: 70 | size_t id; 71 | T* ptr; 72 | }; 73 | } 74 | 75 | #endif //YACS_COMPONENT_H 76 | -------------------------------------------------------------------------------- /examples/basic.cpp: -------------------------------------------------------------------------------- 1 | #define YACS_IMPLEMENTATION 2 | #include "../yacs.h" 3 | #include 4 | 5 | #include 6 | 7 | // component size must be equal or more than sizeof(char*) 8 | // components that size is less 9 | 10 | namespace abc { 11 | struct transform { 12 | float x, y, z; 13 | 14 | ~transform() { 15 | std::cout << "transform destroyed" << "\n"; 16 | } 17 | }; 18 | } 19 | 20 | struct pickable { 21 | int attribute1; 22 | int attribute2; 23 | }; 24 | 25 | struct graphics { 26 | int image_id1; 27 | int image_id2; 28 | }; 29 | 30 | class graphics_component_event : public yacs::event_subscriber> { 31 | public: 32 | void receive(yacs::world* world, const yacs::component_created &event) override { 33 | (void)world; 34 | (void)event; 35 | std::cout << "Received component_created event" << "\n"; 36 | } 37 | }; 38 | 39 | int main(int argc, char* argv[]) { 40 | (void)argc; 41 | (void)argv; 42 | 43 | graphics_component_event ev; 44 | 45 | yacs::world world; 46 | 47 | world.create_allocator(yacs::size_of()*100); 48 | 49 | world.subscribe(&ev); 50 | 51 | auto ent = world.create_entity(); 52 | auto trans_h = ent->add(); 53 | auto pickable_h = ent->add(); 54 | auto graphics_h = ent->add(); 55 | 56 | trans_h->x = 234.0f; 57 | pickable_h->attribute1 = 235; 58 | graphics_h->image_id1 = 74574; 59 | 60 | auto ent2 = world.create_entity(); 61 | ent2->add(); 62 | ent2->add(); 63 | ent2->add(); 64 | 65 | const size_t count = world.count_entities(); 66 | assert(count == 2); 67 | 68 | // forward iterators 69 | for (auto itr = world.get_entities_view().begin(); itr != world.get_entities_view().end(); ++itr) { 70 | const auto &ent = *itr; 71 | auto h = ent.get(); 72 | assert(h.valid()); 73 | } 74 | 75 | size_t counter = 0; 76 | for (const auto &ent : world.get_entities_view()) { 77 | auto h = ent.get(); 78 | assert(h.valid()); 79 | 80 | ++counter; 81 | } 82 | 83 | assert(counter == 2); 84 | 85 | // is reference to component is better than component handle? 86 | for (const auto &comp : world.get_component_view()) { 87 | (void)comp; 88 | } 89 | 90 | assert(ent->get().valid()); 91 | assert(ent->get()->x == 234.0f); 92 | assert(ent->get().valid()); 93 | assert(ent->get()->attribute1 == 235); 94 | assert(ent->get().valid()); 95 | assert(ent->get()->image_id1 == 74574); 96 | 97 | ent->remove(); 98 | ent->remove(); 99 | ent->remove(); 100 | 101 | assert(!ent->get().valid()); 102 | assert(!ent->get().valid()); 103 | assert(!ent->get().valid()); 104 | 105 | world.destroy_entity(ent); 106 | 107 | // autorelease all entites on world destruction 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /yacs_pool.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_POOL_H 2 | #define YACS_POOL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "yacs_component.h" 9 | 10 | namespace yacs { 11 | constexpr size_t align_to(const size_t &size, const size_t aligment) { 12 | return (size + aligment - 1) / aligment * aligment; 13 | } 14 | 15 | class typeless_pool { 16 | public: 17 | typeless_pool(const size_t size, const size_t piece_size, const size_t piece_aligment) noexcept; 18 | ~typeless_pool() noexcept; 19 | 20 | typeless_pool(const typeless_pool ©) noexcept = delete; 21 | typeless_pool & operator=(const typeless_pool ©) noexcept = delete; 22 | typeless_pool(typeless_pool &&move) noexcept; 23 | typeless_pool & operator=(typeless_pool &&move) noexcept; 24 | 25 | void* allocate(); 26 | void free(void* block) noexcept; 27 | void clear() noexcept; 28 | 29 | template 30 | T* create(Args&&... args) { 31 | YACS_ASSERT(sizeof(T) <= m_piece_size && "Type size is more than this pool piece size"); 32 | YACS_ASSERT(alignof(T) <= m_piece_aligment && "Type aligment is more than this pool piece aligment"); 33 | auto ptr = allocate(); 34 | return new (ptr) T(std::forward(args)...); 35 | } 36 | 37 | template 38 | void destroy(T* obj) noexcept { 39 | YACS_ASSERT(sizeof(T) <= m_piece_size && "Type size is more than this pool piece size"); 40 | YACS_ASSERT(alignof(T) <= m_piece_aligment && "Type aligment is more than this pool piece aligment"); 41 | if (obj == nullptr) return; 42 | obj->~T(); 43 | free(obj); 44 | } 45 | 46 | size_t size() const noexcept; 47 | size_t block_size() const noexcept; 48 | size_t piece_size() const noexcept; 49 | size_t piece_aligment() const noexcept; 50 | private: 51 | void allocate_block(); 52 | 53 | size_t m_size; 54 | size_t m_piece_size; 55 | size_t m_piece_aligment; 56 | size_t m_current; 57 | char* m_memory; 58 | void* m_free_mem; 59 | }; 60 | } 61 | 62 | // template 63 | // struct static_container { 64 | // std::array array; 65 | // static_container* next; 66 | // 67 | // static_container() : next(nullptr) {} 68 | // static_container(const T &default_value) : next(nullptr) { 69 | // for (size_t i = 0; i < array.size(); ++i) { 70 | // array[i] = default_value; 71 | // } 72 | // } 73 | // }; 74 | // 75 | // template 76 | // static_container* advance_static_container(static_container* cont, const size_t &index) { 77 | // static_container* current = cont; 78 | // for (size_t i = 0; i < index && current != nullptr; ++i) { 79 | // current = current->next; 80 | // } 81 | // 82 | // return current; 83 | // } 84 | // 85 | // template 86 | // const static_container* advance_static_container(const static_container* cont, const size_t &index) { 87 | // auto current = cont; 88 | // for (size_t i = 0; i < index && current != nullptr; ++i) { 89 | // current = current->next; 90 | // } 91 | // 92 | // return current; 93 | // } 94 | 95 | #endif //YACS_POOL_H 96 | -------------------------------------------------------------------------------- /yacs_type_traits.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_TYPE_TRAITS_H 2 | #define YACS_TYPE_TRAITS_H 3 | 4 | #include 5 | #include 6 | 7 | namespace yacs { 8 | namespace detail { 9 | // https://stackoverflow.com/questions/45948835/stdis-base-of-and-virtual-base-class 10 | template 11 | struct can_static_cast : public std::false_type {}; 12 | 13 | template 14 | struct can_static_cast(std::declval()))>> : public std::true_type {}; 15 | 16 | template 17 | struct is_virtual_base_of : public std::conjunction< 18 | std::is_base_of, 19 | std::negation> 20 | >{}; 21 | 22 | /// Simple type introspection without RTTI. 23 | template 24 | constexpr std::string_view get_type_name() { 25 | //std::cout << __PRETTY_FUNCTION__ << "\n"; 26 | //std::cout << __FUNCSIG__ << "\n"; 27 | #if defined(_MSC_VER) 28 | constexpr std::string_view start_char_seq = "get_type_name<"; 29 | constexpr std::string_view end_char_seq = ">(void)"; 30 | constexpr std::string_view function_type_pattern = ")("; 31 | constexpr std::string_view sig = __FUNCSIG__; 32 | constexpr size_t sig_size = sig.size()+1; 33 | constexpr size_t str_seq_name_start = sig.find(start_char_seq) + start_char_seq.size(); 34 | constexpr size_t end_of_char_str = sig.rfind(start_char_seq); 35 | constexpr size_t count = sig_size - str_seq_name_start - end_char_seq.size() - 1; // отстается символ '>' в конце 36 | constexpr std::string_view substr = sig.substr(str_seq_name_start, count); 37 | if constexpr (substr.find(function_type_pattern) == std::string_view::npos) { 38 | constexpr std::string_view class_char_seq = "class "; 39 | constexpr std::string_view struct_char_seq = "struct "; 40 | const size_t class_seq_start = substr.find(class_char_seq); 41 | const size_t struct_seq_start = substr.find(struct_char_seq); 42 | if constexpr (class_seq_start == 0) return substr.substr(class_char_seq.size()); 43 | if constexpr (struct_seq_start == 0) return substr.substr(struct_char_seq.size());; 44 | } 45 | return substr; 46 | #elif defined(__clang__) 47 | constexpr std::string_view sig = __PRETTY_FUNCTION__; 48 | constexpr std::string_view start_char_seq = "T = "; 49 | constexpr size_t sig_size = sig.size()+1; 50 | constexpr size_t str_seq_name_start = sig.find(start_char_seq) + start_char_seq.size(); 51 | constexpr size_t end_of_char_str = 2; 52 | constexpr size_t count = sig_size - str_seq_name_start - end_of_char_str; 53 | return sig.substr(str_seq_name_start, count); 54 | #elif defined(__GNUC__) 55 | constexpr std::string_view sig = __PRETTY_FUNCTION__; 56 | constexpr std::string_view start_char_seq = "T = "; 57 | constexpr size_t sig_size = sig.size()+1; 58 | constexpr size_t str_seq_name_start = sig.find(start_char_seq) + start_char_seq.size(); 59 | constexpr size_t end_of_char_str = sig_size - sig.find(';'); 60 | constexpr size_t count = sig_size - str_seq_name_start - end_of_char_str; 61 | return sig.substr(str_seq_name_start, count); 62 | #else 63 | #error Compiler not supported for demangling 64 | #endif 65 | } 66 | 67 | constexpr uint8_t to_u8(const char c) { return uint8_t(c); } 68 | 69 | constexpr uint64_t U8TO64_LE(const char* data) { 70 | return uint64_t(to_u8(data[0])) | (uint64_t(to_u8(data[1])) << 8) | (uint64_t(to_u8(data[2])) << 16) | 71 | (uint64_t(to_u8(data[3])) << 24) | (uint64_t(to_u8(data[4])) << 32) | (uint64_t(to_u8(data[5])) << 40) | 72 | (uint64_t(to_u8(data[6])) << 48) | (uint64_t(to_u8(data[7])) << 56); 73 | } 74 | 75 | constexpr uint64_t U8TO64_LE(const uint8_t* data) { 76 | return uint64_t(data[0]) | (uint64_t(data[1]) << 8) | (uint64_t(data[2]) << 16) | 77 | (uint64_t(data[3]) << 24) | (uint64_t(data[4]) << 32) | (uint64_t(data[5]) << 40) | 78 | (uint64_t(data[6]) << 48) | (uint64_t(data[7]) << 56); 79 | } 80 | 81 | constexpr uint64_t to_u64(const char c) { return uint64_t(to_u8(c)); } 82 | 83 | constexpr uint64_t murmur_hash64A(const std::string_view& in_str, const uint64_t seed) { 84 | constexpr uint64_t m = 0xc6a4a7935bd1e995LLU; 85 | constexpr int r = 47; 86 | const size_t len = in_str.size(); 87 | const size_t end = len - (len % sizeof(uint64_t)); 88 | 89 | uint64_t h = seed ^ (len * m); 90 | 91 | for (size_t i = 0; i < end; i += 8) { 92 | uint64_t k = U8TO64_LE(&in_str[i]); 93 | k *= m; 94 | k ^= k >> r; 95 | k *= m; 96 | 97 | h ^= k; 98 | h *= m; 99 | } 100 | 101 | const auto key_end = in_str.substr(end); 102 | const int left = len & 7; 103 | switch (left) { 104 | case 7: h ^= to_u64(key_end[6]) << 48; [[fallthrough]]; 105 | case 6: h ^= to_u64(key_end[5]) << 40; [[fallthrough]]; 106 | case 5: h ^= to_u64(key_end[4]) << 32; [[fallthrough]]; 107 | case 4: h ^= to_u64(key_end[3]) << 24; [[fallthrough]]; 108 | case 3: h ^= to_u64(key_end[2]) << 16; [[fallthrough]]; 109 | case 2: h ^= to_u64(key_end[1]) << 8; [[fallthrough]]; 110 | case 1: h ^= to_u64(key_end[0]); 111 | h *= m; 112 | }; 113 | 114 | h ^= h >> r; 115 | h *= m; 116 | h ^= h >> r; 117 | 118 | return h; 119 | } 120 | } 121 | 122 | constexpr uint64_t default_murmur_seed = 14695981039346656037ull; 123 | constexpr uint64_t murmur_hash64A(const std::string_view &in_str, const uint64_t seed) { 124 | return detail::murmur_hash64A(in_str, seed); 125 | } 126 | 127 | constexpr uint64_t string_hash(const std::string_view &in_str) { 128 | return murmur_hash64A(in_str, default_murmur_seed); 129 | } 130 | 131 | template 132 | constexpr std::string_view type_name() { 133 | return detail::get_type_name(); 134 | } 135 | 136 | template 137 | constexpr uint64_t type_id() { 138 | return string_hash(type_name()); 139 | } 140 | } 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /yacs.h: -------------------------------------------------------------------------------- 1 | #ifndef YACS_H 2 | #define YACS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "yacs_entity.h" 10 | #include "yacs_component.h" 11 | #include "yacs_pool.h" 12 | #include "yacs_utils.h" 13 | 14 | // ugly 15 | #include "include/parallel-hashmap/parallel_hashmap/phmap.h" 16 | 17 | #ifndef YACS_DEFAULT_COMPONENTS_COUNT 18 | #define YACS_DEFAULT_COMPONENTS_COUNT 100 19 | #endif 20 | 21 | #ifndef YACS_DEFAULT_ENTITY_COUNT 22 | #define YACS_DEFAULT_ENTITY_COUNT 100 23 | #endif 24 | 25 | #ifndef YACS_DEFAULT_ENTITY_COMPONENT_CONTAINER_COUNT 26 | #define YACS_DEFAULT_ENTITY_COMPONENT_CONTAINER_COUNT 100 27 | #endif 28 | 29 | // using flat_hash_map extensively, its slower than indexed array access, 30 | // but faster than linear search in small sorted array 31 | 32 | namespace yacs { 33 | // use this to compute real component type size 34 | template 35 | size_t size_of() noexcept; 36 | 37 | class world { 38 | struct components_data; 39 | public: 40 | using entity_ptr_array_t = std::vector*>>; 41 | using component_ptr_array_t = std::vector; 42 | using component_data_container_t = phmap::flat_hash_map>; 43 | 44 | template 45 | class component_type_view { 46 | public: 47 | using internal_type = component_ptr_array_t::iterator; 48 | 49 | class iterator { 50 | public: 51 | //using internal_type = phmap::flat_hash_set::iterator; 52 | using internal_type = component_ptr_array_t::iterator; 53 | using reference = T&; 54 | using pointer = T*; 55 | internal_type current; 56 | 57 | iterator(const internal_type &itr) noexcept : current(itr) {} 58 | iterator(const iterator &other) = default; 59 | iterator(iterator &&other) = default; 60 | iterator & operator=(const iterator &other) = default; 61 | iterator & operator=(iterator &&other) = default; 62 | 63 | iterator & operator++() { 64 | ++current; 65 | return *this; 66 | } 67 | 68 | iterator operator++(int) { 69 | auto cur = current; 70 | ++current; 71 | return cur; 72 | } 73 | 74 | //reference operator*() { return handle(get_component_storage_id(), reinterpret_cast(*current)); } 75 | reference operator*() { return *reinterpret_cast*>(*current)->ptr(); } 76 | pointer operator->() { auto ptr = reinterpret_cast*>(*current); return ptr->ptr(); } // return handle(get_component_storage_id(ptr), ptr); 77 | 78 | friend bool operator==(const iterator& a, const iterator& b) { return a.current == b.current; } 79 | friend bool operator!=(const iterator& a, const iterator& b) { return !(a == b); } 80 | }; 81 | 82 | class const_iterator { 83 | public: 84 | //using internal_type = phmap::flat_hash_set::const_iterator; 85 | using internal_type = component_ptr_array_t::const_iterator; 86 | using reference = const T&; 87 | using pointer = const T*; 88 | internal_type current; 89 | 90 | const_iterator(const internal_type &itr) noexcept : current(itr) {} 91 | const_iterator(const const_iterator &other) = default; 92 | const_iterator(const_iterator &&other) = default; 93 | const_iterator & operator=(const const_iterator &other) = default; 94 | const_iterator & operator=(const_iterator &&other) = default; 95 | 96 | const_iterator & operator++() { 97 | ++current; 98 | return *this; 99 | } 100 | 101 | const_iterator operator++(int) { 102 | auto cur = current; 103 | ++current; 104 | return cur; 105 | } 106 | 107 | //reference operator*() { return handle(get_component_storage_id(), reinterpret_cast(*current)); } 108 | reference operator*() const { return *reinterpret_cast*>(*current)->ptr(); } 109 | pointer operator->() const { auto ptr = reinterpret_cast*>(*current); return ptr->ptr(); } // return handle(get_component_storage_id(ptr), ptr); 110 | 111 | friend bool operator==(const const_iterator& a, const const_iterator& b) { return a.current == b.current; } 112 | friend bool operator!=(const const_iterator& a, const const_iterator& b) { return !(a == b); } 113 | }; 114 | 115 | component_type_view(world* m_world); 116 | 117 | iterator begin(); 118 | iterator end(); 119 | const_iterator begin() const; 120 | const_iterator end() const; 121 | private: 122 | internal_type m_begin; 123 | internal_type m_end; 124 | }; 125 | 126 | template 127 | class entities_view { 128 | public: 129 | using internal_type = entity_ptr_array_t::iterator; 130 | 131 | class iterator { 132 | public: 133 | //using internal_type = phmap::flat_hash_set::iterator; 134 | using internal_type = entity_ptr_array_t::iterator; 135 | using reference = entity&; 136 | using pointer = handle; 137 | 138 | internal_type current; 139 | internal_type end; 140 | 141 | iterator(const internal_type &itr, const internal_type &end) noexcept : current(itr), end(end) { 142 | if (current == end) return; 143 | auto ent = current->second; 144 | while (ent != nullptr && !ent->ptr()->has()) { 145 | ++current; 146 | if (current == end) return; 147 | ent = current->second; 148 | } 149 | } 150 | 151 | iterator(const iterator &other) = default; 152 | iterator(iterator &&other) = default; 153 | iterator & operator=(const iterator &other) = default; 154 | iterator & operator=(iterator &&other) = default; 155 | 156 | iterator & operator++() { 157 | if (current == end) return *this; 158 | 159 | component_storage* ent = nullptr; 160 | do { 161 | ++current; 162 | if (current == end) return *this; 163 | ent = current->second; 164 | } while (ent != nullptr && !ent->ptr()->has()); 165 | 166 | return *this; 167 | } 168 | 169 | iterator operator++(int) { 170 | auto cur = current; 171 | operator++(); 172 | return cur; 173 | } 174 | 175 | reference operator*() { return *current->second->ptr(); } 176 | pointer operator->() { auto ptr = current->second->ptr(); return handle(current->second->id, ptr); } 177 | 178 | friend bool operator==(const iterator& a, const iterator& b) { return a.current == b.current; } 179 | friend bool operator!=(const iterator& a, const iterator& b) { return !(a == b); } 180 | }; 181 | 182 | class const_iterator { 183 | public: 184 | //using internal_type = phmap::flat_hash_set::const_iterator; 185 | using internal_type = entity_ptr_array_t::const_iterator; 186 | using reference = const entity&; 187 | using pointer = handle; 188 | 189 | internal_type current; 190 | internal_type end; 191 | 192 | const_iterator(const internal_type &itr, const internal_type &end) noexcept : current(itr), end(end) { 193 | if (current == end) return; 194 | auto ent = current->second; 195 | while (ent != nullptr && !ent->ptr()->has()) { 196 | ++current; 197 | if (current == end) return; 198 | ent = current->second; 199 | } 200 | } 201 | 202 | const_iterator(const const_iterator &other) = default; 203 | const_iterator(const_iterator &&other) = default; 204 | const_iterator & operator=(const const_iterator &other) = default; 205 | const_iterator & operator=(const_iterator &&other) = default; 206 | 207 | const_iterator & operator++() { 208 | if (current == end) return *this; 209 | 210 | component_storage* ent = nullptr; 211 | do { 212 | ++current; 213 | if (current == end) return *this; 214 | ent = current->second; 215 | } while (ent != nullptr && !ent->ptr()->has()); 216 | 217 | return *this; 218 | } 219 | 220 | const_iterator operator++(int) { 221 | auto cur = current; 222 | operator++(); 223 | return cur; 224 | } 225 | 226 | reference operator*() { return *current->second->ptr(); } 227 | pointer operator->() { auto ptr = current->second->ptr(); return handle(current->second->id, ptr); } 228 | 229 | friend bool operator==(const const_iterator& a, const const_iterator& b) { return a.current == b.current; } 230 | friend bool operator!=(const const_iterator& a, const const_iterator& b) { return !(a == b); } 231 | }; 232 | 233 | entities_view(world* m_world); 234 | 235 | iterator begin(); 236 | iterator end(); 237 | const_iterator begin() const; 238 | const_iterator end() const; 239 | private: 240 | internal_type m_begin; 241 | internal_type m_end; 242 | }; 243 | 244 | template 245 | static size_t size_of(); 246 | 247 | world() noexcept; 248 | ~world() noexcept; 249 | 250 | handle create_entity(); 251 | void destroy_entity(handle ent); 252 | 253 | template 254 | void create_allocator(const size_t &size); 255 | 256 | template 257 | handle create_component(Args&& ...args); 258 | 259 | template 260 | void destroy_component(handle handle); 261 | 262 | void destroy_component(const size_t &type, void* storage); 263 | 264 | // remove systems 265 | void register_system(system* system); 266 | void remove_system(system* system); 267 | 268 | template 269 | void subscribe(event_subscriber* sub); 270 | 271 | template 272 | void unsubscribe(event_subscriber* sub); 273 | 274 | template 275 | void emit(const T &event); 276 | 277 | void update(const size_t &time); 278 | 279 | size_t size() const; 280 | 281 | template 282 | size_t count_components() const; 283 | 284 | template 285 | size_t count_entities() const; 286 | 287 | template 288 | handle get_component(const size_t &index) const; 289 | handle get_entity(const size_t &id) const; 290 | 291 | template 292 | component_type_view get_component_view(); 293 | 294 | template 295 | entities_view get_entities_view(); 296 | 297 | //size_t find_type_index(const size_t &type) const; 298 | private: 299 | struct components_data { 300 | using destructor_t = std::function; 301 | 302 | components_data(const size_t &type_size, const size_t &type_alignment, const size_t &size, const destructor_t &f); 303 | components_data(const components_data &data) noexcept = delete; 304 | components_data(components_data &&data) noexcept = default; 305 | components_data & operator=(const components_data &data) noexcept = delete; 306 | components_data & operator=(components_data &&data) noexcept = default; 307 | ~components_data(); 308 | 309 | template 310 | handle create(Args&& ...args); 311 | 312 | template 313 | void destroy(handle handle); 314 | void destroy(void* storage); 315 | 316 | size_t current_id; 317 | typeless_pool pool; 318 | //phmap::flat_hash_set components; 319 | component_ptr_array_t components; 320 | destructor_t destructor; 321 | }; 322 | 323 | template 324 | component_data_container_t::iterator create_allocator_raw(const size_t &size); 325 | 326 | typeless_pool m_entity_pool; 327 | entity_ptr_array_t m_entities; 328 | component_data_container_t m_components_data; 329 | std::vector m_systems; 330 | //std::vector> m_subscribers; 331 | phmap::flat_hash_map> m_subscribers; 332 | }; 333 | 334 | // наверное имеет смысл запомнить id у компонента, компонент может быть неаккуратно удален 335 | template 336 | handle entity::add(Args&& ...args) { 337 | auto ptr = get_component(type_id()); 338 | if (ptr != nullptr) return handle(); 339 | 340 | auto h = m_world->create_component(std::forward(args)...); 341 | YACS_ASSERT(h.valid()); 342 | set_component(type_id(), h.get()); 343 | m_world->emit(component_created{handle(get_component_storage_id(this), this), h}); 344 | return h; 345 | } 346 | 347 | template 348 | bool entity::remove() { 349 | auto comp = get_component(type_id()); 350 | if (comp == nullptr) return false; 351 | 352 | auto ptr = reinterpret_cast*>(comp); 353 | m_world->emit(component_destroyed{handle(get_component_storage_id(this), this), handle(ptr->id, ptr->ptr())}); 354 | remove_component(type_id()); 355 | m_world->destroy_component(handle(ptr->id, ptr->ptr())); 356 | return true; 357 | } 358 | 359 | template 360 | bool entity::has() const noexcept { 361 | const std::initializer_list list = { type_id()... }; 362 | return has(list); 363 | } 364 | 365 | template 366 | handle entity::get() const noexcept { 367 | auto comp = get_component(type_id()); 368 | if (comp == nullptr) return handle(); 369 | auto ptr = reinterpret_cast*>(comp); 370 | return handle(ptr->id, ptr->ptr()); 371 | } 372 | 373 | template 374 | bool entity::set(handle handle) { 375 | if (has()) return false; 376 | //if (get_type_id() == SIZE_MAX) return false; 377 | 378 | set_component(type_id(), handle.get()); 379 | return true; 380 | } 381 | 382 | template 383 | bool entity::unset() { 384 | if (!has()) return false; 385 | 386 | remove_component(type_id()); 387 | return true; 388 | } 389 | 390 | template 391 | size_t size_of() { return sizeof(component_storage); } 392 | 393 | template 394 | world::component_type_view::component_type_view(world* m_world) { 395 | const size_t type = type_id(); 396 | //const size_t index = find_type_index(type_id); 397 | const auto itr = m_world->m_components_data.find(type); 398 | m_begin = itr->second->components.begin(); 399 | m_end = itr->second->components.end(); 400 | } 401 | 402 | template 403 | typename world::component_type_view::iterator world::component_type_view::begin() { 404 | return world::component_type_view::iterator(m_begin); 405 | } 406 | 407 | template 408 | typename world::component_type_view::iterator world::component_type_view::end() { 409 | return world::component_type_view::iterator(m_end); 410 | } 411 | 412 | template 413 | typename world::component_type_view::const_iterator world::component_type_view::begin() const { 414 | return world::component_type_view::const_iterator(m_begin); 415 | } 416 | 417 | template 418 | typename world::component_type_view::const_iterator world::component_type_view::end() const { 419 | return world::component_type_view::const_iterator(m_end); 420 | } 421 | 422 | template 423 | world::entities_view::entities_view(world* m_world) : m_begin(m_world->m_entities.begin()), m_end(m_world->m_entities.end()) {} 424 | 425 | template 426 | typename world::entities_view::iterator world::entities_view::begin() { 427 | return world::entities_view::iterator(m_begin, m_end); 428 | } 429 | 430 | template 431 | typename world::entities_view::iterator world::entities_view::end() { 432 | return world::entities_view::iterator(m_end, m_end); 433 | } 434 | 435 | template 436 | typename world::entities_view::const_iterator world::entities_view::begin() const { 437 | return world::entities_view::const_iterator(m_begin, m_end); 438 | } 439 | 440 | template 441 | typename world::entities_view::const_iterator world::entities_view::end() const { 442 | return world::entities_view::const_iterator(m_end, m_end); 443 | } 444 | 445 | template 446 | void world::create_allocator(const size_t &size) { create_allocator_raw(size); } 447 | 448 | template 449 | handle world::create_component(Args&& ...args) { 450 | auto itr = m_components_data.find(type_id()); 451 | if (itr == m_components_data.end()) { 452 | itr = create_allocator_raw(YACS_DEFAULT_COMPONENTS_COUNT * sizeof(component_storage)); 453 | } 454 | 455 | return itr->second-> template create(std::forward(args)...); 456 | } 457 | 458 | template 459 | void world::destroy_component(handle h) { 460 | if (!h.valid()) return; 461 | auto itr = m_components_data.find(type_id()); 462 | if (itr == m_components_data.end()) return; 463 | itr->second->destroy(h); 464 | } 465 | 466 | template 467 | void world::subscribe(event_subscriber* sub) { 468 | m_subscribers[type_id()].insert(reinterpret_cast(sub)); 469 | } 470 | 471 | template 472 | void world::unsubscribe(event_subscriber* sub) { 473 | auto itr = m_subscribers.find(type_id()); 474 | if (itr == m_subscribers.end()) return; 475 | itr->second.erase(sub); 476 | } 477 | 478 | template 479 | void world::emit(const T &event) { 480 | const auto itr = m_subscribers.find(type_id()); 481 | if (itr == m_subscribers.end()) return; 482 | 483 | for (auto ptr : itr->second) { 484 | auto ev = reinterpret_cast*>(ptr); 485 | ev->receive(this, event); 486 | } 487 | } 488 | 489 | template 490 | size_t world::count_components() const { 491 | const auto itr = m_components_data.find(type_id()); 492 | if (itr == m_components_data.end()) return 0; 493 | return itr->second.components.size(); 494 | } 495 | 496 | template 497 | size_t world::count_entities() const { 498 | size_t c = 0; 499 | for (const auto &p : m_entities) { 500 | if (p.second == nullptr) continue; 501 | c += size_t(p.second->ptr()->has()); 502 | } 503 | 504 | return c; 505 | } 506 | 507 | template 508 | world::component_type_view world::get_component_view() { 509 | return world::component_type_view(this); 510 | } 511 | 512 | template 513 | world::entities_view world::get_entities_view() { 514 | return world::entities_view(this); 515 | } 516 | 517 | template 518 | world::component_data_container_t::iterator world::create_allocator_raw(const size_t &size) { 519 | if (const auto itr = m_components_data.find(type_id()); itr != m_components_data.end()) return m_components_data.end(); 520 | 521 | return m_components_data.emplace(type_id(), std::make_unique(sizeof(component_storage), alignof(component_storage), size, [] (void* ptr, typeless_pool &pool) { 522 | auto p = reinterpret_cast*>(ptr); 523 | pool.destroy(p); 524 | })).first; 525 | } 526 | 527 | template 528 | handle world::components_data::create(Args&& ...args) { 529 | auto ptr = pool.create>(current_id, std::forward(args)...); 530 | components.push_back(ptr); 531 | current_id++; 532 | return handle(ptr->id, ptr->ptr()); 533 | } 534 | 535 | template 536 | void world::components_data::destroy(handle h) { 537 | auto ptr = reinterpret_cast(h.get()); 538 | destroy(ptr); 539 | } 540 | } 541 | 542 | #endif // YACS_H 543 | 544 | #ifdef YACS_IMPLEMENTATION 545 | namespace yacs { 546 | entity::entity(world* world) noexcept : m_world(world) {} 547 | entity::~entity() noexcept { 548 | // size_t type_counter = 0; 549 | // 550 | // for (auto comp : m_components.array) { 551 | // const size_t type_id = type_counter; 552 | // ++type_counter; 553 | // if (comp == nullptr) continue; 554 | // m_world->destroy_component(type_id, comp); 555 | // } 556 | // 557 | // auto comps = m_components.next; 558 | // while (comps != nullptr) { 559 | // for (auto comp : comps->array) { 560 | // const size_t type_id = type_counter; 561 | // ++type_counter; 562 | // if (comp == nullptr) continue; 563 | // m_world->destroy_component(type_id, comp); 564 | // } 565 | // 566 | // auto old_comps = comps; 567 | // comps = comps->next; 568 | // m_world->destroy_entity_component_container(old_comps); 569 | // } 570 | 571 | for (const auto &pair : m_components) { 572 | m_world->destroy_component(pair.first, pair.second); 573 | } 574 | } 575 | 576 | bool entity::has(const std::initializer_list &list) const noexcept { 577 | for (const size_t &type : list) { 578 | if (!raw_has(type)) return false; 579 | } 580 | 581 | return true; 582 | } 583 | 584 | void* entity::raw_get(const size_t &component_type) const noexcept { 585 | return get_component(component_type); 586 | } 587 | 588 | size_t entity::id() const noexcept { return get_component_storage_id(this); } 589 | size_t entity::components_count() const noexcept { 590 | return m_components.size(); 591 | } 592 | 593 | void entity::set_component(const size_t &type, void* comp) noexcept { 594 | m_components[type] = comp; 595 | } 596 | 597 | void entity::remove_component(const size_t &type) noexcept { 598 | auto itr = m_components.find(type); 599 | if (itr == m_components.end()) return; 600 | m_components._erase(itr); 601 | } 602 | 603 | void* entity::get_component(const size_t &type) const noexcept { 604 | const auto itr = m_components.find(type); 605 | return itr != m_components.end() ? itr->second : nullptr; 606 | } 607 | 608 | bool entity::raw_has(const size_t &type) const noexcept { 609 | const auto itr = m_components.find(type); 610 | return itr != m_components.end(); 611 | } 612 | 613 | world::world() noexcept : 614 | m_entity_pool(sizeof(component_storage) * YACS_DEFAULT_ENTITY_COUNT, sizeof(component_storage), alignof(component_storage)) 615 | //m_entity_component_containers(sizeof(entity::component_container_t), alignof(entity::component_container_t), sizeof(entity::component_container_t) * YACS_DEFAULT_ENTITY_COMPONENT_CONTAINER_COUNT) 616 | {} 617 | 618 | world::~world() noexcept { 619 | for (const auto &pair : m_entities) { 620 | m_entity_pool.destroy(pair.second); 621 | } 622 | } 623 | 624 | static size_t make_id(const uint32_t index, const uint32_t counter) { 625 | return (size_t(counter) << 32) | size_t(index); 626 | } 627 | 628 | handle world::create_entity() { 629 | size_t index = 0; 630 | for (; index < m_entities.size() && m_entities[index].second != nullptr; ++index) {} 631 | if (index == m_entities.size()) m_entities.emplace_back(0, nullptr); 632 | static_assert(sizeof(size_t) == 8); 633 | //const entity::id id(index, m_entities[index].first); 634 | const size_t id = make_id(index, m_entities[index].first); 635 | auto ent = m_entity_pool.create>(id, this); 636 | m_entities[index].second = ent; 637 | const handle h(id, ent->ptr()); 638 | emit(entity_created{h}); 639 | return h; 640 | } 641 | 642 | void world::destroy_entity(handle ent) { 643 | if (!ent.valid()) return; 644 | emit(entity_destroyed{ent}); 645 | const size_t id = get_component_storage_id(ent.get()); 646 | const size_t index = size_t(uint32_t(id)); 647 | //m_entities.erase(ent); 648 | assert(index < m_entities.size()); 649 | m_entity_pool.destroy(m_entities[index].second); 650 | m_entities[index].first+=1; 651 | m_entities[index].second = nullptr; 652 | } 653 | 654 | void world::destroy_component(const size_t &type, void* storage) { 655 | // const size_t index = find_type_index(type); 656 | // if (m_components_data[index].second == nullptr) return; 657 | // m_components_data[index].second->destroy(storage); 658 | const auto itr = m_components_data.find(type); 659 | if (itr == m_components_data.end()) return; 660 | itr->second->destroy(storage); 661 | } 662 | 663 | // тут неправильно 664 | void world::register_system(system* system) { 665 | m_systems.push_back(system); 666 | } 667 | 668 | void world::remove_system(system* system) { 669 | m_systems.erase(std::find(m_systems.begin(), m_systems.end(), system)); 670 | } 671 | 672 | void world::update(const size_t &time) { 673 | for (auto system : m_systems) { 674 | system->update(time); 675 | } 676 | } 677 | 678 | size_t world::size() const { 679 | return m_entities.size(); 680 | } 681 | 682 | // size_t world::find_type_index(const size_t &type) const { 683 | // size_t index = type % components_type_count; 684 | // size_t counter = 1; 685 | // while (counter < components_type_count && m_components_data[index].first != type && m_components_data[index].second != nullptr) { 686 | // index = (index + counter) % components_type_count; 687 | // ++counter; 688 | // } 689 | // 690 | // return index; 691 | // } 692 | 693 | world::components_data::components_data(const size_t &type_size, const size_t &type_alignment, const size_t &size, const destructor_t &destructor) : 694 | current_id(0), 695 | pool(size, type_size, type_alignment), 696 | destructor(destructor) 697 | {} 698 | 699 | world::components_data::~components_data() { 700 | for (auto ptr : components) { 701 | destructor(ptr, pool); 702 | } 703 | } 704 | 705 | void world::components_data::destroy(void* storage) { 706 | // auto itr = components.find(storage); 707 | // if (itr == components.end()) return; 708 | // components._erase(itr); 709 | // destructor(storage, pool); 710 | for (size_t i = 0; i < components.size(); ++i) { 711 | if (components[i] != storage) continue; 712 | std::swap(components[i], components.back()); 713 | components.pop_back(); 714 | destructor(storage, pool); 715 | return; 716 | } 717 | } 718 | 719 | // entity::component_container_t* world::create_entity_component_container() { 720 | // return m_entity_component_containers.create(nullptr); 721 | // } 722 | // 723 | // void world::destroy_entity_component_container(entity::component_container_t* cont) { 724 | // m_entity_component_containers.destroy(cont); 725 | // } 726 | 727 | typeless_pool::typeless_pool(const size_t size, const size_t piece_size, const size_t piece_aligment) noexcept : 728 | m_size(align_to(size, piece_aligment)), m_piece_size(piece_size), m_piece_aligment(piece_aligment), m_current(0), m_memory(nullptr), m_free_mem(nullptr) 729 | { 730 | assert(m_size >= sizeof(void*)*2); 731 | assert(m_piece_size >= sizeof(void*)); 732 | } 733 | 734 | typeless_pool::~typeless_pool() noexcept { clear(); } 735 | 736 | typeless_pool::typeless_pool(typeless_pool &&move) noexcept : 737 | m_size(move.m_size), m_piece_size(move.m_piece_size), m_piece_aligment(move.m_piece_aligment), m_current(move.m_current), m_memory(move.m_memory), m_free_mem(move.m_free_mem) 738 | { 739 | move.m_memory = nullptr; 740 | move.m_free_mem = nullptr; 741 | } 742 | 743 | typeless_pool & typeless_pool::operator=(typeless_pool &&move) noexcept { 744 | m_size = move.m_size; 745 | m_piece_size = move.m_piece_size; 746 | m_piece_aligment = move.m_piece_aligment; 747 | m_current = move.m_current; 748 | m_memory = move.m_memory; 749 | m_free_mem = move.m_free_mem; 750 | move.m_memory = nullptr; 751 | move.m_free_mem = nullptr; 752 | return *this; 753 | } 754 | 755 | void* typeless_pool::allocate() { 756 | if (m_free_mem != nullptr) { 757 | auto ptr = m_free_mem; 758 | m_free_mem = reinterpret_cast(m_free_mem)[0]; 759 | return ptr; 760 | } 761 | 762 | if (m_memory == nullptr || (m_current + m_piece_size > m_size)) allocate_block(); 763 | auto ptr = &m_memory[m_current]; 764 | m_current += m_piece_size; 765 | return ptr; 766 | } 767 | 768 | void typeless_pool::free(void* block) noexcept { 769 | reinterpret_cast(block)[0] = m_free_mem; 770 | m_free_mem = block; 771 | } 772 | 773 | size_t typeless_pool::size() const noexcept { 774 | size_t summ = 0; 775 | auto mem = m_memory; 776 | while (mem != nullptr) { 777 | summ += m_size; 778 | mem = reinterpret_cast(mem)[0]; 779 | } 780 | 781 | return summ; 782 | } 783 | 784 | size_t typeless_pool::block_size() const noexcept { return m_size; } 785 | size_t typeless_pool::piece_size() const noexcept { return m_piece_size; } 786 | size_t typeless_pool::piece_aligment() const noexcept { return m_piece_aligment; } 787 | 788 | void typeless_pool::allocate_block() { 789 | const size_t offset = std::max(m_piece_aligment, sizeof(void*)); 790 | const size_t final_size = offset + m_size; 791 | auto new_mem = new (std::align_val_t{m_piece_aligment}) char[final_size]; 792 | assert(new_mem != nullptr); 793 | reinterpret_cast(new_mem)[0] = m_memory; 794 | m_memory = new_mem; 795 | m_current = offset; 796 | } 797 | 798 | void typeless_pool::clear() noexcept { 799 | auto mem = m_memory; 800 | while (mem != nullptr) { 801 | auto next = reinterpret_cast(mem)[0]; 802 | ::operator delete[] (mem, std::align_val_t{m_piece_aligment}); 803 | mem = next; 804 | } 805 | } 806 | } 807 | #endif // YACS_IMPLEMENTATION 808 | --------------------------------------------------------------------------------