// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "MassEntityTypes.h" #include "MassRequirements.h" #include "Subsystems/EngineSubsystem.h" #include "Subsystems/GameInstanceSubsystem.h" #include "Subsystems/LocalPlayerSubsystem.h" #include "Subsystems/WorldSubsystem.h" #include "Engine/LocalPlayer.h" #include "Engine/GameInstance.h" #if WITH_EDITOR #include "Editor.h" #include "EditorSubsystem.h" #else #include "Engine/Engine.h" #endif // WITH_EDITOR struct FMassSubsystemAccess { MASSENTITY_API explicit FMassSubsystemAccess(UWorld* InWorld = nullptr); //----------------------------------------------------------------------------- // Statically-typed subsystems //----------------------------------------------------------------------------- template::IsDerived>::Type> T* GetMutableSubsystem() { const uint32 SystemIndex = FMassExternalSubsystemBitSet::GetTypeIndex(); if (ensureMsgf(MutableSubsystemsBitSet.IsBitSet(SystemIndex) , TEXT("Missing 'template<> struct TMassExternalSubsystemTraits' for this subsystem"), *GetNameSafe(T::StaticClass()))) { return GetSubsystemInternal(SystemIndex); } return nullptr; } template::IsDerived>::Type> T& GetMutableSubsystemChecked() { T* InstancePtr = GetMutableSubsystem(); check(InstancePtr); return *InstancePtr; } template::IsDerived>::Type> const T* GetSubsystem() { const uint32 SystemIndex = FMassExternalSubsystemBitSet::GetTypeIndex(); if (ensureMsgf(ConstSubsystemsBitSet.IsBitSet(SystemIndex) || MutableSubsystemsBitSet.IsBitSet(SystemIndex) , TEXT("Missing 'template<> struct TMassExternalSubsystemTraits' for this subsystem"), *GetNameSafe(T::StaticClass()))) { return GetSubsystemInternal(SystemIndex); } return nullptr; } template::IsDerived>::Type> const T& GetSubsystemChecked() { const T* InstancePtr = GetSubsystem(); check(InstancePtr); return *InstancePtr; } //----------------------------------------------------------------------------- // UClass-provided subsystems //----------------------------------------------------------------------------- template::IsDerived>::Type> T* GetMutableSubsystem(const TSubclassOf SubsystemClass) { const uint32 SystemIndex = FMassExternalSubsystemBitSet::GetTypeIndex(**SubsystemClass); if (ensureMsgf(MutableSubsystemsBitSet.IsBitSet(SystemIndex) , TEXT("Missing 'template<> struct TMassExternalSubsystemTraits' for this subsystem"), *GetNameSafe(SubsystemClass))) { return GetSubsystemInternal(SystemIndex, SubsystemClass); } return nullptr; } template::IsDerived>::Type> T& GetMutableSubsystemChecked(const TSubclassOf SubsystemClass) { T* InstancePtr = GetMutableSubsystem(SubsystemClass); check(InstancePtr); return *InstancePtr; } template::IsDerived>::Type> const T* GetSubsystem(const TSubclassOf SubsystemClass) { const uint32 SystemIndex = FMassExternalSubsystemBitSet::GetTypeIndex(**SubsystemClass); if (ensureMsgf(ConstSubsystemsBitSet.IsBitSet(SystemIndex) || MutableSubsystemsBitSet.IsBitSet(SystemIndex) , TEXT("Missing 'template<> struct TMassExternalSubsystemTraits' for this subsystem"), *GetNameSafe(SubsystemClass))) { return GetSubsystemInternal(SystemIndex, SubsystemClass); } return nullptr; } template::IsDerived>::Type> const T& GetSubsystemChecked(const TSubclassOf SubsystemClass) { const T* InstancePtr = GetSubsystem(SubsystemClass); check(InstancePtr); return *InstancePtr; } //----------------------------------------------------------------------------- // remaining API //----------------------------------------------------------------------------- MASSENTITY_API bool CacheSubsystemRequirements(const FMassSubsystemRequirements& SubsystemRequirements); MASSENTITY_API void SetSubsystemRequirements(const FMassSubsystemRequirements& SubsystemRequirements); void GetSubsystemRequirementBits(FMassExternalSubsystemBitSet& OutConstSubsystemsBitSet, FMassExternalSubsystemBitSet& OutMutableSubsystemsBitSet) { OutConstSubsystemsBitSet = ConstSubsystemsBitSet; OutMutableSubsystemsBitSet = MutableSubsystemsBitSet; } void SetSubsystemRequirementBits(const FMassExternalSubsystemBitSet& InConstSubsystemsBitSet, const FMassExternalSubsystemBitSet& InMutableSubsystemsBitSet) { ConstSubsystemsBitSet = InConstSubsystemsBitSet; MutableSubsystemsBitSet = InMutableSubsystemsBitSet; } template static constexpr bool DoesRequireWorld() { constexpr bool bIsWorldSubsystem = TIsDerivedFrom::IsDerived; constexpr bool bIsGameInstanceSubsystem = TIsDerivedFrom::IsDerived; constexpr bool bIsLocalPlayerSubsystem = TIsDerivedFrom::IsDerived; return (bIsWorldSubsystem || bIsGameInstanceSubsystem || bIsLocalPlayerSubsystem); } template::IsDerived>::Type> static T* FetchSubsystemInstance(UWorld* World) { check(World); if constexpr (TIsDerivedFrom::IsDerived) { return UWorld::GetSubsystem(World); } else if constexpr (TIsDerivedFrom::IsDerived) { return UGameInstance::GetSubsystem(World->GetGameInstance()); } else if constexpr (TIsDerivedFrom::IsDerived) { // note that this default implementation will work only for the first player in a local-coop game // to customize this behavior specialize the FetchSubsystemInstance template function for the type you need. return ULocalPlayer::GetSubsystem(World->GetFirstLocalPlayerFromController()); } else { checkf(false, TEXT("FMassSubsystemAccess::FetchSubsystemInstance: Unhandled world-related USubsystem class %s"), *T::StaticClass()->GetName()); } } template::IsDerived>::Type> static T* FetchSubsystemInstance() { if constexpr (TIsDerivedFrom::IsDerived) { return GEngine->GetEngineSubsystem(); } #if WITH_EDITOR else if constexpr (TIsDerivedFrom::IsDerived) { return GEditor->GetEditorSubsystem(); } #endif // WITH_EDITOR else { checkf(false, TEXT("FMassSubsystemAccess::FetchSubsystemInstance: Unhandled world-less USubsystem class %s"), *T::StaticClass()->GetName()); } } static MASSENTITY_API USubsystem* FetchSubsystemInstance(UWorld* World, TSubclassOf SubsystemClass); protected: template T* GetSubsystemInternal(const uint32 SystemIndex) { if (UNLIKELY(Subsystems.IsValidIndex(SystemIndex) == false)) { Subsystems.AddZeroed(Subsystems.Num() - SystemIndex + 1); } T* SystemInstance = (T*)Subsystems[SystemIndex]; if (SystemInstance == nullptr) { if constexpr (DoesRequireWorld()) { SystemInstance = FetchSubsystemInstance>(World.Get()); } else { SystemInstance = FetchSubsystemInstance>(); } Subsystems[SystemIndex] = SystemInstance; } return SystemInstance; } template T* GetSubsystemInternal(const uint32 SystemIndex, const TSubclassOf SubsystemClass) { if (UNLIKELY(Subsystems.IsValidIndex(SystemIndex) == false)) { Subsystems.AddZeroed(Subsystems.Num() - SystemIndex + 1); } USubsystem* SystemInstance = (T*)Subsystems[SystemIndex]; if (SystemInstance == nullptr) { SystemInstance = FetchSubsystemInstance(World.Get(), SubsystemClass); Subsystems[SystemIndex] = SystemInstance; } return Cast(SystemInstance); } MASSENTITY_API bool CacheSubsystem(const uint32 SystemIndex); FMassExternalSubsystemBitSet ConstSubsystemsBitSet; FMassExternalSubsystemBitSet MutableSubsystemsBitSet; TArray Subsystems; TWeakObjectPtr World; };