🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
16 KiB

  1. #ifndef ENTT_ENTITY_PROTOTYPE_HPP
  2. #define ENTT_ENTITY_PROTOTYPE_HPP
  3. #include <tuple>
  4. #include <utility>
  5. #include <cstddef>
  6. #include <type_traits>
  7. #include <unordered_map>
  8. #include "../config/config.h"
  9. #include "registry.hpp"
  10. #include "entity.hpp"
  11. #include "fwd.hpp"
  12. namespace entt {
  13. /**
  14. * @brief Prototype container for _concepts_.
  15. *
  16. * A prototype is used to define a _concept_ in terms of components.<br/>
  17. * Prototypes act as templates for those specific types of an application which
  18. * users would otherwise define through a series of component assignments to
  19. * entities. In other words, prototypes can be used to assign components to
  20. * entities of a registry at once.
  21. *
  22. * @note
  23. * Components used along with prototypes must be copy constructible. Prototypes
  24. * wrap component types with custom types, so they do not interfere with other
  25. * users of the registry they were built with.
  26. *
  27. * @warning
  28. * Prototypes directly use their underlying registries to store entities and
  29. * components for their purposes. Users must ensure that the lifetime of a
  30. * registry and its contents exceed that of the prototypes that use it.
  31. *
  32. * @tparam Entity A valid entity type (see entt_traits for more details).
  33. */
  34. template<typename Entity>
  35. class basic_prototype {
  36. using basic_fn_type = void(const basic_prototype &, basic_registry<Entity> &, const Entity);
  37. using component_type = typename basic_registry<Entity>::component_type;
  38. template<typename Component>
  39. struct component_wrapper { Component component; };
  40. struct component_handler {
  41. basic_fn_type *assign_or_replace;
  42. basic_fn_type *assign;
  43. };
  44. void release() {
  45. if(reg->valid(entity)) {
  46. reg->destroy(entity);
  47. }
  48. }
  49. public:
  50. /*! @brief Registry type. */
  51. using registry_type = basic_registry<Entity>;
  52. /*! @brief Underlying entity identifier. */
  53. using entity_type = Entity;
  54. /*! @brief Unsigned integer type. */
  55. using size_type = std::size_t;
  56. /**
  57. * @brief Constructs a prototype that is bound to a given registry.
  58. * @param ref A valid reference to a registry.
  59. */
  60. basic_prototype(registry_type &ref)
  61. : reg{&ref},
  62. entity{ref.create()}
  63. {}
  64. /**
  65. * @brief Releases all its resources.
  66. */
  67. ~basic_prototype() {
  68. release();
  69. }
  70. /**
  71. * @brief Move constructor.
  72. *
  73. * After prototype move construction, instances that have been moved from
  74. * are placed in a valid but unspecified state. It's highly discouraged to
  75. * continue using them.
  76. *
  77. * @param other The instance to move from.
  78. */
  79. basic_prototype(basic_prototype &&other)
  80. : handlers{std::move(other.handlers)},
  81. reg{other.reg},
  82. entity{other.entity}
  83. {
  84. other.entity = null;
  85. }
  86. /**
  87. * @brief Move assignment operator.
  88. *
  89. * After prototype move assignment, instances that have been moved from are
  90. * placed in a valid but unspecified state. It's highly discouraged to
  91. * continue using them.
  92. *
  93. * @param other The instance to move from.
  94. * @return This prototype.
  95. */
  96. basic_prototype & operator=(basic_prototype &&other) {
  97. if(this != &other) {
  98. auto tmp{std::move(other)};
  99. handlers.swap(tmp.handlers);
  100. std::swap(reg, tmp.reg);
  101. std::swap(entity, tmp.entity);
  102. }
  103. return *this;
  104. }
  105. /**
  106. * @brief Assigns to or replaces the given component of a prototype.
  107. * @tparam Component Type of component to assign or replace.
  108. * @tparam Args Types of arguments to use to construct the component.
  109. * @param args Parameters to use to initialize the component.
  110. * @return A reference to the newly created component.
  111. */
  112. template<typename Component, typename... Args>
  113. Component & set(Args &&... args) {
  114. component_handler handler;
  115. handler.assign_or_replace = [](const basic_prototype &proto, registry_type &other, const Entity dst) {
  116. const auto &wrapper = proto.reg->template get<component_wrapper<Component>>(proto.entity);
  117. other.template assign_or_replace<Component>(dst, wrapper.component);
  118. };
  119. handler.assign = [](const basic_prototype &proto, registry_type &other, const Entity dst) {
  120. if(!other.template has<Component>(dst)) {
  121. const auto &wrapper = proto.reg->template get<component_wrapper<Component>>(proto.entity);
  122. other.template assign<Component>(dst, wrapper.component);
  123. }
  124. };
  125. handlers[reg->template type<Component>()] = handler;
  126. auto &wrapper = reg->template assign_or_replace<component_wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
  127. return wrapper.component;
  128. }
  129. /**
  130. * @brief Removes the given component from a prototype.
  131. * @tparam Component Type of component to remove.
  132. */
  133. template<typename Component>
  134. void unset() ENTT_NOEXCEPT {
  135. reg->template reset<component_wrapper<Component>>(entity);
  136. handlers.erase(reg->template type<Component>());
  137. }
  138. /**
  139. * @brief Checks if a prototype owns all the given components.
  140. * @tparam Component Components for which to perform the check.
  141. * @return True if the prototype owns all the components, false otherwise.
  142. */
  143. template<typename... Component>
  144. bool has() const ENTT_NOEXCEPT {
  145. return reg->template has<component_wrapper<Component>...>(entity);
  146. }
  147. /**
  148. * @brief Returns references to the given components.
  149. *
  150. * @warning
  151. * Attempting to get a component from a prototype that doesn't own it
  152. * results in undefined behavior.<br/>
  153. * An assertion will abort the execution at runtime in debug mode if the
  154. * prototype doesn't own an instance of the given component.
  155. *
  156. * @tparam Component Types of components to get.
  157. * @return References to the components owned by the prototype.
  158. */
  159. template<typename... Component>
  160. decltype(auto) get() const ENTT_NOEXCEPT {
  161. if constexpr(sizeof...(Component) == 1) {
  162. return (std::as_const(*reg).template get<component_wrapper<Component...>>(entity).component);
  163. } else {
  164. return std::tuple<std::add_const_t<Component> &...>{get<Component>()...};
  165. }
  166. }
  167. /*! @copydoc get */
  168. template<typename... Component>
  169. inline decltype(auto) get() ENTT_NOEXCEPT {
  170. if constexpr(sizeof...(Component) == 1) {
  171. return (const_cast<Component &>(std::as_const(*this).template get<Component>()), ...);
  172. } else {
  173. return std::tuple<Component &...>{get<Component>()...};
  174. }
  175. }
  176. /**
  177. * @brief Returns pointers to the given components.
  178. * @tparam Component Types of components to get.
  179. * @return Pointers to the components owned by the prototype.
  180. */
  181. template<typename... Component>
  182. auto try_get() const ENTT_NOEXCEPT {
  183. if constexpr(sizeof...(Component) == 1) {
  184. const auto *wrapper = reg->template try_get<component_wrapper<Component...>>(entity);
  185. return wrapper ? &wrapper->component : nullptr;
  186. } else {
  187. return std::tuple<std::add_const_t<Component> *...>{try_get<Component>()...};
  188. }
  189. }
  190. /*! @copydoc try_get */
  191. template<typename... Component>
  192. inline auto try_get() ENTT_NOEXCEPT {
  193. if constexpr(sizeof...(Component) == 1) {
  194. return (const_cast<Component *>(std::as_const(*this).template try_get<Component>()), ...);
  195. } else {
  196. return std::tuple<Component *...>{try_get<Component>()...};
  197. }
  198. }
  199. /**
  200. * @brief Creates a new entity using a given prototype.
  201. *
  202. * Utility shortcut, equivalent to the following snippet:
  203. *
  204. * @code{.cpp}
  205. * const auto entity = registry.create();
  206. * prototype(registry, entity);
  207. * @endcode
  208. *
  209. * @note
  210. * The registry may or may not be different from the one already used by
  211. * the prototype. There is also an overload that directly uses the
  212. * underlying registry.
  213. *
  214. * @param other A valid reference to a registry.
  215. * @return A valid entity identifier.
  216. */
  217. entity_type create(registry_type &other) const {
  218. const auto entt = other.create();
  219. assign(other, entt);
  220. return entt;
  221. }
  222. /**
  223. * @brief Creates a new entity using a given prototype.
  224. *
  225. * Utility shortcut, equivalent to the following snippet:
  226. *
  227. * @code{.cpp}
  228. * const auto entity = registry.create();
  229. * prototype(entity);
  230. * @endcode
  231. *
  232. * @note
  233. * This overload directly uses the underlying registry as a working space.
  234. * Therefore, the components of the prototype and of the entity will share
  235. * the same registry.
  236. *
  237. * @return A valid entity identifier.
  238. */
  239. inline entity_type create() const {
  240. return create(*reg);
  241. }
  242. /**
  243. * @brief Assigns the components of a prototype to a given entity.
  244. *
  245. * Assigning a prototype to an entity won't overwrite existing components
  246. * under any circumstances.<br/>
  247. * In other words, only those components that the entity doesn't own yet are
  248. * copied over. All the other components remain unchanged.
  249. *
  250. * @note
  251. * The registry may or may not be different from the one already used by
  252. * the prototype. There is also an overload that directly uses the
  253. * underlying registry.
  254. *
  255. * @warning
  256. * Attempting to use an invalid entity results in undefined behavior.<br/>
  257. * An assertion will abort the execution at runtime in debug mode in case of
  258. * invalid entity.
  259. *
  260. * @param other A valid reference to a registry.
  261. * @param dst A valid entity identifier.
  262. */
  263. void assign(registry_type &other, const entity_type dst) const {
  264. for(auto &handler: handlers) {
  265. handler.second.assign(*this, other, dst);
  266. }
  267. }
  268. /**
  269. * @brief Assigns the components of a prototype to a given entity.
  270. *
  271. * Assigning a prototype to an entity won't overwrite existing components
  272. * under any circumstances.<br/>
  273. * In other words, only those components that the entity doesn't own yet are
  274. * copied over. All the other components remain unchanged.
  275. *
  276. * @note
  277. * This overload directly uses the underlying registry as a working space.
  278. * Therefore, the components of the prototype and of the entity will share
  279. * the same registry.
  280. *
  281. * @warning
  282. * Attempting to use an invalid entity results in undefined behavior.<br/>
  283. * An assertion will abort the execution at runtime in debug mode in case of
  284. * invalid entity.
  285. *
  286. * @param dst A valid entity identifier.
  287. */
  288. inline void assign(const entity_type dst) const {
  289. assign(*reg, dst);
  290. }
  291. /**
  292. * @brief Assigns or replaces the components of a prototype for an entity.
  293. *
  294. * Existing components are overwritten, if any. All the other components
  295. * will be copied over to the target entity.
  296. *
  297. * @note
  298. * The registry may or may not be different from the one already used by
  299. * the prototype. There is also an overload that directly uses the
  300. * underlying registry.
  301. *
  302. * @warning
  303. * Attempting to use an invalid entity results in undefined behavior.<br/>
  304. * An assertion will abort the execution at runtime in debug mode in case of
  305. * invalid entity.
  306. *
  307. * @param other A valid reference to a registry.
  308. * @param dst A valid entity identifier.
  309. */
  310. void assign_or_replace(registry_type &other, const entity_type dst) const {
  311. for(auto &handler: handlers) {
  312. handler.second.assign_or_replace(*this, other, dst);
  313. }
  314. }
  315. /**
  316. * @brief Assigns or replaces the components of a prototype for an entity.
  317. *
  318. * Existing components are overwritten, if any. All the other components
  319. * will be copied over to the target entity.
  320. *
  321. * @note
  322. * This overload directly uses the underlying registry as a working space.
  323. * Therefore, the components of the prototype and of the entity will share
  324. * the same registry.
  325. *
  326. * @warning
  327. * Attempting to use an invalid entity results in undefined behavior.<br/>
  328. * An assertion will abort the execution at runtime in debug mode in case of
  329. * invalid entity.
  330. *
  331. * @param dst A valid entity identifier.
  332. */
  333. inline void assign_or_replace(const entity_type dst) const {
  334. assign_or_replace(*reg, dst);
  335. }
  336. /**
  337. * @brief Assigns the components of a prototype to an entity.
  338. *
  339. * Assigning a prototype to an entity won't overwrite existing components
  340. * under any circumstances.<br/>
  341. * In other words, only the components that the entity doesn't own yet are
  342. * copied over. All the other components remain unchanged.
  343. *
  344. * @note
  345. * The registry may or may not be different from the one already used by
  346. * the prototype. There is also an overload that directly uses the
  347. * underlying registry.
  348. *
  349. * @warning
  350. * Attempting to use an invalid entity results in undefined behavior.<br/>
  351. * An assertion will abort the execution at runtime in debug mode in case of
  352. * invalid entity.
  353. *
  354. * @param other A valid reference to a registry.
  355. * @param dst A valid entity identifier.
  356. */
  357. inline void operator()(registry_type &other, const entity_type dst) const ENTT_NOEXCEPT {
  358. assign(other, dst);
  359. }
  360. /**
  361. * @brief Assigns the components of a prototype to an entity.
  362. *
  363. * Assigning a prototype to an entity won't overwrite existing components
  364. * under any circumstances.<br/>
  365. * In other words, only the components that the entity doesn't own yet are
  366. * copied over. All the other components remain unchanged.
  367. *
  368. * @note
  369. * This overload directly uses the underlying registry as a working space.
  370. * Therefore, the components of the prototype and of the entity will share
  371. * the same registry.
  372. *
  373. * @warning
  374. * Attempting to use an invalid entity results in undefined behavior.<br/>
  375. * An assertion will abort the execution at runtime in debug mode in case of
  376. * invalid entity.
  377. *
  378. * @param dst A valid entity identifier.
  379. */
  380. inline void operator()(const entity_type dst) const ENTT_NOEXCEPT {
  381. assign(*reg, dst);
  382. }
  383. /**
  384. * @brief Creates a new entity using a given prototype.
  385. *
  386. * Utility shortcut, equivalent to the following snippet:
  387. *
  388. * @code{.cpp}
  389. * const auto entity = registry.create();
  390. * prototype(registry, entity);
  391. * @endcode
  392. *
  393. * @note
  394. * The registry may or may not be different from the one already used by
  395. * the prototype. There is also an overload that directly uses the
  396. * underlying registry.
  397. *
  398. * @param other A valid reference to a registry.
  399. * @return A valid entity identifier.
  400. */
  401. inline entity_type operator()(registry_type &other) const ENTT_NOEXCEPT {
  402. return create(other);
  403. }
  404. /**
  405. * @brief Creates a new entity using a given prototype.
  406. *
  407. * Utility shortcut, equivalent to the following snippet:
  408. *
  409. * @code{.cpp}
  410. * const auto entity = registry.create();
  411. * prototype(entity);
  412. * @endcode
  413. *
  414. * @note
  415. * This overload directly uses the underlying registry as a working space.
  416. * Therefore, the components of the prototype and of the entity will share
  417. * the same registry.
  418. *
  419. * @return A valid entity identifier.
  420. */
  421. inline entity_type operator()() const ENTT_NOEXCEPT {
  422. return create(*reg);
  423. }
  424. /**
  425. * @brief Returns a reference to the underlying registry.
  426. * @return A reference to the underlying registry.
  427. */
  428. inline const registry_type & backend() const ENTT_NOEXCEPT {
  429. return *reg;
  430. }
  431. /*! @copydoc backend */
  432. inline registry_type & backend() ENTT_NOEXCEPT {
  433. return const_cast<registry_type &>(std::as_const(*this).backend());
  434. }
  435. private:
  436. std::unordered_map<component_type, component_handler> handlers;
  437. registry_type *reg;
  438. entity_type entity;
  439. };
  440. }
  441. #endif // ENTT_ENTITY_PROTOTYPE_HPP