neoGFX
Cross-platform C++ app/game engine
component.hpp
Go to the documentation of this file.
1 // component.hpp
2 /*
3  neogfx C++ GUI Library
4  Copyright (c) 2018 Leigh Johnston. All Rights Reserved.
5 
6  This program is free software: you can redistribute it and / or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #pragma once
20 
21 #include <neogfx/neogfx.hpp>
22 #include <vector>
23 #include <unordered_map>
24 #include <string>
26 #include <neogfx/game/ecs_ids.hpp>
28 
29 namespace neogfx::game
30 {
31  class i_ecs;
32 
33  template <typename Data>
34  struct shared;
35 
36  template <typename T>
37  inline bool batchable(const std::optional<T>& lhs, const std::optional<T>& rhs)
38  {
39  return !!lhs == !!rhs && (lhs == std::nullopt || batchable(*lhs, *rhs));
40  }
41 
42  template <typename Data>
43  inline bool batchable(const shared<Data>& lhs, const shared<Data>& rhs)
44  {
45  if (!!lhs.ptr != !!rhs.ptr)
46  return false;
47  if (lhs.ptr == nullptr)
48  return true;
49  return batchable(*lhs.ptr, *rhs.ptr);
50  }
51 
52  namespace detail
53  {
54  template <typename Data>
56  {
58  typedef data_type value_type;
59  typedef std::vector<value_type> container_type;
60  static constexpr bool optional = false;
61  };
62 
63  template <typename Data>
65  {
67  typedef std::optional<data_type> value_type;
68  typedef std::vector<value_type> container_type;
69  static constexpr bool optional = true;
70  };
71 
72  template <typename Data>
74  {
76  typedef data_type mapped_type;
77  typedef std::pair<const std::string, mapped_type> value_type;
78  typedef std::unordered_map<std::string, mapped_type> container_type;
79  static constexpr bool optional = false;
80  };
81 
82  template <typename Data>
84  {
86  typedef std::optional<data_type> mapped_type;
87  typedef std::pair<const std::string, mapped_type> value_type;
88  typedef std::unordered_map<std::string, mapped_type> container_type;
89  static constexpr bool optional = true;
90  };
91  }
92 
93  template <typename Data, typename Base>
94  class static_component_base : public Base
95  {
97  public:
98  struct entity_record_not_found : std::logic_error { entity_record_not_found() : std::logic_error("neogfx::static_component::entity_record_not_found") {} };
99  struct invalid_data : std::logic_error { invalid_data() : std::logic_error("neogfx::static_component::invalid_data") {} };
100  public:
102  typedef typename data_type::meta data_meta_type;
105  public:
107  iEcs{ aEcs }
108  {
109  }
110  static_component_base(const self_type& aOther) :
111  iEcs{ aOther.iEcs },
112  iComponentData{ aOther.iComponentData }
113  {
114  }
115  public:
116  self_type& operator=(const self_type& aRhs)
117  {
118  iComponentData = aRhs.iComponentData;
119  return *this;
120  }
121  public:
122  game::i_ecs& ecs() const override
123  {
124  return iEcs;
125  }
126  const component_id& id() const override
127  {
128  return data_meta_type::id();
129  }
130  public:
131  std::recursive_mutex& mutex() const
132  {
133  return iMutex;
134  }
135  public:
136  bool is_data_optional() const override
137  {
139  }
140  const i_string& name() const override
141  {
142  return data_meta_type::name();
143  }
144  uint32_t field_count() const override
145  {
146  return data_meta_type::field_count();
147  }
148  component_data_field_type field_type(uint32_t aFieldIndex) const override
149  {
150  return data_meta_type::field_type(aFieldIndex);
151  }
152  neolib::uuid field_type_id(uint32_t aFieldIndex) const override
153  {
154  return data_meta_type::field_type_id(aFieldIndex);
155  }
156  const i_string& field_name(uint32_t aFieldIndex) const override
157  {
158  return data_meta_type::field_name(aFieldIndex);
159  }
160  public:
161  const component_data_t& component_data() const
162  {
163  return iComponentData;
164  }
165  component_data_t& component_data()
166  {
167  return iComponentData;
168  }
169  const value_type& operator[](typename component_data_t::size_type aIndex) const
170  {
171  return *std::next(component_data().begin(), aIndex);
172  }
173  value_type& operator[](typename component_data_t::size_type aIndex)
174  {
175  return *std::next(component_data().begin(), aIndex);
176  }
177  private:
178  game::i_ecs& iEcs;
179  mutable std::recursive_mutex iMutex;
180  component_data_t iComponentData;
181  };
182 
183  template <typename Data>
184  class static_component : public static_component_base<Data, i_component>
185  {
188  public:
189  using typename base_type::entity_record_not_found;
190  using typename base_type::invalid_data;
191  public:
192  typedef typename base_type::data_type data_type;
196  typedef std::vector<entity_id> component_data_entities_t;
197  typedef typename component_data_t::size_type reverse_index_t;
198  typedef std::vector<reverse_index_t> reverse_indices_t;
199  typedef std::vector<reverse_index_t> free_indices_t;
200  public:
201  typedef std::unique_ptr<self_type> snapshot_ptr;
203  {
204  public:
205  scoped_snapshot(self_type& aOwner) :
206  iOwner{ aOwner }
207  {
208  std::scoped_lock<std::recursive_mutex> lock{ iOwner.mutex() };
209  ++iOwner.iUsingSnapshot;
210  }
212  iOwner{ aOther.iOwner }
213  {
214  std::scoped_lock<std::recursive_mutex> lock{ iOwner.mutex() };
215  ++iOwner.iUsingSnapshot;
216  }
218  {
219  std::scoped_lock<std::recursive_mutex> lock{ iOwner.mutex() };
220  --iOwner.iUsingSnapshot;
221  }
222  public:
223  self_type& data() const
224  {
225  return *iOwner.iSnapshot;
226  }
227  private:
228  self_type& iOwner;
229  };
230  private:
231  static constexpr reverse_index_t invalid = ~reverse_index_t{};
232  public:
234  base_type{ aEcs },
235  iHaveSnapshot{ false },
236  iUsingSnapshot{ 0u }
237  {
238  }
239  static_component(const self_type& aOther) :
240  base_type{ aOther },
241  iEntities{ aOther.iEntities },
242  iFreeIndices{ aOther.iFreeIndices },
243  iReverseIndices{ aOther.iReverseIndices },
244  iHaveSnapshot{ false },
245  iUsingSnapshot{ 0u }
246  {
247  }
248  public:
249  self_type& operator=(const self_type& aRhs)
250  {
251  base_type::operator=(aRhs);
252  iEntities = aRhs.iEntities;
253  iFreeIndices = aRhs.iFreeIndices;
254  iReverseIndices = aRhs.iReverseIndices;
255  return *this;
256  }
257  public:
258  using base_type::ecs;
259  using base_type::id;
260  using base_type::mutex;
261  public:
262  using base_type::is_data_optional;
263  using base_type::name;
264  using base_type::field_count;
265  using base_type::field_type;
266  using base_type::field_type_id;
267  using base_type::field_name;
268  public:
269  using base_type::component_data;
270  using base_type::operator[];
271  public:
272  entity_id entity(const data_type& aData) const
273  {
274  const data_type* lhs = &aData;
275  const data_type* rhs = &base_type::component_data()[0];
276  auto index = lhs - rhs;
277  return entities()[index];
278  }
279  const component_data_entities_t& entities() const
280  {
281  return iEntities;
282  }
283  component_data_entities_t& entities()
284  {
285  return iEntities;
286  }
287  const reverse_indices_t& reverse_indices() const
288  {
289  return iReverseIndices;
290  }
291  reverse_indices_t& reverse_indices()
292  {
293  return iReverseIndices;
294  }
295  reverse_index_t reverse_index(entity_id aEntity) const
296  {
297  if (reverse_indices().size() > aEntity)
298  return reverse_indices()[aEntity];
299  return invalid;
300  }
301  bool has_entity_record(entity_id aEntity) const override
302  {
303  return reverse_index(aEntity) != invalid;
304  }
305  const data_type& entity_record(entity_id aEntity) const
306  {
307  auto reverseIndex = reverse_index(aEntity);
308  if (reverseIndex == invalid)
309  throw entity_record_not_found();
310  return base_type::component_data()[reverseIndex];
311  }
312  value_type& entity_record(entity_id aEntity)
313  {
314  return const_cast<value_type&>(to_const(*this).entity_record(aEntity));
315  }
316  void destroy_entity_record(entity_id aEntity) override
317  {
318  auto reverseIndex = reverse_index(aEntity);
319  if (reverseIndex == invalid)
320  throw entity_record_not_found();
321  if constexpr (data_meta_type::has_handles)
322  data_meta_type::free_handles(base_type::component_data()[reverseIndex], ecs());
323  entities()[reverseIndex] = null_entity;
324  reverse_indices()[aEntity] = invalid;
325  free_indices().push_back(reverseIndex);
326  if (have_snapshot())
327  {
328  std::scoped_lock<std::recursive_mutex> lock{ mutex() };
329  if (have_snapshot())
330  {
331  auto ss = snapshot();
332  ss.data().destroy_entity_record(aEntity);
333  }
334  }
335 
336  // todo: sort/remove component data of dead entities
337  }
338  value_type& populate(entity_id aEntity, const value_type& aData)
339  {
340  return do_populate(aEntity, aData);
341  }
342  value_type& populate(entity_id aEntity, value_type&& aData)
343  {
344  return do_populate(aEntity, aData);
345  }
346  void* populate(entity_id aEntity, const void* aComponentData, std::size_t aComponentDataSize) override
347  {
348  if ((aComponentData == nullptr && !is_data_optional()) || aComponentDataSize != sizeof(data_type))
349  throw invalid_data();
350  if (aComponentData != nullptr)
351  return &do_populate(aEntity, *static_cast<const data_type*>(aComponentData));
352  else
353  return &do_populate(aEntity, value_type{}); // empty optional
354  }
355  public:
356  bool have_snapshot() const
357  {
358  return iHaveSnapshot;
359  }
361  {
362  std::scoped_lock<std::recursive_mutex> lock{ mutex() };
363  if (!iUsingSnapshot)
364  {
365  if (iSnapshot == nullptr)
366  iSnapshot = snapshot_ptr{ new self_type{*this} };
367  else
368  *iSnapshot = *this;
369  iHaveSnapshot = true;
370  }
371  }
373  {
374  std::scoped_lock<std::recursive_mutex> lock{ mutex() };
375  return scoped_snapshot{ *this };
376  }
377  template <typename Compare>
378  void sort(Compare aComparator)
379  {
380  neolib::intrusive_sort(base_type::component_data().begin(), base_type::component_data().end(),
381  [this](auto lhs, auto rhs)
382  {
383  std::swap(*lhs, *rhs);
384  auto lhsIndex = lhs - base_type::component_data().begin();
385  auto rhsIndex = rhs - base_type::component_data().begin();
386  auto& lhsEntity = entities()[lhsIndex];
387  auto& rhsEntity = entities()[rhsIndex];
388  std::swap(lhsEntity, rhsEntity);
389  if (lhsEntity != invalid)
390  reverse_indices()[lhsEntity] = lhsIndex;
391  if (rhsEntity != invalid)
392  reverse_indices()[rhsEntity] = rhsIndex;
393  }, aComparator);
394  }
395  private:
396  free_indices_t& free_indices()
397  {
398  return iFreeIndices;
399  }
400  template <typename T>
401  value_type& do_populate(entity_id aEntity, T&& aComponentData)
402  {
403  if (has_entity_record(aEntity))
404  return do_update(aEntity, aComponentData);
405  reverse_index_t reverseIndex = invalid;
406  if (!free_indices().empty())
407  {
408  reverseIndex = free_indices().back();
409  free_indices().pop_back();
410  base_type::component_data()[reverseIndex] = std::forward<T>(aComponentData);
411  entities()[reverseIndex] = aEntity;
412  }
413  else
414  {
415  reverseIndex = base_type::component_data().size();
416  base_type::component_data().push_back(std::forward<T>(aComponentData));
417  try
418  {
419  entities().push_back(aEntity);
420  }
421  catch (...)
422  {
423  base_type::component_data().pop_back();
424  throw;
425  }
426  }
427  try
428  {
429  if (reverse_indices().size() <= aEntity)
430  reverse_indices().resize(aEntity + 1, invalid);
431  reverse_indices()[aEntity] = reverseIndex;
432  }
433  catch (...)
434  {
435  entities()[reverseIndex] = null_entity;
436  throw;
437  }
438  return base_type::component_data()[reverseIndex];
439  }
440  template <typename T>
441  value_type& do_update(entity_id aEntity, T&& aComponentData)
442  {
443  auto& record = entity_record(aEntity);
444  record = aComponentData;
445  return record;
446  }
447  private:
448  component_data_entities_t iEntities;
449  free_indices_t iFreeIndices;
450  reverse_indices_t iReverseIndices;
451  mutable std::atomic<bool> iHaveSnapshot;
452  mutable std::atomic<uint32_t> iUsingSnapshot;
453  mutable snapshot_ptr iSnapshot;
454  };
455 
456  template <typename Data>
457  struct shared
458  {
460  const mapped_type* ptr;
461  };
462 
463  template <typename Data>
464  class static_shared_component : public static_component_base<shared<ecs_data_type_t<Data>>, i_shared_component>
465  {
468  public:
469  using typename base_type::entity_record_not_found;
470  using typename base_type::invalid_data;
471  public:
472  typedef typename base_type::data_type data_type;
476  typedef typename component_data_t::mapped_type mapped_type;
477  public:
479  base_type{ aEcs }
480  {
481  }
482  public:
483  using base_type::ecs;
484  using base_type::id;
485  using base_type::mutex;
486  public:
487  using base_type::is_data_optional;
488  using base_type::name;
489  using base_type::field_count;
490  using base_type::field_type;
491  using base_type::field_type_id;
492  using base_type::field_name;
493  public:
494  using base_type::component_data;
495  public:
496  const mapped_type& operator[](typename component_data_t::size_type aIndex) const
497  {
498  return std::next(component_data().begin(), aIndex)->second;
499  }
500  mapped_type& operator[](typename component_data_t::size_type aIndex)
501  {
502  return std::next(component_data().begin(), aIndex)->second;
503  }
504  const mapped_type& operator[](const std::string& aName) const
505  {
506  return component_data()[aName];
507  }
508  mapped_type& operator[](const std::string& aName)
509  {
510  return component_data()[aName];
511  }
512  public:
513  mapped_type& populate(const std::string& aName, const mapped_type& aData)
514  {
515  base_type::component_data()[aName] = aData;
516  auto& result = base_type::component_data()[aName];
517  if constexpr (mapped_type::meta::has_updater)
518  mapped_type::meta::update(result, ecs(), null_entity);
519  return result;
520  }
521  mapped_type& populate(const std::string& aName, mapped_type&& aData)
522  {
523  base_type::component_data()[aName] = std::move(aData);
524  auto& result = base_type::component_data()[aName];
525  if constexpr (mapped_type::meta::has_updater)
526  mapped_type::meta::update(result, ecs(), null_entity);
527  return result;
528  }
529  void* populate(const std::string& aName, const void* aComponentData, std::size_t aComponentDataSize) override
530  {
531  if ((aComponentData == nullptr && !is_data_optional()) || aComponentDataSize != sizeof(mapped_type))
532  throw invalid_data();
533  if (aComponentData != nullptr)
534  return &populate(aName, *static_cast<const mapped_type*>(aComponentData));
535  else
536  return &populate(aName, mapped_type{}); // empty optional
537  }
538  };
539 }
component_data_t::mapped_type mapped_type
Definition: component.hpp:476
constexpr entity_id null_entity
Definition: ecs_ids.hpp:35
static_component_base(const self_type &aOther)
Definition: component.hpp:110
value_type & populate(entity_id aEntity, value_type &&aData)
Definition: component.hpp:342
const value_type & operator[](typename component_data_t::size_type aIndex) const
Definition: component.hpp:169
const component_id & id() const override
Definition: component.hpp:126
const component_data_entities_t & entities() const
Definition: component.hpp:279
const mapped_type & operator[](typename component_data_t::size_type aIndex) const
Definition: component.hpp:496
std::remove_cv_t< std::remove_reference_t< _Ty > > ecs_data_type_t
void swap(any &aLhs, any &aRhs)
Definition: any.hpp:205
std::unordered_map< std::string, mapped_type > container_type
Definition: component.hpp:78
base_type::data_meta_type data_meta_type
Definition: component.hpp:193
mapped_type & operator[](typename component_data_t::size_type aIndex)
Definition: component.hpp:500
neolib::uuid field_type_id(uint32_t aFieldIndex) const override
Definition: component.hpp:152
base_type::data_meta_type data_meta_type
Definition: component.hpp:473
entity_id entity(const data_type &aData) const
Definition: component.hpp:272
base_type::component_data_t component_data_t
Definition: component.hpp:195
self_type & operator=(const self_type &aRhs)
Definition: component.hpp:116
mapped_type & operator[](const std::string &aName)
Definition: component.hpp:508
detail::crack_component_data< shared< neogfx::game::mesh > >::mapped_type mapped_type
Definition: component.hpp:459
const i_string & field_name(uint32_t aFieldIndex) const override
Definition: component.hpp:156
static_shared_component(game::i_ecs &aEcs)
Definition: component.hpp:478
void * populate(entity_id aEntity, const void *aComponentData, std::size_t aComponentDataSize) override
Definition: component.hpp:346
void sort(Compare aComparator)
Definition: component.hpp:378
std::recursive_mutex & mutex() const
Definition: component.hpp:131
component_data_field_type field_type(uint32_t aFieldIndex) const override
Definition: component.hpp:148
component_data_t & component_data()
Definition: component.hpp:165
std::unordered_map< std::string, mapped_type > container_type
Definition: component.hpp:88
uint32_t id
Definition: primitives.hpp:31
reverse_index_t reverse_index(entity_id aEntity) const
Definition: component.hpp:295
std::pair< const std::string, mapped_type > value_type
Definition: component.hpp:77
std::vector< reverse_index_t > reverse_indices_t
Definition: component.hpp:198
base_type::component_data_t component_data_t
Definition: component.hpp:475
bool batchable(const std::optional< T > &lhs, const std::optional< T > &rhs)
Definition: component.hpp:37
std::unique_ptr< self_type > snapshot_ptr
Definition: component.hpp:201
basic_size< coordinate > size
static_component(const self_type &aOther)
Definition: component.hpp:239
const reverse_indices_t & reverse_indices() const
Definition: component.hpp:287
mapped_type & populate(const std::string &aName, const mapped_type &aData)
Definition: component.hpp:513
to_const_reference_t< T > to_const(T &&object)
Definition: neolib.hpp:59
std::vector< value_type > container_type
Definition: component.hpp:59
scoped_snapshot(const scoped_snapshot &aOther)
Definition: component.hpp:211
self_type & operator=(const self_type &aRhs)
Definition: component.hpp:249
reverse_indices_t & reverse_indices()
Definition: component.hpp:291
void destroy_entity_record(entity_id aEntity) override
Definition: component.hpp:316
value_type & operator[](typename component_data_t::size_type aIndex)
Definition: component.hpp:173
mapped_type & populate(const std::string &aName, mapped_type &&aData)
Definition: component.hpp:521
void intrusive_sort(RandomIt first, RandomIt last, Swapper swapper, Compare comp)
base_type::data_type data_type
Definition: component.hpp:192
component_data_entities_t & entities()
Definition: component.hpp:283
component_data_t::size_type reverse_index_t
Definition: component.hpp:197
std::vector< entity_id > component_data_entities_t
Definition: component.hpp:196
static_component_base(game::i_ecs &aEcs)
Definition: component.hpp:106
const component_data_t & component_data() const
Definition: component.hpp:161
value_type & entity_record(entity_id aEntity)
Definition: component.hpp:312
value_type & populate(entity_id aEntity, const value_type &aData)
Definition: component.hpp:338
detail::crack_component_data< Data >::container_type component_data_t
Definition: component.hpp:104
id_t entity_id
Definition: ecs_ids.hpp:34
bool has_entity_record(entity_id aEntity) const override
Definition: component.hpp:301
const i_string & name() const override
Definition: component.hpp:140
scoped_snapshot snapshot()
Definition: component.hpp:372
static_component(game::i_ecs &aEcs)
Definition: component.hpp:233
uint32_t field_count() const override
Definition: component.hpp:144
detail::crack_component_data< Data >::value_type value_type
Definition: component.hpp:103
base_type::value_type value_type
Definition: component.hpp:194
void * populate(const std::string &aName, const void *aComponentData, std::size_t aComponentDataSize) override
Definition: component.hpp:529
std::vector< reverse_index_t > free_indices_t
Definition: component.hpp:199
game::i_ecs & ecs() const override
Definition: component.hpp:122
const mapped_type * ptr
Definition: component.hpp:460
const mapped_type & operator[](const std::string &aName) const
Definition: component.hpp:504
const data_type & entity_record(entity_id aEntity) const
Definition: component.hpp:305
detail::crack_component_data< Data >::data_type data_type
Definition: component.hpp:101
bool is_data_optional() const override
Definition: component.hpp:136
base_type::value_type value_type
Definition: component.hpp:474