NasNas
An intuitive and beginner friendly 2D game framework for C++
Storage.hpp
1 // Created by Modar Nasser on 15/08/2021.
2 
3 #pragma once
4 
5 #include <map>
6 #include <stdexcept>
7 #include <string>
8 #include <type_traits>
9 #include <vector>
10 
11 namespace ns::ecs {
12  using Entity = unsigned long;
13 }
14 
15 namespace ns::ecs::detail {
16  using UID = unsigned long;
17  inline UID get_next_id() {
18  static UID counter = 0;
19  return counter++;
20  }
21 
22  template <class T>
23  auto getTypeId() -> decltype(auto) {
24  static auto id = get_next_id();
25  return id;
26  }
27 
28  template <typename T, typename = std::enable_if<std::is_integral_v<T>>>
29  struct sparse_set {
30  virtual ~sparse_set() = default;
31  auto data() const -> const std::vector<T>& {
32  return m_packed;
33  }
34 
35  void append(T elmnt) {
36  m_packed.emplace_back(elmnt);
37  m_sparse[elmnt] = m_packed.size() - 1;
38  }
39 
40  virtual void remove(T elmnt) {
41  const auto index = m_sparse[elmnt];
42 
43  if (index != m_packed.size() - 1) {
44  m_packed[index] = m_packed[m_packed.size() - 1];
45  m_sparse[m_packed[index]] = index;
46  }
47 
48  m_packed.pop_back();
49  m_sparse.erase(elmnt);
50  }
51 
52  auto size() const -> std::size_t {
53  return m_packed.size();
54  }
55 
56  auto contains(T elmnt) const -> bool {
57  return m_sparse.find(elmnt) != m_sparse.end();
58  }
59 
60  auto index(T elmnt) const -> std::size_t {
61  return m_sparse.at(elmnt);
62  }
63 
64  private:
65  std::map<T, size_t> m_sparse; // map[elmnt] = index
66  std::vector<T> m_packed; // vec[index] = elmnt
67  };
68 
69  template <typename TEntity, typename...>
71  using super = sparse_set<TEntity>;
72  };
73 
74  template <typename TEntity, typename TComp>
77 
78  auto components() -> std::vector<TComp>& {
79  return m_components;
80  }
81 
82  template <typename ...Targs>
83  auto add(TEntity ent, Targs&& ...args) -> TComp& {
84  if (this->contains(ent))
85  return get(ent);
86 
87  super::append(ent);
88  if constexpr(sizeof...(args) == 0)
89  return m_components.emplace_back();
90  else if constexpr(std::is_aggregate_v<TComp>) {
91  m_components.push_back({std::forward<Targs>(args)...});
92  return m_components.back();
93  }
94  else
95  return m_components.emplace_back(std::forward<Targs>(args)...);
96  }
97 
98  void remove(TEntity ent) override {
99  if (!this->contains(ent))
100  return;
101 
102  const auto i = this->index(ent);
103 
104  if (i != m_components.size() - 1) {
105  m_components[i] = std::move(m_components[m_components.size() - 1]);
106  }
107  m_components.pop_back();
108 
109  super::remove(ent);
110  }
111 
112  auto get(TEntity ent) -> TComp& {
113  if (this->contains(ent))
114  return m_components[this->index(ent)];
115  throw std::runtime_error("Trying to get unexisting entity " + std::to_string(ent)
116  + " from pool of type " + typeid(TComp).name());
117  }
118 
119  private:
120  std::vector<TComp> m_components; // vec[index] = comp
121  };
122 }