// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Queue.h" #include "EnvironmentQuery/EnvQueryNode.h" #include "MassEQSTypes.h" #include "MassSubsystemBase.h" #include "MassExternalSubsystemTraits.h" #include "MassEQSSubsystem.generated.h" /** * Subsystem for sending UMassEQSGenerator and UMassEQSTest requests to Mass Processors, * and for returning the results back to EQS. */ UCLASS() class UMassEQSSubsystem : public UMassSubsystemBase { GENERATED_BODY() /** Queue holding UniquePtrs to the data required for each Request */ using FRequestQueue = TQueue>; public: /** Push new Request into RequestQueue */ FMassEQSRequestHandle PushRequest(const FEnvQueryInstance& QueryInstance, const int32 RequestQueueIndex, TUniquePtr&& Request); /** Dequeue next Request off the RequestQueue, removing it from the queue, and transferring ownership of UniquePtr */ TUniquePtr PopRequest(const int32 RequestQueueIndex); /** Send results from a finished/processed request to be stored in this subsystem */ void SubmitResults(FMassEQSRequestHandle RequestHandle, TUniquePtr&& Result); /** Try to Acquire the results from a request from the QueryInstance, if it is available. */ TUniquePtr TryAcquireResults(FMassEQSRequestHandle RequestHandle); /** Returns the index into RequestQueues for the input class */ int32 GetRequestQueueIndex(TSubclassOf RequestClass); /** * If Request in RequestQueue -> Remove from Queue * If Result in Results -> Remove from Results * Otherwise, Request is currently being processed -> Log this ID as Cancelled, and ignore result when submitted. */ void CancelRequest(FMassEQSRequestHandle RequestHandle); /** Finalization step for Handle Management */ void FinalizeRequest(FMassEQSRequestHandle RequestHandle); virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; protected: /** * Mapping from Class of requester, to index into RequestQueues. * Each Class that extends UMassEQSGenerator or UMassEQSTest * can be a requester, this will store a Queue for each of these types. * @see PostInitialize() */ TMap, int32> RequestQueueLookup = {}; /** * Array of Queues holding each Request. * TQueue does not support copy/assignment, which TMap requires. * This is the reason the TQueues are stored in a separate Array. */ TArray RequestQueues = {}; /** * The detector only guards the extension of RequestQueues array. Modifying specific elements is not guarded since * those are a thread safe queues. */ UE_MT_DECLARE_RW_ACCESS_DETECTOR(RequestAccessDetector); /** Holds results for finished Query */ TMap> Results; UE_MT_DECLARE_RW_ACCESS_DETECTOR(ResultAccessDetector); FIndexedHandleManagerBase HandleManager; /** * Holds all RequestHandles of requests who have been canceled, and not yet submitted. * Once this request is submitted, we will ignore the results. */ TArray CancelledRequests; /** Used to ignore request cancellation after subsystem is De-Initialized */ bool bIsInitialized = false; }; template<> struct TMassExternalSubsystemTraits final { enum { GameThreadOnly = false, ThreadSafeWrite = true, }; };