-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathSystem.hpp
More file actions
259 lines (205 loc) · 7.71 KB
/
System.hpp
File metadata and controls
259 lines (205 loc) · 7.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* Copyright (c) 2016-2026 Andrew J. MacDonald. All rights reserved. */
#pragma once
#include <Core/Defines.hpp>
#include <Core/containers/Array.hpp>
#include <Core/containers/HashSet.hpp>
#include <Core/utilities/EnumFlags.hpp>
#include <Core/functional/Delegate.hpp>
#include <Core/reflection/ObjectBase.hpp>
#include <Core/reflection/Handle.hpp>
#include <scene/ComponentContainer.hpp>
#include <scene/EntitySetHelpers.hpp>
namespace Hyperion {
class EntityManager;
class Scene;
class World;
class SystemComponentDescriptors : HashSet<ComponentInfo, &ComponentInfo::typeId>
{
public:
EntitySetId entitySetId; // can be 0 if inited dynamically (from Span<ComponentInfo>)
template <class... ComponentDescriptors>
SystemComponentDescriptors(ComponentDescriptors&&... componentDescriptors)
: HashSet({ std::forward<ComponentDescriptors>(componentDescriptors)... }),
entitySetId(GetEntitySetId<std::conditional_t<bool(ComponentDescriptors::Access& ComponentAccess::READ_WRITE), typename ComponentDescriptors::Type, VoidComponentType>...>())
{
Assert(Size() == sizeof...(ComponentDescriptors), "Duplicate component descriptors found");
}
explicit SystemComponentDescriptors(Span<ComponentInfo> componentInfos)
{
for (ComponentInfo& componentInfo : componentInfos)
{
Insert(componentInfo);
}
}
using HashSet::ToArray;
HYP_DEF_STL_BEGIN_END(HashSet::Begin(), HashSet::End())
};
/*! \brief A system is attached to a World and batch processes entities with specific components each tick.
* Systems are grouped into SystemExecutionGroups which define how and when they are executed (e.g., on the sim thread, in parallel, etc.). */
HYP_CLASS(Abstract)
class HYP_API SystemBase : public ObjectBase
{
HYP_OBJECT_BODY(SystemBase);
public:
friend class EntityManager;
friend class SystemExecutionGroup;
friend class World;
virtual ~SystemBase() override = default;
Name GetName() const;
virtual bool ShouldProcessScene(Scene* scene) const
{
return true;
}
/*! \brief Returns true if this System can be executed in parallel, false otherwise.
* If false, the System will be executed on the sim thread.
*
* \return True if this System can be executed in parallel, false otherwise.
*/
virtual bool AllowParallelExecution() const
{
return true;
}
/*! \brief Returns true if this System requires the sim thread to execute, false otherwise.
* \details Use this to ensure that the System can access sim thread-only resources or perform operations
* that must be done on the sim thread, such as modifying the Scene or World state.
* If true, the System will be executed on the sim thread.
*
* \return True if this System requires the sim thread to execute, false otherwise.
*/
virtual bool RequiresSimThread() const
{
return false;
}
virtual bool AllowUpdate() const
{
return true;
}
/*! \brief Returns the TypeIds of the components this System operates on.
* To be used by the EntityManager in order to properly order the Systems based on their dependencies.
*
* \return The TypeIds of the components this System operates on.
*/
HYP_FORCE_INLINE const Array<TypeId>& GetComponentTypeIds() const
{
return m_componentTypeIds;
}
/*! \brief Returns the ComponentInfo objects of the components this System operates on.
*
* \return The ComponentInfos of the components this System operates on.
*/
HYP_FORCE_INLINE const Array<ComponentInfo>& GetComponentInfos() const
{
return m_componentInfos;
}
/*! \brief Returns true if all given TypeIds are operated on by this System, false otherwise.
*
* \param componentTypeIds The TypeIds of the components to check.
* \param receiveEventsContext If true, this function will skip components that do not receive events for this System.
*
* \return True if all given TypeIds are operated on by this System, false otherwise.
*/
bool ActsOnComponents(const Array<TypeId>& componentTypeIds, bool receiveEventsContext) const
{
for (const TypeId componentTypeId : m_componentTypeIds)
{
const ComponentInfo& componentInfo = GetComponentInfo(componentTypeId);
// skip observe-only components
if (!(componentInfo.access & ComponentAccess::READ_WRITE))
{
continue;
}
if (receiveEventsContext && !componentInfo.receivesEvents)
{
continue;
}
if (!componentTypeIds.Contains(componentTypeId))
{
return false;
}
}
return true;
}
/*! \brief Returns true if this System operates on the component with the given TypeId, false otherwise.
*
* \param componentTypeId The TypeId of the component to check.
* \param includeReadOnly If true, this function will return true even if the component is read-only.
* Otherwise, read-only components will be ignored.
*
* \return True if this System operates on the component with the given TypeId, false otherwise.
*/
bool HasComponentTypeId(TypeId componentTypeId, bool includeReadOnly = true) const
{
const bool hasComponentTypeId = m_componentTypeIds.Contains(componentTypeId);
if (!hasComponentTypeId)
{
return false;
}
if (includeReadOnly)
{
return true;
}
const ComponentInfo& componentInfo = GetComponentInfo(componentTypeId);
return !!(componentInfo.access & ComponentAccess::WRITE);
}
/*! \brief Returns the ComponentInfo of the component with the given TypeId.
*
* \param componentTypeId The TypeId of the component to check.
*
* \return The ComponentInfo of the component with the given TypeId.
*/
const ComponentInfo& GetComponentInfo(TypeId componentTypeId) const
{
const auto it = m_componentTypeIds.Find(componentTypeId);
Assert(it != m_componentTypeIds.End(), "Component type Id not found");
const size_t index = m_componentTypeIds.IndexOf(it);
Assert(index != size_t(-1), "Component type Id not found");
return m_componentInfos[index];
}
virtual void OnEntityAdded(Entity* entity)
{
}
virtual void OnEntityRemoved(Entity* entity)
{
}
virtual void OnAddedToWorld(World* world)
{
}
virtual void OnRemovedFromWorld(World* world)
{
}
virtual void Shutdown()
{
}
virtual void Process(float delta, Span<Handle<Scene>> scenes) = 0;
HYP_FORCE_INLINE World* GetWorld() const
{
return m_world;
}
protected:
SystemBase()
{
}
/*! \brief Set a callback to be called once after the System has processed.
* The callback will be called on the sim thread after the System has finished processing, so it is safe to add/remove components within the callback.
*
* \param fn The callback to call after the System has processed. */
template <class Func>
void AfterProcess(Func&& fn)
{
m_afterProcessProcs.EmplaceBack(std::forward<Func>(fn));
}
virtual void Init() override
{
SetReady(true);
}
virtual SystemComponentDescriptors GetComponentDescriptors() const = 0;
World* m_world = nullptr;
DelegateHandlerSet m_delegateHandlers;
private:
void InitComponentInfos_Internal();
HashSet<WeakHandle<Entity>> m_initializedEntities;
Array<TypeId> m_componentTypeIds;
Array<ComponentInfo> m_componentInfos;
Array<Proc<void()>> m_afterProcessProcs;
};
} // namespace Hyperion