Apparatus Version 1.0.0
ECS-like data-driven workflow for Unreal Engine.
Mechanism.h
1 /*
2  * ░▒▓ APPARATUS ▓▒░
3  *
4  * File: ApparatusFunctionLibrary.h
5  * Created: Friday, 23rd October 2020 7:00:48 pm
6  * Author: Vladislav Dmitrievich Turbanov (vladislav@turbanov.ru)
7  * ───────────────────────────────────────────────────────────────────
8  * Last Modified: Tuesday, 6th April 2021 1:36:08 pm
9  * Modified By: Vladislav Dmitrievich Turbanov (vladislav@turbanov.ru)
10  * ───────────────────────────────────────────────────────────────────
11  *
12  * The Apparatus source code is for your internal usage only.
13  * Redistribution of this file is strictly prohibited.
14  *
15  * Community forums: https://talk.turbanov.ru
16  *
17  * Copyright 2020 - 2021, SP Vladislav Dmitrievich Turbanov
18  * Made in Russia, Moscow City, Chekhov City
19  */
20 
21 #pragma once
22 
23 #include <map>
24 #include <ostream>
25 #include <typeinfo>
26 
27 #include "CoreUObject.h"
28 #include "Engine.h"
29 #include "UObject/Class.h"
30 
31 #include "Fingerprint.h"
32 #include "Filter.h"
33 #include "Mechanical.h"
34 #include "SubjectiveActorComponent.h"
35 
36 #include "Mechanism.generated.h"
37 
38 DECLARE_STATS_GROUP(TEXT("Mechanism"), STATGROUP_Mechanism, STATCAT_Advanced);
39 
40 DECLARE_CYCLE_STAT_EXTERN(TEXT("Boot"), STAT_MechanismBoot,
41  STATGROUP_Mechanism, );
42 DECLARE_CYCLE_STAT_EXTERN(TEXT("Buffer"), STAT_MechanismBuffer,
43  STATGROUP_Mechanism, );
44 DECLARE_CYCLE_STAT_EXTERN(TEXT("Find Matching Belts"), STAT_MechanismFindMatchingBelts,
45  STATGROUP_Mechanism, );
46 DECLARE_CYCLE_STAT_EXTERN(TEXT("Evaluate"), STAT_MechanismEvaluate,
47  STATGROUP_Mechanism, );
48 DECLARE_CYCLE_STAT_EXTERN(TEXT("Evaluate ~ Input"), STAT_MechanismEvaluateInput,
49  STATGROUP_Mechanism, );
50 DECLARE_CYCLE_STAT_EXTERN(TEXT("Evaluate ~ Steady"),
51  STAT_MechanismEvaluateSteady, STATGROUP_Mechanism, );
52 DECLARE_CYCLE_STAT_EXTERN(TEXT("Evaluate ~ Presentation"),
53  STAT_MechanismEvaluatePresentation,
54  STATGROUP_Mechanism, );
55 DECLARE_CYCLE_STAT_EXTERN(TEXT("Fetch"), STAT_MechanismFetchDetails,
56  STATGROUP_Mechanism, );
57 
61 UCLASS()
62 class APPARATUSRUNTIME_API UMechanism: public UObject
63 {
64  GENERATED_BODY()
65 
66 
69  static TArray<TWeakObjectPtr<UBelt>> Belts;
70 
74  static TArray<TWeakInterfacePtr<IMechanical>> Mechanicals;
75 
79  static TMap<FFilter, TArray<TWeakObjectPtr<UBelt>>> BeltsByFilters;
80 
84  static TSet<TWeakInterfacePtr<ISubjective>> All;
85 
89  static TArray<TWeakInterfacePtr<ISubjective>> AllNotBooted;
90 
91  static void DoRegister(TScriptInterface<ISubjective> Subjective);
92 
93  static void DoUnregister(TScriptInterface<ISubjective> Subjective);
94 
95  static void DoHandleChange(TScriptInterface<ISubjective> Subjective);
96 
97  static void DoHandleChange(TScriptInterface<ISubjective> Subjective,
98  const TArray<UDetail*>& Details);
99 
100  static class UBelt* DoBufferBelt(class UBelt* BufferingBelt,
101  class UBelt* Belt,
102  const FFilter& Filter);
103 
104  static class UBelt* DoBufferBelts(class UBelt* BufferingBelt,
105  const TArray<class UBelt*>& TargetBelts,
106  const FFilter& Filter);
107 
108  static FORCEINLINE void DoClearMatchingBeltsCache()
109  {
110  BeltsByFilters.Reset();
111  }
112 
116  static FORCEINLINE void ClearMatchingBeltsCache()
117  {
118  DoClearMatchingBeltsCache();
119  }
120 
121  static class UBelt*
122  DoObtainMostSpecificBelt(UObject* BeltOwner,
123  TScriptInterface<ISubjective> Subjective);
124 
125  static void DoFindMatchingBelts(const struct FFilter& InFilter,
126  TArray<class UBelt*>& OutBelts);
127 
128  static FORCEINLINE void
129  DoGetBeltsMatchingFingerprint(const struct FFingerprint& Fingerprint,
130  TArray<class UBelt*>& OutBelts)
131  {
132  return DoFindMatchingBelts(FFilter(Fingerprint), OutBelts);
133  }
134 
135  static void DoFetchDetailsInAllBelts();
136 
140  static void FetchDetailsInAllBelts();
141 
142  static TWeakObjectPtr<class UMechanism> Instance;
143 
144  friend class UBelt;
145 
146 public:
147 
151  FORCEINLINE static const TSet<TWeakInterfacePtr<ISubjective>>& GetAll()
152  {
153  return All;
154  }
155 
159  FORCEINLINE static const TArray<TWeakInterfacePtr<ISubjective>>& GetAllNotBooted()
160  {
161  return AllNotBooted;
162  }
163 
167  UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Apparatus")
168  static void
169  GetBeltsMatchingFingerprint(const struct FFingerprint& Fingerprint,
170  TArray<class UBelt*>& OutBelts);
171 
175  UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Apparatus")
176  static void
177  FindMatchingBelts(const struct FFilter& Filter,
178  TArray<class UBelt*>& OutBelts);
179 
183  UFUNCTION(BlueprintCallable, Category = "Apparatus")
184  static void BootAll();
185 
189  UFUNCTION(BlueprintCallable, Category = "Apparatus|Mechanics",
190  Meta = (HidePin = "Mechanical", DefaultToSelf = "Mechanical"))
191  static class UBelt*
192  BufferBelt(UObject* Mechanical,
193  class UBelt* Belt,
194  const FFilter& Filter);
195 
199  UFUNCTION(BlueprintCallable, Category = "Apparatus|Mechanics",
200  Meta = (HidePin = "Mechanical", DefaultToSelf = "Mechanical"))
201  static class UBelt*
202  BufferBelts(UObject* Mechanical,
203  const TArray<class UBelt*>& TargetBelts,
204  const FFilter& Filter);
205 
209  static void HandleChange(TScriptInterface<ISubjective> Subjective);
210 
214  static void HandleChange(TScriptInterface<ISubjective> Subjective,
215  const TArray<UDetail*>& Details);
216 
222  static void RegisterSubjective(TScriptInterface<ISubjective> Subjective);
223 
227  static void UnregisterSubjective(TScriptInterface<ISubjective> Subjective);
228 
232  static class UBelt*
233  ObtainMostSpecificBelt(UObject* BeltOwner,
234  TScriptInterface<ISubjective> Subjective);
235 
236 
242  static class UMechanism* GetInstance()
243  {
244  if (Instance.IsValid())
245  {
246  return Instance.Get();
247  }
248  Instance = NewObject<UMechanism>(GetTransientPackage());
249  check(Instance.IsValid());
250  Instance->AddToRoot();
251  return Instance.Get();
252  }
253 
259  static void RegisterMechanical(TScriptInterface<IMechanical> Mechanical);
260 
266  static FORCEINLINE void UnregisterMechanical(TScriptInterface<IMechanical> Mechanical)
267  {
268  check(Mechanical);
269  Mechanicals.Remove(Mechanical.GetObject());
270  }
271 }; // class UMechanism
272 
273 FORCEINLINE class UBelt* UMechanism::DoBufferBelt(
274  UBelt* BufferingBelt,
275  class UBelt* Belt,
276  const FFilter& Filter)
277 {
278  if (Filter.GetBootFilter() == EBootFilter::Booted)
279  {
280  // Trigger the booting when buffering for booted subjects:
281  BootAll();
282  }
283 
284  SCOPE_CYCLE_COUNTER(STAT_MechanismBuffer);
285  BufferingBelt->BufferDetailsFromBelt(Belt, Filter);
286 
287  return BufferingBelt;
288 }
289 
290 FORCEINLINE class UBelt* UMechanism::DoBufferBelts(
291  UBelt* BufferingBelt,
292  const TArray<class UBelt*>& TargetBelts,
293  const FFilter& Filter)
294 {
295  if (Filter.GetBootFilter() == EBootFilter::Booted)
296  {
297  // Trigger the booting when buffering for booted subjects:
298  BootAll();
299  }
300  SCOPE_CYCLE_COUNTER(STAT_MechanismBuffer);
301  BufferingBelt->BufferDetailsFromBelts(TargetBelts, Filter);
302 
303  return BufferingBelt;
304 }
305 
306 inline void UMechanism::DoFetchDetailsInAllBelts()
307 {
308  SCOPE_CYCLE_COUNTER(STAT_MechanismFetchDetails);
309  for (auto Belt : Belts)
310  {
311  if (Belt == nullptr)
312  continue;
313  Belt->FetchDetails();
314  }
315 }
316 
317 FORCEINLINE class UBelt*
318 UMechanism::BufferBelt(UObject* Mechanical,
319  class UBelt* Belt,
320  const FFilter& Filter)
321 {
322  IMechanical* M = CastChecked<IMechanical>(Mechanical);
323  return DoBufferBelt(M->GetBufferingBelt(), Belt, Filter);
324 }
325 
326 FORCEINLINE class UBelt*
327 UMechanism::BufferBelts(UObject* Mechanical,
328  const TArray<class UBelt*>& TargetBelts,
329  const FFilter& Filter)
330 {
331  IMechanical* M = CastChecked<IMechanical>(Mechanical);
332  return DoBufferBelts(M->GetBufferingBelt(), TargetBelts, Filter);
333 }
334 
335 FORCEINLINE void UMechanism::DoHandleChange(TScriptInterface<ISubjective> Subjective)
336 {
337  check(Subjective);
338  DoObtainMostSpecificBelt(GetTransientPackage(), Subjective)->Refresh(Subjective);
339 }
340 
341 FORCEINLINE void UMechanism::DoHandleChange(TScriptInterface<ISubjective> Subjective,
342  const TArray<UDetail*>& Details)
343 {
344  check(Subjective);
345  DoObtainMostSpecificBelt(GetTransientPackage(), Subjective)->Refresh(Subjective, Details);
346 }
347 
348 FORCEINLINE void UMechanism::HandleChange(TScriptInterface<ISubjective> Subjective)
349 {
350  DoHandleChange(Subjective);
351 }
352 
353 FORCEINLINE void
355  TArray<class UBelt*>& OutBelts)
356 {
357  DoFindMatchingBelts(Filter, OutBelts);
358 }
359 
360 FORCEINLINE void
362  TArray<class UBelt*>& OutBelts)
363 {
364  DoGetBeltsMatchingFingerprint(Fingerprint, OutBelts);
365 }
366 
367 FORCEINLINE void
368 UMechanism::HandleChange(TScriptInterface<ISubjective> Subjective,
369  const TArray<UDetail*>& Details)
370 {
371  DoHandleChange(Subjective, Details);
372 }
373 
374 FORCEINLINE void UMechanism::RegisterSubjective(TScriptInterface<ISubjective> Subjective)
375 {
376  DoRegister(Subjective);
377 }
378 
379 FORCEINLINE void UMechanism::UnregisterSubjective(TScriptInterface<ISubjective> Subjective)
380 {
381  DoUnregister(Subjective);
382 }
383 
384 inline void UMechanism::RegisterMechanical(TScriptInterface<IMechanical> Mechanical)
385 {
386  check(Mechanical);
387  Mechanicals.Add(Mechanical.GetObject());
388  // Make sure that all of the currently collected subjectives are in some belt.
389  for (int32 i = 0; i < AllNotBooted.Num(); ++i)
390  {
391  if (!AllNotBooted[i].IsValid())
392  {
393  AllNotBooted.RemoveAt(i--);
394  continue;
395  }
396  if (AllNotBooted[i]->GetSlot() != nullptr)
397  {
398  continue;
399  }
400  HandleChange(AllNotBooted[i].ToScriptInterface());
401  }
402 }
403 
404 inline UDetail* ISubjective::EnableDetail(TSubclassOf<UDetail> DetailType)
405 {
406  if (GetFingerprint().ContainsDetail(DetailType))
407  {
408  // The detail is already available and is active.
409  auto r = FindDetail(DetailType);
410  check(r);
411  return r;
412  }
413 
414  // Try to find among existing details first,
415  // including the inactive ones.
416  const bool bIncludeDisabled = true;
417  UDetail* Detail = FindDetail(DetailType, bIncludeDisabled);
418  if (Detail)
419  {
420  if (!Detail->IsEnabled())
421  {
422  // Found an inactive detail.
423  // Activate it and return.
424  Detail->SetEnabled();
425  return Detail;
426  }
427  return Detail;
428  }
429 
430  // Create a new detail of a needed type.
431  Detail = NewObject<UDetail>(this->_getUObject(), DetailType);
432  check(Detail);
433 
434  GetDetailsRef().Add(Detail);
435  GetFingerprint().Add(DetailType);
436 
437  checkSlow(FindDetailByType(DetailType));
438 
439  UMechanism::HandleChange(this->_getUObject());
440 
441  Detail->Activated();
442 
443  return Detail;
444 }
445 
446 inline UDetail* ISubjective::AddDetail(TSubclassOf<UDetail> DetailType,
447  const bool bReuseInactive)
448 {
449  check(DetailType);
450 
451  if (bReuseInactive)
452  {
453  // Try to find an inactive detail.
454 
455  // In the UDetail case we can skip the IsA checks for a slight
456  // performance benefit.
457  if (UNLIKELY(DetailType == UDetail::StaticClass()))
458  {
459  for (auto Detail : GetDetailsRef())
460  {
461  if (!Detail)
462  continue;
463  if (!Detail->IsEnabled())
464  {
465  // Set the detail to be active and return it to the user.
466  Detail->SetEnabled(true);
467  return Detail;
468  }
469  return Detail;
470  }
471  }
472  else
473  {
474  for (auto Detail : GetDetailsRef())
475  {
476  if (!Detail)
477  continue;
478  if (Detail->IsEnabled())
479  continue;
480  if (Detail->IsA(DetailType))
481  {
482  // Set the detail to be active and return it to the user.
483  Detail->SetEnabled(true);
484  return Detail;
485  }
486  }
487  }
488  }
489 
490  // No existing inactive detail was found,
491  // so create a new detail of a needed type.
492  UDetail* Detail = NewObject<UDetail>(this->_getUObject(), DetailType);
493  check(Detail);
494 
495  GetDetailsRef().Add(Detail);
496  GetFingerprint().Add(DetailType);
497 
498  checkSlow(FindDetailByType(DetailType));
499 
500  UMechanism::HandleChange(this->_getUObject());
501 
502  Detail->Activated();
503 
504  return Detail;
505 }
A common interface for all mechanisms.
Definition: Mechanical.h:43
virtual UBelt * GetBufferingBelt() const
A special callback to process a newly created subjects boot process.
Definition: Mechanical.h:83
An interface for all sorts of subjects.
Definition: Subjective.h:42
virtual TArray< UDetail * > & GetDetailsRef()
Direct access for the internal details array.
Definition: Subjective.h:75
class UDetail * EnableDetail(TSubclassOf< UDetail > DetailType)
Enable a detail of a certain type.
Definition: Mechanism.h:404
virtual FFingerprint & GetFingerprint()
Get the internal fingerprint of the subjective.
Definition: Subjective.h:93
UDetail * FindDetail(TSubclassOf< UDetail > DetailType, const bool bIncludeDisabled=false) const
Find the detail by a type.
Definition: Subjective.h:286
class UDetail * AddDetail(TSubclassOf< UDetail > DetailType, const bool bReuseInactive=false)
Add a new active detail or reuse an inactive one.
Definition: Mechanism.h:446
The conveyor belt consisting of subjects.
Definition: Belt.h:60
void BufferDetailsFromBelts(const TArray< class UBelt * > &Belts, const FFilter &TargetFilter)
Fetch the details from other belts, using the specified filter.
Definition: Belt.cpp:314
void FetchDetails()
Fetch all of the current active details in this belt.
Definition: Belt.cpp:267
void BufferDetailsFromBelt(UBelt *Belt, const FFilter &TargetFilter)
Fetch the details from another belt, using the specified filter.
Definition: Belt.cpp:288
void Refresh(TScriptInterface< ISubjective > Subjective)
Refresh a subjective within the belt.
Definition: Belt.cpp:164
The base subject detail (component) class.
Definition: Detail.h:35
bool IsEnabled() const
Check if the detail currently active.
Definition: Detail.h:56
virtual void Activated()
The event is fired, when the detail has become active for the subject.
Definition: Detail.h:105
void SetEnabled(const bool bState=true)
Set a detail to be active, or not.
Definition: Detail.cpp:28
The main Mechanism function library.
Definition: Mechanism.h:63
static const TSet< TWeakInterfacePtr< ISubjective > > & GetAll()
All of the subjectives currently available and registered.
Definition: Mechanism.h:151
static void RegisterMechanical(TScriptInterface< IMechanical > Mechanical)
Register a mechanical within the mechanism.
Definition: Mechanism.h:384
static class UBelt * BufferBelts(UObject *Mechanical, const TArray< class UBelt * > &TargetBelts, const FFilter &Filter)
Buffer multiple belts with a filter.
Definition: Mechanism.h:327
static const TArray< TWeakInterfacePtr< ISubjective > > & GetAllNotBooted()
All of the subjectives available that are not yet booted.
Definition: Mechanism.h:159
static void GetBeltsMatchingFingerprint(const struct FFingerprint &Fingerprint, TArray< class UBelt * > &OutBelts)
Get all of the belts matching a fingerprint.
Definition: Mechanism.h:361
static void BootAll()
Boot all of the subjectives currently available.
Definition: Mechanism.cpp:44
static void UnregisterMechanical(TScriptInterface< IMechanical > Mechanical)
Unregister a mechanical from the mechanism.
Definition: Mechanism.h:266
static void UnregisterSubjective(TScriptInterface< ISubjective > Subjective)
Remove a subjective from the mechanism completely.
Definition: Mechanism.h:379
static class UBelt * BufferBelt(UObject *Mechanical, class UBelt *Belt, const FFilter &Filter)
Buffer a single belt with a filter.
Definition: Mechanism.h:318
static void FindMatchingBelts(const struct FFilter &Filter, TArray< class UBelt * > &OutBelts)
Get all of the belts matching a filter.
Definition: Mechanism.h:354
static void HandleChange(TScriptInterface< ISubjective > Subjective)
Handle the subjective change.
Definition: Mechanism.h:348
static void RegisterSubjective(TScriptInterface< ISubjective > Subjective)
Register a subjective within the mechanism.
Definition: Mechanism.h:374
The details filter specification.
Definition: Filter.h:44
EBootFilter GetBootFilter() const
Get the active boot filter.
Definition: Filter.h:83
The details fingerprint.
Definition: Fingerprint.h:117
bool Add(std::initializer_list< TSubclassOf< class UDetail >> DetailTypes)
Add detail types to a fingerprint.