MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Subsystems.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13
14#include "CoreMinimal.h"
15#include "Subsystems/Subsystem.h"
16#include "Subsystems/EngineSubsystem.h"
17#include "Subsystems/GameInstanceSubsystem.h"
18#include "Subsystems/LocalPlayerSubsystem.h"
19#include "Subsystems/WorldSubsystem.h"
20#include "Engine/Engine.h"
21#include "Engine/LocalPlayer.h"
22#include "Engine/GameInstance.h"
23#include "Mcro/Concepts.h"
24#include "Mcro/AssertMacros.h"
25#include "Kismet/GameplayStatics.h"
26
28{
29 using namespace Mcro::Concepts;
30 using namespace Mcro::TypeName;
31
32 template<typename T>
33 concept CSubsystem = CDerivedFrom<T, USubsystem>;
34
35 template<typename T>
36 concept CEngineSubsystem = CDerivedFrom<T, UEngineSubsystem>;
37
38 template<typename T>
39 concept CGameInstanceSubsystem = CDerivedFrom<T, UGameInstanceSubsystem>;
40
41 template<typename T>
42 concept CLocalPlayerSubsystem = CDerivedFrom<T, ULocalPlayerSubsystem>;
43
44 template<typename T>
45 concept CWorldSubsystem = CDerivedFrom<T, UWorldSubsystem>;
46
52
53 /** @brief Extra namespace encapsulates common vocabulary */
54 namespace Subsystems
55 {
56 /**
57 * @brief
58 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
59 * inherits from. Required arguments may vary depending on the type of subsystem.
60 *
61 * @tparam T UEngineSubsystem derivative
62 * @return Subsystem if exists or nullptr
63 */
64 template<CEngineSubsystem T>
65 T* Get()
66 {
67 return GEngine ? GEngine->GetEngineSubsystem<T>() : nullptr;
68 }
69
70 /**
71 * @brief
72 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
73 * inherits from. Required arguments may vary depending on the type of subsystem.
74 *
75 * @tparam T UGameInstanceSubsystem derivative
76 * @return Subsystem if exists or nullptr
77 */
78 template<CGameInstanceSubsystem T>
79 T* Get(
80 const UObject* worldContextObject = nullptr,
82 EGetWorldErrorMode errorMode = EGetWorldErrorMode::LogAndReturnNull
83 )
84 {
85 if (!GEngine)
86 {
87 return nullptr;
88 }
89 if (worldContextObject == nullptr)
90 {
91 if (IsRunningDedicatedServer() || GEngine->GameViewport == nullptr)
92 {
94 }
95 switch (fallback)
96 {
97 default:
99 {
100 const FWorldContext* worldContext = GEngine->GameViewport
101 ? GEngine->GetWorldContextFromGameViewport(GEngine->GameViewport)
102 : nullptr;
103 const UGameInstance* owningGameInstance = worldContext ? worldContext->OwningGameInstance : nullptr;
104 return owningGameInstance ? owningGameInstance->GetSubsystem<T>() : nullptr;
105 }
107 {
108 for (auto it = GEngine->GetWorldContexts().CreateConstIterator(); it; ++it)
109 {
110 if (T* result = it->OwningGameInstance ? it->OwningGameInstance->GetSubsystem<T>() : nullptr)
111 {
112 return result;
113 }
114 }
115 return nullptr;
116 }
117 }
118 }
119 if (const UWorld* world = GEngine->GetWorldFromContextObject(worldContextObject, errorMode))
120 {
121 return UGameInstance::GetSubsystem<T>(world->GetGameInstance());
122 }
123 return nullptr;
124 }
125
126 /**
127 * @brief
128 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
129 * inherits from. Required arguments may vary depending on the type of subsystem.
130 *
131 * @tparam T ULocalPlayerSubsystem derivative
132 * @return Subsystem if exists or nullptr
133 */
134 template<CLocalPlayerSubsystem T>
135 T* Get(const UObject* worldContext)
136 {
137 const UWorld* world = GEngine->GetWorldFromContextObject(worldContext, EGetWorldErrorMode::LogAndReturnNull);
138 APlayerController* controller = world && !world->IsNetMode(NM_DedicatedServer)
139 ? UGameplayStatics::GetPlayerController(worldContext, 0)
140 : nullptr;
141 return controller ? ULocalPlayer::GetSubsystem<T>(controller->GetLocalPlayer()) : nullptr;
142 }
143
144 /**
145 * @brief
146 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
147 * inherits from. Required arguments may vary depending on the type of subsystem.
148 *
149 * @tparam T UWorldSubsystem derivative
150 * @return Subsystem if exists or nullptr
151 */
152 template<CWorldSubsystem T>
153 T* Get(const UObject* worldContext, EGetWorldErrorMode errorMode = EGetWorldErrorMode::LogAndReturnNull)
154 {
155 if (const UWorld* world = GEngine ? GEngine->GetWorldFromContextObject(worldContext, errorMode) : nullptr)
156 {
157 return world->GetSubsystem<T>();
158 }
159 return nullptr;
160 }
161
162 /**
163 * @brief
164 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
165 * inherits from. Required arguments may vary depending on the type of subsystem. This is a checked version so
166 * if any steps fail to produce the target subsystem the program may crash.
167 *
168 * @tparam T type of the subsystem
169 * @return Guaranteed valid reference to a subsystem
170 */
171 template<CSubsystem T, typename... Args>
172 T& GetChecked(Args... args)
173 {
174 T* result = Get<T>(args...);
175
176 ASSERT_CRASH(result,
177 ->WithMessageF(TEXT_"Couldn't find required subsystem {0}", TTypeName<T>)
178 )
179
180 return *result;
181 }
182
183 /**
184 * @brief
185 * Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsystem T
186 * inherits from. Required arguments may vary depending on the type of subsystem. This is an ensured version so
187 * if any steps fail to produce the target subsystem an ensure may be hit.
188 *
189 * @tparam T type of the subsystem
190 * @return Subsystem if exists or nullptr
191 */
192 template<CSubsystem T, typename... Args>
193 T* GetEnsured(Args... args)
194 {
195 T* result = Get<T>(args...);
196 ensure(result);
197 return result;
198 }
199
200 /**
201 * @brief
202 * Helper for checking if some other subsystem should be created. Useful when you want to make a subsystem
203 * which should only be created if some other subsystem should be also created.
204 *
205 * @tparam T type of the subsystem
206 *
207 * @param outer
208 * be sure to pass outer argument which type aligns with checked subsystem outer. This may fail for example
209 * if you pass game instance as outer to check for world subsystem existence as ShouldCreateSubsystem of that
210 * subsystem may expect the outer to be World.
211 *
212 * @return True if the target subsystem should be created
213 */
214 template<CSubsystem T>
215 bool ShouldCreate(UObject* outer)
216 {
217 const USubsystem* cdo = T::StaticClass()->template GetDefaultObject<USubsystem>();
218 return cdo->ShouldCreateSubsystem(outer);
219 }
220 }
221}
#define ASSERT_CRASH(condition,...)
Use this instead of check macro if the checked expression shouldn't be ignored in shipping builds....
This header exists because STL headers in Android doesn't define STL concepts (other than same_as whi...
#define TEXT_
A convenience alternative to Unreal's own TEXT macro but this one doesn't require parenthesis around ...
Definition TextMacros.h:53
T * Get()
Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsyste...
Definition Subsystems.h:65
T & GetChecked(Args... args)
Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsyste...
Definition Subsystems.h:172
bool ShouldCreate(UObject *outer)
Helper for checking if some other subsystem should be created. Useful when you want to make a subsyst...
Definition Subsystems.h:215
T * GetEnsured(Args... args)
Helper for getting a subsystem, the internal boilerplate will be chosen based on the type of subsyste...
Definition Subsystems.h:193
constexpr FStringView TTypeName
Get a friendly string of an input type without using typeid(T).name().
Definition TypeName.h:161