Helios Engine 0.1.0
A modular ECS based data-oriented C++23 game engine
 
Loading...
Searching...
No Matches
system_config.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <helios/core_pch.hpp>
4
10
11#include <array>
12#include <cstddef>
13#include <functional>
14#include <span>
15#include <utility>
16#include <vector>
17
18namespace helios::app {
19
20// Forward declaration
21class SubApp;
22
23/**
24 * @brief Fluent builder for configuring systems with ordering and sets.
25 * @details Provides a chainable API for specifying system dependencies, set membership and
26 * relationships with other system sets. The configuration is applied when the builder is
27 * destroyed or when explicitly finalized.
28 * @tparam Schedule Schedule type where systems are added
29 * @tparam Systems System types being configured
30 *
31 * @example
32 * @code
33 * app.AddSystems<MovementSystem, CollisionSystem>(kUpdate)
34 * .After<InputSystem>()
35 * .Before<RenderSystem>()
36 * .InSet<PhysicsSet>()
37 * .AfterSet<InputSet>()
38 * .BeforeSet<RenderSet>()
39 * .Sequence();
40 * @endcode
41 */
42template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
43 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
45public:
46 /**
47 * @brief Constructs a system configuration builder.
48 * @param sub_app Reference to the sub-app where systems will be added
49 * @param schedule Schedule instance where systems are added
50 */
51 explicit SystemConfig(SubApp& sub_app, Schedule schedule = {}) noexcept : sub_app_(sub_app), schedule_(schedule) {}
52 SystemConfig(const SystemConfig&) = delete;
53 SystemConfig(SystemConfig&&) noexcept = default;
54
55 /**
56 * @brief Destructor - automatically applies configuration if not already applied.
57 */
58 ~SystemConfig();
59
60 SystemConfig& operator=(const SystemConfig&) = delete;
61 SystemConfig& operator=(SystemConfig&&) noexcept = default;
62
63 /**
64 * @brief Adds ordering constraint: these systems run after specified systems.
65 * @details Creates a dependency edge from each specified system to each system in this config.
66 * @tparam AfterSystems System types that must run before these systems
67 * @return Reference to this config for method chaining
68 */
69 template <ecs::SystemTrait... AfterSystems>
70 requires(sizeof...(AfterSystems) > 0 && utils::UniqueTypes<AfterSystems...>)
71 SystemConfig& After();
72
73 /**
74 * @brief Adds ordering constraint: these systems run before specified systems.
75 * @details Creates a dependency edge from each system in this config to each specified system.
76 * @tparam BeforeSystems System types that must run after these systems
77 * @return Reference to this config for method chaining
78 */
79 template <ecs::SystemTrait... BeforeSystems>
80 requires(sizeof...(BeforeSystems) > 0 && utils::UniqueTypes<BeforeSystems...>)
81 SystemConfig& Before();
82
83 /**
84 * @brief Adds these systems to specified system sets.
85 * @details Systems inherit ordering constraints and run conditions from their sets.
86 * Systems can belong to multiple sets.
87 * @tparam Sets System set types
88 * @return Reference to this config for method chaining
89 */
90 template <SystemSetTrait... Sets>
91 requires(sizeof...(Sets) > 0 && utils::UniqueTypes<Sets...>)
92 SystemConfig& InSet();
93
94 /**
95 * @brief Adds ordering constraint: these systems run after specified system sets.
96 * @details Creates a dependency edge from each specified set to all systems in this config.
97 * All systems that belong to the specified sets will run before these systems.
98 * @tparam AfterSets System set types that must run before these systems
99 * @return Reference to this config for method chaining
100 */
101 template <SystemSetTrait... AfterSets>
102 requires(sizeof...(AfterSets) > 0 && utils::UniqueTypes<AfterSets...>)
103 SystemConfig& AfterSet();
104
105 /**
106 * @brief Adds ordering constraint: these systems run before specified system sets.
107 * @details Creates a dependency edge from all systems in this config to each specified set.
108 * All systems in this config will run before systems that belong to the specified sets.
109 * @tparam BeforeSets System set types that must run after these systems
110 * @return Reference to this config for method chaining
111 */
112 template <SystemSetTrait... BeforeSets>
113 requires(sizeof...(BeforeSets) > 0 && utils::UniqueTypes<BeforeSets...>)
114 SystemConfig& BeforeSet();
115
116 /**
117 * @brief Creates an ordered sequence of systems.
118 * @details Each system runs after the previous one in the template parameter order.
119 * Equivalent to manually adding After constraints: System2.After<System1>(), System3.After<System2>(), etc.
120 * @warning Only valid when configuring multiple systems (sizeof...(Systems) > 1).
121 * @return Reference to this config for method chaining
122 */
123 SystemConfig& Sequence() noexcept
124 requires(sizeof...(Systems) > 1);
125
126 /**
127 * @brief Explicitly applies the configuration and adds systems to the sub-app.
128 * @details Called automatically by destructor if not already applied.
129 * Can be called explicitly to control when configuration is finalized.
130 */
131 void Apply();
132
133private:
134 std::reference_wrapper<SubApp> sub_app_; ///< Sub-app where systems are added
135 Schedule schedule_; ///< Schedule where systems are added
136 bool sequence_ = false; ///< Whether to create sequential ordering
137 bool applied_ = false; ///< Whether configuration has been applied
138
139 std::vector<ecs::SystemTypeId> before_systems_; ///< Systems that run after these systems
140 std::vector<ecs::SystemTypeId> after_systems_; ///< Systems that run before these systems
141 std::vector<SystemSetId> system_sets_; ///< System sets these systems belong to
142 std::vector<SystemSetId> before_sets_; ///< System sets that must run after these systems
143 std::vector<SystemSetId> after_sets_; ///< System sets that must run before these systems
144};
145
146template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
147 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
148inline SystemConfig<Schedule, Systems...>::~SystemConfig() {
149 if (!applied_) {
150 Apply();
151 }
152}
153
154template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
155 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
156template <ecs::SystemTrait... AfterSystems>
157 requires(sizeof...(AfterSystems) > 0 && utils::UniqueTypes<AfterSystems...>)
158inline auto SystemConfig<Schedule, Systems...>::After() -> SystemConfig& {
159 (after_systems_.push_back(ecs::SystemTypeIdOf<AfterSystems>()), ...);
160 return *this;
161}
162
163template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
164 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
165template <ecs::SystemTrait... BeforeSystems>
166 requires(sizeof...(BeforeSystems) > 0 && utils::UniqueTypes<BeforeSystems...>)
167inline auto SystemConfig<Schedule, Systems...>::Before() -> SystemConfig& {
168 (before_systems_.push_back(ecs::SystemTypeIdOf<BeforeSystems>()), ...);
169 return *this;
170}
171
172template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
173 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
174template <SystemSetTrait... Sets>
175 requires(sizeof...(Sets) > 0 && utils::UniqueTypes<Sets...>)
176inline auto SystemConfig<Schedule, Systems...>::InSet() -> SystemConfig& {
177 (system_sets_.push_back(SystemSetIdOf<Sets>()), ...);
178 return *this;
179}
180
181template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
182 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
183template <SystemSetTrait... AfterSets>
184 requires(sizeof...(AfterSets) > 0 && utils::UniqueTypes<AfterSets...>)
185inline auto SystemConfig<Schedule, Systems...>::AfterSet() -> SystemConfig& {
186 (after_sets_.push_back(SystemSetIdOf<AfterSets>()), ...);
187 return *this;
188}
189
190template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
191 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
192template <SystemSetTrait... BeforeSets>
193 requires(sizeof...(BeforeSets) > 0 && utils::UniqueTypes<BeforeSets...>)
194inline auto SystemConfig<Schedule, Systems...>::BeforeSet() -> SystemConfig& {
195 (before_sets_.push_back(SystemSetIdOf<BeforeSets>()), ...);
196 return *this;
197}
198
199template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
200 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
201inline auto SystemConfig<Schedule, Systems...>::Sequence() noexcept -> SystemConfig&
202 requires(sizeof...(Systems) > 1)
203{
204 sequence_ = true;
205 return *this;
206}
207
208template <ScheduleTrait Schedule, ecs::SystemTrait... Systems>
209 requires(sizeof...(Systems) > 0 && utils::UniqueTypes<Systems...>)
210inline void SystemConfig<Schedule, Systems...>::Apply() {
211 if (applied_) [[unlikely]] {
212 return;
213 }
214
215 applied_ = true;
216
217 auto& sub_app = sub_app_.get();
218 auto& scheduler = sub_app.GetScheduler();
219
220 // Add systems to the schedule first so that scheduler storage is populated.
221 (sub_app.template AddSystem<Systems>(schedule_), ...);
222
223 // Helper to register explicit system-to-system ordering with scheduler.
224 const auto register_ordering_for = [&scheduler, this]<typename T>(std::span<const ecs::SystemTypeId> before_ids,
225 std::span<const ecs::SystemTypeId> after_ids) {
226 if (!before_ids.empty() || !after_ids.empty()) {
227 details::SystemOrdering ordering{};
228 ordering.before.assign(before_ids.begin(), before_ids.end());
229 ordering.after.assign(after_ids.begin(), after_ids.end());
230 scheduler.template RegisterOrdering<T>(schedule_, std::move(ordering));
231 }
232 };
233
234 // Helper to append metadata to SystemInfo via Scheduler helper APIs and register set membership.
235 const auto append_metadata_for = [&scheduler, this]<typename T>() {
236 if (!before_systems_.empty() || !after_systems_.empty()) {
237 scheduler.template AppendSystemOrderingMetadata<T>(schedule_, std::span<const ecs::SystemTypeId>(before_systems_),
238 std::span<const ecs::SystemTypeId>(after_systems_));
239 }
240 if (!system_sets_.empty()) {
241 // Record membership in each configured set
242 constexpr ecs::SystemTypeId system_id = ecs::SystemTypeIdOf<T>();
243 for (const auto set_id : system_sets_) {
244 scheduler.AddSystemToSet(set_id, system_id);
245 }
246
247 scheduler.template AppendSystemSetMetadata<T>(schedule_, std::span<const SystemSetId>(system_sets_));
248 }
249 };
250
251 if (sequence_ && sizeof...(Systems) > 1) {
252 // Sequence: each system runs after the previous one in template parameter order.
253 constexpr std::array system_ids = {ecs::SystemTypeIdOf<Systems>()...};
254
255 size_t idx = 0;
256
257 (
258 [&]<typename T>() {
259 std::vector<ecs::SystemTypeId> before_ids(before_systems_.begin(), before_systems_.end());
260 std::vector<ecs::SystemTypeId> after_ids(after_systems_.begin(), after_systems_.end());
261
262 if (idx > 0) {
263 // Current system must run after the previous system in the sequence.
264 after_ids.push_back(system_ids[idx - 1]);
265 }
266
267 register_ordering_for.template operator()<T>(before_ids, after_ids);
268 append_metadata_for.template operator()<T>();
269
270 ++idx;
271 }.template operator()<Systems>(),
272 ...);
273 } else {
274 // No sequence: each system just gets the accumulated before/after system constraints.
275 (
276 [&]<typename T>() {
277 register_ordering_for.template operator()<T>(std::span<const ecs::SystemTypeId>(before_systems_),
278 std::span<const ecs::SystemTypeId>(after_systems_));
279 append_metadata_for.template operator()<T>();
280 }.template operator()<Systems>(),
281 ...);
282 }
283
284 // NOTE:
285 // - before_sets_ / after_sets_ are collected by the builder API,
286 // but the current scheduler implementation does not yet track SystemSetInfo
287 // or set-level ordering constraints. Once such infrastructure exists,
288 // these vectors can be propagated similarly to the system-level constraints.
289}
290
291} // namespace helios::app
A sub-application with its own ECS world, systems, and resources.
Definition sub_app.hpp:167
SystemConfig(SystemConfig &&) noexcept=default
SystemConfig(const SystemConfig &)=delete
SystemConfig(SubApp &sub_app, Schedule schedule={}) noexcept
Constructs a system configuration builder.
Trait to identify valid system set types.
Concept for valid system types.
Definition system.hpp:76
size_t SystemSetId
Type alias for system set type IDs.
size_t SystemTypeId
Type ID for systems.
Definition system.hpp:93
BasicQuery< World, Allocator, Components... > Query
Type alias for query with mutable world access.
Definition query.hpp:2481
STL namespace.
Ordering constraints for a system.
Definition scheduler.hpp:51
std::vector< ecs::SystemTypeId > before
Definition scheduler.hpp:52