ecs.hpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. #ifndef MA_POINTLESS_ENTITY_COMPONENT_SYSTEM
  2. #define MA_POINTLESS_ENTITY_COMPONENT_SYSTEM
  3. #include <vector>
  4. #include <memory>
  5. #include <numeric>
  6. #include <limits>
  7. #include <string>
  8. #include "simple/support/meta.hpp"
  9. #include "simple/support/tuple_utils.hpp"
  10. #include "simple/support/function_utils.hpp"
  11. #include "simple/support/rational.hpp"
  12. #include "simple/support/algorithm/utils.hpp"
  13. template <typename... Interfaces>
  14. class entities;
  15. template <typename Base, typename... Interfaces>
  16. // TODO: enable if is_abstract<Base> && has_virtual_destructor<Base>
  17. // and maybe is_abstract<Interfaces>, but not sure
  18. struct object_interface
  19. {
  20. static constexpr size_t type_count = 1 + sizeof...(Interfaces);
  21. };
  22. // TODO: parameterise std::unique_ptr and std::vector templates?? do I need that within a project or between projects, cause in a latter case can get away with using declaration in a configuration header
  23. template <typename... Types>
  24. class components
  25. {
  26. public:
  27. using types = std::tuple<Types...>;
  28. template <typename Element, typename ...Args>
  29. auto& emplace(Args&&... args)
  30. {
  31. using simple::support::meta::find_t;
  32. using simple::support::meta::bind;
  33. using simple::support::meta::make_index_segment;
  34. using simple::support::tuple_car;
  35. using simple::support::tuple_car_t;
  36. using simple::support::tuple_tie_cdr;
  37. using simple::support::tie_subtuple;
  38. using simple::support::is_template_instance_v;
  39. using found = find_t<bind<is_component, size_constant<0>, Element>, types>;
  40. static_assert(found::value != std::tuple_size_v<types>, "Element type not found in known component list.");
  41. if constexpr (is_template_instance_v<object_interface, typename found::type>)
  42. {
  43. auto object_vectors = tie_subtuple(vectors,
  44. make_index_segment<
  45. found::functor::binding::value,
  46. found::type::type_count
  47. >()
  48. );
  49. auto& elements = tuple_car(object_vectors);
  50. auto element = std::make_unique<Element>(std::forward<Args>(args)...);
  51. Element& raw = *element.get();
  52. add_interface(raw, tuple_tie_cdr(object_vectors));
  53. elements.emplace_back(std::move(element)); // assuming raw pointer will not change... that's safe right?
  54. return raw;
  55. }
  56. else
  57. {
  58. return std::get<found::functor::binding::value>(vectors).emplace_back(std::forward<Args>(args)...);
  59. }
  60. }
  61. template <typename Element>
  62. Element& push(Element&& element)
  63. { return emplace<simple::support::remove_cvref_t<Element>>(std::forward<Element>(element)); }
  64. // TODO: return a range ??
  65. template <typename Component>
  66. const auto& get() const noexcept
  67. {
  68. using simple::support::meta::find_v;
  69. return std::get<find_v<Component, flat_types>>(vectors);
  70. }
  71. std::string log_sizes() const
  72. {
  73. std::string info;
  74. simple::support::transform([&](auto& v){
  75. info += std::to_string(v.size()) + ": " + typeid(typename std::remove_reference_t<decltype(v)>::value_type).name() + '\n';
  76. }, vectors);
  77. return info;
  78. }
  79. private:
  80. template <typename T>
  81. struct flatten_object_interface { using type = std::tuple<T>; };
  82. template <typename Base, typename... Interfaces>
  83. struct flatten_object_interface<object_interface<Base, Interfaces...>>
  84. { using type = std::tuple<std::unique_ptr<Base>, Interfaces*...>; };
  85. template <size_t i> using size_constant = std::integral_constant<size_t, i>;
  86. template <typename flat_index, typename T, typename Component>
  87. struct is_component : std::is_same<T,Component>
  88. {
  89. using binding = size_constant<flat_index{} + !is_component::value>;
  90. };
  91. template <typename flat_index, typename T, typename Base, typename... Interfaces>
  92. struct is_component<flat_index, T, object_interface<Base,Interfaces...>>
  93. {
  94. static constexpr bool value = std::is_base_of_v<Base, T>;
  95. static constexpr size_t increment =
  96. [](bool value)
  97. {
  98. return value
  99. ? 0
  100. : object_interface<Base,Interfaces...>::type_count
  101. ;
  102. }
  103. (is_component::value);
  104. using binding = size_constant<flat_index{} + increment>;
  105. };
  106. using flat_types = simple::support::meta::reconstruct_t<types,flatten_object_interface>;
  107. template <typename T>
  108. using container = std::vector<T>;
  109. template <typename Tuple>
  110. using make_containers_t = simple::support::meta::transform_t<Tuple, container>;
  111. template <typename T>
  112. using container_ref = container<T>&;
  113. template <typename... Ts>
  114. using make_container_refs_t = simple::support::meta::transform_t<std::tuple<Ts...>, container_ref>;
  115. template <typename T>
  116. using iterator = typename std::vector<T>::const_iterator;
  117. using iterators = simple::support::meta::transform_t<flat_types, iterator>;
  118. template <typename El>
  119. void add_interface(El&, std::tuple<>){}
  120. template <typename El, typename In, typename... Rest>
  121. // NOTE: this shorthand can't deduce the In :/
  122. // void add_interface(El& element, make_container_refs_t<In*, Rest...> interfaces)
  123. void add_interface(El& element, std::tuple<container<In*>&, container<Rest>&...> interfaces)
  124. {
  125. using simple::support::tuple_car;
  126. using simple::support::tuple_tie_cdr;
  127. if constexpr (std::is_base_of_v<In, El>)
  128. tuple_car(interfaces).push_back(&element);
  129. add_interface(element,tuple_tie_cdr(interfaces));
  130. }
  131. make_containers_t<flat_types> vectors;
  132. template <typename Component>
  133. auto& get() noexcept
  134. {
  135. using simple::support::meta::find_v;
  136. return std::get<find_v<Component, flat_types>>(vectors);
  137. }
  138. friend class entities<Types...>;
  139. };
  140. using entity_id_t = simple::support::rational<unsigned,
  141. simple::support::meta_constant<unsigned, 1u << (std::numeric_limits<unsigned>::digits/2)>>;
  142. template <typename... Components>
  143. class entities
  144. {
  145. public:
  146. using components_type = components<Components...>;
  147. entities() : _components(), offsets() {}
  148. entities(components_type components) :
  149. _components(std::move(components)),
  150. offsets(simple::support::from_tuple<decltype(offsets)>(simple::support::transform(
  151. [](auto& v) -> entity_size_t {return v.size();}, _components.vectors )))
  152. {}
  153. template <typename T>
  154. struct entity
  155. {
  156. public:
  157. T components;
  158. const entity_id_t id;
  159. entities& world;
  160. bool owning;
  161. ~entity()
  162. {
  163. if(owning)
  164. world.erase(id);
  165. }
  166. entity(entity& other) = delete;
  167. entity& operator=(entity& other) = delete;
  168. entity& operator=(entity&& other) = delete;
  169. entity(entity&& other)
  170. : entity(other.world, std::move(other.components), other.id)
  171. {
  172. other.release();
  173. }
  174. entity& release()
  175. {
  176. owning = false;
  177. return *this;
  178. }
  179. private:
  180. entity(entities& world, T&& components, const entity_id_t& id) :
  181. components(std::forward<T>(components)),
  182. id(id),
  183. world(world),
  184. owning(true)
  185. {}
  186. friend class entities;
  187. };
  188. // TODO; emplace specific components to replace the make function below,
  189. // component type1, args1
  190. // component_type2, args2
  191. // ...
  192. // component_typeN, argsN
  193. //
  194. // maybe use a component helper template, so it looks like this:
  195. // entities.emplace
  196. // (
  197. // component<Type1>(args1...),
  198. // component<Type2>(args2...),
  199. // component<Type3>(args3...),
  200. // component<Type4>(args4...),
  201. // );
  202. // can also sfinae on this helper template.
  203. //
  204. // return a predictable entity object with references to all components and the id
  205. //
  206. // then again do you actually need to make new types of entity at runtime? entity types (which are defined by entity_sizes) should also be compile time, and also also also maybe fixed per id block
  207. // and when not, can still be RLE
  208. auto next_block_id()
  209. {
  210. return entity_id_t{} +
  211. (empty(entity_ids) ? 0 : unsigned(entity_ids.back()+1))
  212. ;
  213. }
  214. template <typename Function>
  215. auto make(Function&& function)
  216. {
  217. return make(entity_id_t{} +
  218. (empty(entity_ids) ? 0 : unsigned(entity_ids.back()))
  219. , std::forward<Function>(function));
  220. }
  221. template <typename Function>
  222. entity<std::invoke_result_t<
  223. Function, components<Components...>&>>
  224. make(entity_id_t block_id, Function&& function)
  225. {
  226. using simple::support::transform;
  227. auto size_before = transform([](const auto& x) {return x.size(); },
  228. _components.vectors);
  229. auto&& product = std::invoke(
  230. std::forward<Function>(function),
  231. _components);
  232. auto [id, pos] = get_id(block_id);
  233. if(pos != entity_ids.end())
  234. {
  235. transform([off = pos - entity_ids.begin()]
  236. (auto& entity_size, auto size_before, auto& component)
  237. {
  238. auto size_pos = entity_size.begin() + off;
  239. std::rotate( // !
  240. component.begin() + std::accumulate(entity_size.begin(), size_pos, 0),
  241. component.begin() + size_before,
  242. component.end()
  243. );
  244. }, entity_sizes, size_before, _components.vectors);
  245. }
  246. transform([off = pos - entity_ids.begin()]
  247. (auto& entity_size, auto size_before, const auto& component)
  248. {
  249. auto size_pos = entity_size.begin() + off;
  250. entity_size.insert(size_pos, component.size() - size_before);
  251. }, entity_sizes, size_before, _components.vectors);
  252. entity_ids.insert(pos,id);
  253. return
  254. {
  255. *this,
  256. std::forward<decltype(product)>(product),
  257. id
  258. };
  259. };
  260. // TODO: swap for entities of same size, to allow stuff like remove erase
  261. // template <typename Element, typename ...Args>
  262. // Element& emplace(Args&&... args)
  263. // {
  264. // return make([&](auto& components) -> auto&
  265. // {
  266. // return
  267. // components.template emplace<Element>
  268. // (std::forward<Args>(args)...);
  269. // }).components;
  270. // }
  271. // TODO: reuse code from find, why is it so hard :/
  272. // TODO: forget() method, that will turn this into a nop (you know, the classic problem: we technically own all objects we hand out, but we pretend that the user owns them instead with owning handles. now the user's destruction pattern might not be very efficient for our arrangement, and we can do nothing about that, exceeeeept when we know we are going to be destroyed as well, then we can ignore user requests to destroy and take care of it all in one go, unfortunately if the user is following RAII diligently, everything will be destroyed before us, so we have to give them a way to indicate this doomsday scenario manually)
  273. template <typename Projection>
  274. void erase(entity_id_t first_id, entity_id_t last_id, Projection proj)
  275. {
  276. using simple::support::transform_arg;
  277. auto found = simple::support::range{
  278. lower_bound(begin(entity_ids), end(entity_ids), first_id, transform_arg{proj}),
  279. upper_bound(begin(entity_ids), end(entity_ids), last_id, transform_arg{proj})
  280. };
  281. assert(found.valid());
  282. using simple::support::transform;
  283. transform([&](auto& components, auto& sizes, auto entities_offset)
  284. {
  285. auto found_size = simple::support::map_range(found, sizes, begin(entity_ids));
  286. auto offset_begin = std::accumulate(begin(sizes), found_size.begin(), 0);
  287. auto offset_end = std::accumulate(found_size.begin(), found_size.end(), offset_begin);
  288. auto components_range = simple::support::get_iterator_range
  289. (
  290. components,
  291. simple::support::range{offset_begin, offset_end}
  292. ) + entities_offset
  293. ;
  294. components.erase(components_range.begin(), components_range.end());
  295. sizes.erase(found_size.begin(), found_size.end());
  296. }, _components.vectors, entity_sizes, offsets);
  297. entity_ids.erase(found.begin(), found.end());
  298. }
  299. void erase(entity_id_t first_id, entity_id_t last_id)
  300. { erase(first_id, last_id, [](auto x){ return x; }); }
  301. void erase(entity_id_t id) { erase(id, id); }
  302. void erase_block(entity_id_t first_id, entity_id_t last_id)
  303. { erase(first_id, last_id, [](auto x){ return unsigned(x); }); }
  304. void erase_block(entity_id_t id) { erase_block(id, id); }
  305. template <typename Component>
  306. decltype(auto) get_all() const noexcept
  307. { return _components.template get<Component>(); }
  308. template <typename Component>
  309. auto get() const noexcept
  310. {
  311. using simple::support::make_range;
  312. using simple::support::meta::find_v;
  313. auto components = make_range(_components.template get<Component>());
  314. components.begin() += offsets[find_v<Component, types>];
  315. return components;
  316. }
  317. // TODO: support a subset of components, not just one or all
  318. template <typename Component = void, typename Projection = void>
  319. auto find(entity_id_t id, Projection proj = [](entity_id_t x) { return x; }) noexcept
  320. {
  321. using simple::support::transform_arg;
  322. auto block_pair = std::equal_range(begin(entity_ids), end(entity_ids), id, transform_arg{proj});
  323. auto block = simple::support::range{block_pair.first, block_pair.second};
  324. using simple::support::transform;
  325. using simple::support::meta::find_v;
  326. constexpr auto component_index = find_v<Component, types>;
  327. if constexpr (component_index == std::tuple_size_v<types>)
  328. {
  329. return transform([&block,this](auto& components, auto& sizes, auto offset)
  330. {
  331. // TODO: remove this->, when stupod clang 11+ stops complaining about unused this ffs
  332. return this->get_components(block,components,sizes,offset);
  333. }, _components.vectors, entity_sizes, offsets);
  334. }
  335. else
  336. {
  337. return get_components
  338. (
  339. block,
  340. _components.template get<Component>(),
  341. entity_sizes[component_index],
  342. offsets[component_index]
  343. );
  344. }
  345. }
  346. // TODO: support a subset of components, not just one or all
  347. template <typename Component = void>
  348. auto find_block(entity_id_t id) noexcept
  349. {
  350. return find(id, [](auto x){ return unsigned(x); });
  351. }
  352. template <typename Component, typename It, size_t Index = 0>
  353. class component_iterator
  354. {
  355. public:
  356. using iterator = It;
  357. using difference_type = typename std::iterator_traits<iterator>::difference_type;
  358. using value_type = Component;
  359. using pointer = Component*;
  360. using reference = Component&;
  361. using iterator_category = typename std::iterator_traits<iterator>::iterator_category;
  362. component_iterator(It base, entities& world) :
  363. base(base), world(&world)
  364. {}
  365. constexpr decltype(auto) operator*() const
  366. { return *(world->find<Component>(base->id).begin() + Index); }
  367. constexpr auto operator->() const
  368. { return world->find<Component>(base->id).begin() + Index; }
  369. constexpr decltype(auto) operator*()
  370. { return *(world->find<Component>(base->id).begin() + Index); }
  371. constexpr auto operator->()
  372. { return world->find<Component>(base->id).begin() + Index; }
  373. constexpr component_iterator& operator++()
  374. {
  375. ++base;
  376. return *this;
  377. }
  378. constexpr component_iterator operator++(int)
  379. {
  380. component_iterator ret = *this;
  381. ++(*this);
  382. return ret;
  383. }
  384. constexpr component_iterator& operator+=(difference_type d)
  385. {
  386. base += d;
  387. return *this;
  388. }
  389. constexpr component_iterator& operator-=(difference_type d)
  390. {
  391. base -= d;
  392. return *this;
  393. }
  394. constexpr friend component_iterator operator+(const component_iterator& one, difference_type d)
  395. {
  396. return component_iterator(one) += d;
  397. }
  398. constexpr friend component_iterator operator+(difference_type d, const component_iterator& one)
  399. {
  400. return component_iterator(one) += d;
  401. }
  402. constexpr friend component_iterator operator-(const component_iterator& one, difference_type d)
  403. {
  404. return component_iterator(one) -= d;
  405. }
  406. constexpr friend component_iterator operator-(difference_type d, const component_iterator& one)
  407. {
  408. return component_iterator(one) -= d;
  409. }
  410. constexpr friend difference_type operator-(const component_iterator& one, const component_iterator& other)
  411. {
  412. return one.base - other.base;
  413. }
  414. constexpr friend bool operator==(const component_iterator& one, const component_iterator& other)
  415. {
  416. return one.base == other.base && one.world == other.world;
  417. }
  418. constexpr friend bool operator!=(const component_iterator& one, const component_iterator& other)
  419. {
  420. return !(one == other);
  421. }
  422. private:
  423. iterator base;
  424. entities* world;
  425. };
  426. // template <typename Component, typename It>
  427. // class iterator
  428. // {
  429. // public:
  430. // using difference_type = entity id difference
  431. // using value_type = tuple of assignable ranges
  432. // using pointer = ???
  433. // using reference = value_type???
  434. // using iterator_category = bidirectional, could be random access if limited to a block and if block is guaranteed to be all same entity type then we can store single element size for whole block and use multiplication yay... now go do it
  435. //
  436. // iterator(entities& world) :
  437. // world(&world)
  438. // {}
  439. //
  440. // iterator(entity_id_t id, entities& world) :
  441. // base(base), world(&world)
  442. // {}
  443. //
  444. // private:
  445. // entities* world;
  446. // };
  447. template <typename Component, size_t Index = 0, typename EntityIt = entity_id_t*>
  448. auto get_component_iterator(EntityIt i)
  449. {
  450. return component_iterator<Component,EntityIt, Index>{i, *this};
  451. }
  452. std::string log_sizes() const
  453. {
  454. // TODO: entity ids, entity_sizes, offsets
  455. return _components.log_sizes();
  456. }
  457. private:
  458. using entity_size_t = unsigned short;
  459. template <typename ComponentVector>
  460. auto get_components
  461. (
  462. simple::support::range<std::vector<entity_id_t>::const_iterator> block,
  463. ComponentVector& components,
  464. const std::vector<entity_size_t>& sizes,
  465. const entity_size_t entities_offset
  466. )
  467. {
  468. auto found_size = simple::support::map_range(block, sizes, begin(entity_ids));
  469. auto offset_begin = std::accumulate(begin(sizes), found_size.begin(), 0);
  470. auto offset_end = std::accumulate(found_size.begin(), found_size.end(), offset_begin);
  471. return simple::support::get_iterator_range(
  472. components,
  473. simple::support::range{offset_begin, offset_end}
  474. ) + entities_offset;
  475. };
  476. std::pair<entity_id_t, std::vector<entity_id_t>::iterator> get_id(entity_id_t block_id)
  477. {
  478. using simple::support::transform_arg;
  479. auto block_end = std::upper_bound
  480. (
  481. begin(entity_ids),
  482. end(entity_ids),
  483. block_id,
  484. transform_arg{[](entity_id_t id) { return unsigned(id); }}
  485. );
  486. if(block_end == begin(entity_ids))
  487. return {block_id, block_end}; // block is empty, block id also is the first id
  488. entity_id_t last_in_block = *(block_end - 1);
  489. if(unsigned(last_in_block) != unsigned(block_id))
  490. return {block_id, block_end}; // last element is not from this block, so again it's empty
  491. using simple::support::next;
  492. auto next_id = next(last_in_block);
  493. if(next_id == block_id + 1) // overflew into next block
  494. {
  495. // scramble to find a free id
  496. auto hole = std::adjacent_find(std::reverse_iterator(block_end), entity_ids.rend(),
  497. [block_id](auto next_id, auto prev_id)
  498. {
  499. return next(prev_id) != next_id || unsigned(prev_id) != unsigned(block_id);
  500. }
  501. );
  502. assert(hole != entity_ids.rend() && *hole == block_id); // gulp
  503. auto next_pos = std::next(hole).base();
  504. return {next(*std::prev(next_pos)), next_pos};
  505. }
  506. return {next_id, block_end};
  507. }
  508. using components_t = components<Components...>;
  509. using types = typename components_t::flat_types;
  510. components_t _components;
  511. std::vector<entity_id_t> entity_ids;
  512. std::array<
  513. std::vector<entity_size_t>,
  514. std::tuple_size_v<types>
  515. > entity_sizes;
  516. std::array<
  517. entity_size_t,
  518. std::tuple_size_v<types>
  519. > offsets;
  520. };
  521. #endif /* end of include guard */