2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectSubsystem.h"
2021-11-26 15:48:13 -05:00
# include "SmartObjectDefinition.h"
2021-09-28 13:33:00 -04:00
# include "SmartObjectComponent.h"
# include "SmartObjectCollection.h"
# include "EngineUtils.h"
2022-01-19 14:16:16 -05:00
# include "MassCommandBuffer.h"
2022-02-21 01:10:34 -05:00
# include "SmartObjectHashGrid.h"
2021-09-28 13:33:00 -04:00
# include "VisualLogger/VisualLogger.h"
2022-02-17 03:40:43 -05:00
# include "ProfilingDebugging/CpuProfilerTrace.h"
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
# include "DebugRenderSceneProxy.h"
# include "SmartObjectSubsystemRenderingActor.h"
# endif
2022-01-26 17:33:02 -05:00
# if WITH_SMARTOBJECT_DEBUG
# include "MassExecutor.h"
# endif
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
# include "Engine/LevelBounds.h"
# include "WorldPartition/WorldPartition.h"
# endif
2021-09-28 13:33:00 -04:00
namespace UE : : SmartObject
{
2021-11-02 11:12:43 -04:00
USmartObjectComponent * FindSmartObjectComponent ( const AActor & SmartObjectActor )
{
return SmartObjectActor . FindComponentByClass < USmartObjectComponent > ( ) ;
}
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
namespace Debug
{
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
2021-11-02 11:12:43 -04:00
static FAutoConsoleCommandWithWorld RegisterAllSmartObjectsCmd (
TEXT ( " ai.debug.so.RegisterAllSmartObjects " ) ,
TEXT ( " Force register all objects registered in the subsystem to simulate & debug runtime flows (will ignore already registered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugRegisterAllSmartObjects ( ) ;
}
} )
) ;
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
static FAutoConsoleCommandWithWorld UnregisterAllSmartObjectsCmd (
TEXT ( " ai.debug.so.UnregisterAllSmartObjects " ) ,
TEXT ( " Force unregister all objects registered in the subsystem to simulate & debug runtime flows (will ignore already unregistered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugUnregisterAllSmartObjects ( ) ;
}
} )
) ;
2021-09-28 13:33:00 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
2021-11-02 11:12:43 -04:00
}
2021-09-28 13:33:00 -04:00
} // UE::SmartObject
//----------------------------------------------------------------------//
// USmartObjectSubsystem
//----------------------------------------------------------------------//
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : OnWorldComponentsUpdated ( UWorld & World )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
// Load class required to instantiate the space partition structure
UE_CVLOG_UELOG ( ! SpacePartitionClassName . IsValid ( ) , this , LogSmartObject , Error , TEXT ( " A valid space partition class name is required. " ) ) ;
if ( SpacePartitionClassName . IsValid ( ) )
{
SpacePartitionClass = LoadClass < USmartObjectSpacePartition > ( this , * SpacePartitionClassName . ToString ( ) ) ;
UE_CVLOG_UELOG ( * SpacePartitionClass = = nullptr , this , LogSmartObject , Error , TEXT ( " Unable to load class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
}
// Class not specified or invalid, use some default
if ( SpacePartitionClass . Get ( ) = = nullptr )
{
SpacePartitionClassName = FSoftClassPath ( USmartObjectHashGrid : : StaticClass ( ) ) ;
SpacePartitionClass = USmartObjectHashGrid : : StaticClass ( ) ;
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Using default class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
}
# if UE_ENABLE_DEBUG_DRAWING
// Spawn the rendering actor
FActorSpawnParameters SpawnInfo ;
SpawnInfo . SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod : : AlwaysSpawn ;
RenderingActor = World . SpawnActor < ASmartObjectSubsystemRenderingActor > ( SpawnInfo ) ;
# endif // UE_ENABLE_DEBUG_DRAWING
2021-11-02 11:12:43 -04:00
// Register collections that were unable to register since they got loaded before the subsystem got created/initialized.
2021-09-28 13:33:00 -04:00
RegisterCollectionInstances ( ) ;
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
SpawnMissingCollection ( ) ;
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
ComputeBounds ( World , * MainCollection ) ;
}
2022-02-21 01:10:34 -05:00
# endif // WITH_EDITOR
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
UE_CVLOG_UELOG ( ! IsValid ( MainCollection ) , this , LogSmartObject , Error , TEXT ( " Collection is expected to be set once world components are updated. " ) ) ;
2021-09-28 13:33:00 -04:00
}
USmartObjectSubsystem * USmartObjectSubsystem : : GetCurrent ( const UWorld * World )
{
return UWorld : : GetSubsystem < USmartObjectSubsystem > ( World ) ;
}
2022-01-31 18:52:49 -05:00
void USmartObjectSubsystem : : AddToSimulation ( const FSmartObjectHandle Handle , const USmartObjectDefinition & Definition , const FTransform & Transform , const FBox & Bounds )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
if ( ! ensureMsgf ( Handle . IsValid ( ) , TEXT ( " SmartObject needs a valid Handle to be added to the simulation " ) ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
2022-01-31 18:52:49 -05:00
if ( ! ensureMsgf ( RuntimeSmartObjects . Find ( Handle ) = = nullptr , TEXT ( " Handle '%s' already registered in runtime simulation " ) , * LexToString ( Handle ) ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
2022-02-03 14:03:16 -05:00
if ( ! ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to add a smartobject to the simulation " ) ) )
{
return ;
}
2022-01-31 18:52:49 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Adding SmartObject '%s' to runtime simulation. " ) , * LexToString ( Handle ) ) ;
2021-09-28 13:33:00 -04:00
2022-01-31 18:52:49 -05:00
FSmartObjectRuntime & Runtime = RuntimeSmartObjects . Emplace ( Handle , FSmartObjectRuntime ( Definition ) ) ;
Runtime . SetRegisteredID ( Handle ) ;
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
Runtime . Bounds = Bounds ;
# endif
2022-01-19 14:16:16 -05:00
// Create runtime data and entity for each slot
2022-02-03 14:03:16 -05:00
int32 SlotIndex = 0 ;
for ( const FSmartObjectSlotDefinition & SlotDefinition : Definition . GetSlots ( ) )
2022-01-19 14:16:16 -05:00
{
2022-02-03 14:03:16 -05:00
// Build our shared fragment
FMassArchetypeSharedFragmentValues SharedFragmentValues ;
const uint32 DefinitionHash = UE : : StructUtils : : GetStructCrc32 ( FConstStructView : : Make ( SlotDefinition ) ) ;
FConstSharedStruct & SharedFragment = EntitySubsystem - > GetOrCreateConstSharedFragment ( DefinitionHash , FSmartObjectSlotDefinitionFragment ( Definition , SlotDefinition ) ) ;
SharedFragmentValues . AddConstSharedFragment ( SharedFragment ) ;
2022-01-19 14:16:16 -05:00
2022-02-03 14:03:16 -05:00
FSmartObjectSlotTransform TransformFragment ;
TOptional < FTransform > OptionalTransform = Definition . GetSlotTransform ( Transform , FSmartObjectSlotIndex ( SlotIndex ) ) ;
TransformFragment . SetTransform ( OptionalTransform . Get ( Transform ) ) ;
2022-01-19 14:16:16 -05:00
2022-02-03 14:03:16 -05:00
const FMassEntityHandle EntityHandle = EntitySubsystem - > ReserveEntity ( ) ;
EntitySubsystem - > Defer ( ) . PushCommand (
FBuildEntityFromFragmentInstances ( EntityHandle ,
{
FStructView : : Make ( TransformFragment )
} ,
SharedFragmentValues ) ) ;
FSmartObjectSlotHandle SlotHandle ( EntityHandle ) ;
RuntimeSlotStates . Add ( SlotHandle , FSmartObjectSlotClaimState ( ) ) ;
Runtime . SlotHandles [ SlotIndex ] = SlotHandle ;
SlotIndex + + ;
}
// For objects added to simulation after initial collection, we need to flush the command buffer
if ( bInitialCollectionAddedToSimulation )
{
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-02-11 08:53:05 -05:00
EntitySubsystem - > FlushCommands ( ) ;
2022-01-19 14:16:16 -05:00
}
2021-09-28 13:33:00 -04:00
// Transfer spatial information to the runtime instance
2021-11-02 11:12:43 -04:00
Runtime . SetTransform ( Transform ) ;
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
// Insert to the spatial representation structure and store associated data
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
Runtime . SpatialEntryData = SpacePartition - > Add ( Handle , Bounds ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-26 15:48:13 -05:00
void USmartObjectSubsystem : : AddToSimulation ( const FSmartObjectCollectionEntry & Entry , const USmartObjectDefinition & Definition )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
AddToSimulation ( Entry . GetHandle ( ) , Definition , Entry . GetTransform ( ) , Entry . GetBounds ( ) ) ;
2021-11-02 11:12:43 -04:00
}
void USmartObjectSubsystem : : AddToSimulation ( const USmartObjectComponent & Component )
{
2021-11-26 15:48:13 -05:00
if ( ensureMsgf ( Component . GetDefinition ( ) ! = nullptr , TEXT ( " Component must have a valid definition asset to register to the simulation " ) ) )
{
2022-01-19 14:16:16 -05:00
AddToSimulation ( Component . GetRegisteredHandle ( ) , * Component . GetDefinition ( ) , Component . GetComponentTransform ( ) , Component . GetSmartObjectBounds ( ) ) ;
2021-11-26 15:48:13 -05:00
}
2021-11-02 11:12:43 -04:00
}
2022-01-31 18:52:49 -05:00
void USmartObjectSubsystem : : RemoveFromSimulation ( const FSmartObjectHandle Handle )
2021-11-02 11:12:43 -04:00
{
2022-01-31 18:52:49 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Removing SmartObject '%s' from runtime simulation. " ) , * LexToString ( Handle ) ) ;
2021-11-02 11:12:43 -04:00
2022-01-31 18:52:49 -05:00
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
2022-01-26 17:33:02 -05:00
if ( SmartObjectRuntime = = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Failed to remove SmartObject '%s' from runtime simulation. No entry found. " ) , * LexToString ( Handle ) ) ;
2022-01-26 17:33:02 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
2022-02-21 01:10:34 -05:00
if ( ! ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to remove a smartobject from the simulation " ) ) )
{
return ;
}
2022-01-26 17:33:02 -05:00
// Abort everything before removing since abort flow may require access to runtime data
AbortAll ( * SmartObjectRuntime ) ;
2022-02-21 01:10:34 -05:00
// Remove from space partition
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Remove ( Handle , SmartObjectRuntime - > SpatialEntryData ) ;
2022-01-19 14:16:16 -05:00
2022-02-21 01:10:34 -05:00
// Destroy entities associated to slots
2022-01-19 14:16:16 -05:00
TArray < FMassEntityHandle > EntitiesToDestroy ;
2022-02-21 01:10:34 -05:00
EntitiesToDestroy . Reserve ( SmartObjectRuntime - > SlotHandles . Num ( ) ) ;
2022-01-26 17:33:02 -05:00
for ( const FSmartObjectSlotHandle & SlotHandle : SmartObjectRuntime - > SlotHandles )
2022-01-19 14:16:16 -05:00
{
RuntimeSlotStates . Remove ( SlotHandle ) ;
EntitiesToDestroy . Add ( SlotHandle ) ;
}
EntitySubsystem - > Defer ( ) . BatchDestroyEntities ( EntitiesToDestroy ) ;
2022-01-26 17:33:02 -05:00
2022-02-21 01:10:34 -05:00
// Remove object runtime data
2022-01-31 18:52:49 -05:00
RuntimeSmartObjects . Remove ( Handle ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : RemoveFromSimulation ( const FSmartObjectCollectionEntry & Entry )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
RemoveFromSimulation ( Entry . GetHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
}
void USmartObjectSubsystem : : RemoveFromSimulation ( const USmartObjectComponent & SmartObjectComponent )
{
2022-01-19 14:16:16 -05:00
RemoveFromSimulation ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
}
bool USmartObjectSubsystem : : RegisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
{
// Main collection may not assigned until world components are updated (active level set and actors registered)
// In this case objects will be part of the loaded collection or collection will be rebuilt from registered components
if ( IsValid ( MainCollection ) )
{
const UWorld & World = GetWorldRef ( ) ;
bool bAddToCollection = true ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
// For "build on demand collections" we wait an explicit build request to clear and repopulate
if ( MainCollection - > IsBuildOnDemand ( ) )
{
bAddToCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection that is built on demand only. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
// For partition world we don't alter the collection unless we are explicitly building the collection
else if ( World . IsPartitionedWorld ( ) & & ! MainCollection - > IsBuildingForWorldPartition ( ) )
{
bAddToCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection that is owned by partitioned world. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
}
2021-09-28 13:33:00 -04:00
# endif // WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( bAddToCollection )
{
const bool bIsNewEntry = MainCollection - > AddSmartObject ( SmartObjectComponent ) ;
// At runtime we only add new collection entries to the simulation. All existing entries were added on WorldBeginPlay
if ( World . IsGameWorld ( ) & & bInitialCollectionAddedToSimulation & & bIsNewEntry )
{
2022-01-19 14:16:16 -05:00
RuntimeCreatedEntries . Add ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
AddToSimulation ( SmartObjectComponent ) ;
}
}
}
2022-01-12 16:15:32 -05:00
else
{
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection since Main Collection is not set. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
if ( RegisteredSOComponents . Find ( & SmartObjectComponent ) ! = INDEX_NONE )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( SmartObjectComponent . GetOwner ( ) , LogSmartObject , Error , TEXT ( " Trying to register SmartObject %s more than once. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
return false ;
}
RegisteredSOComponents . Add ( & SmartObjectComponent ) ;
# endif // WITH_EDITOR
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
2021-11-02 11:12:43 -04:00
DebugRegisteredComponents . Add ( & SmartObjectComponent ) ;
2021-09-28 13:33:00 -04:00
# endif
return true ;
}
2021-11-02 11:12:43 -04:00
bool USmartObjectSubsystem : : UnregisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( IsValid ( MainCollection ) )
{
const UWorld & World = GetWorldRef ( ) ;
bool bRemoveFromCollection = true ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
// For "build on demand collections" we wait an explicit build request to clear and repopulate
if ( MainCollection - > IsBuildOnDemand ( ) )
{
bRemoveFromCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not removed from collection that is built on demand only. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
// For partition world we never remove from the collection since it is built incrementally
else if ( World . IsPartitionedWorld ( ) )
{
bRemoveFromCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not removed from collection that is owned by partitioned world. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
}
# endif // WITH_EDITOR
// At runtime, only entries created outside the initial collection are removed from simulation and collection
if ( World . IsGameWorld ( ) & & bInitialCollectionAddedToSimulation )
{
2022-01-19 14:16:16 -05:00
bRemoveFromCollection = RuntimeCreatedEntries . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ! = INDEX_NONE ;
2021-11-02 11:12:43 -04:00
if ( bRemoveFromCollection )
{
RemoveFromSimulation ( SmartObjectComponent ) ;
}
}
if ( bRemoveFromCollection )
{
MainCollection - > RemoveSmartObject ( SmartObjectComponent ) ;
}
2021-09-28 13:33:00 -04:00
}
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
if ( RegisteredSOComponents . Remove ( & SmartObjectComponent ) = = 0 )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( SmartObjectComponent . GetOwner ( ) , LogSmartObject , Error , TEXT ( " Trying to unregister SmartObject %s but it wasn't registered. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
2021-09-28 13:33:00 -04:00
return false ;
}
# endif // WITH_EDITOR
# if WITH_SMARTOBJECT_DEBUG
2021-11-02 11:12:43 -04:00
DebugRegisteredComponents . Remove ( & SmartObjectComponent ) ;
2021-09-28 13:33:00 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
return true ;
}
bool USmartObjectSubsystem : : RegisterSmartObjectActor ( const AActor & SmartObjectActor )
{
USmartObjectComponent * SOComponent = UE : : SmartObject : : FindSmartObjectComponent ( SmartObjectActor ) ;
if ( SOComponent = = nullptr )
{
UE_VLOG_UELOG ( & SmartObjectActor , LogSmartObject , Error , TEXT ( " Failed to register SmartObject for %s. USmartObjectComponent is missing. " ) , * SmartObjectActor . GetName ( ) ) ;
return false ;
}
return RegisterSmartObject ( * SOComponent ) ;
}
bool USmartObjectSubsystem : : UnregisterSmartObjectActor ( const AActor & SmartObjectActor )
{
USmartObjectComponent * SOComponent = UE : : SmartObject : : FindSmartObjectComponent ( SmartObjectActor ) ;
if ( SOComponent = = nullptr )
{
UE_VLOG_UELOG ( & SmartObjectActor , LogSmartObject , Error , TEXT ( " Failed to unregister SmartObject for %s. USmartObjectComponent is missing. " ) , * SmartObjectActor . GetName ( ) ) ;
return false ;
}
return UnregisterSmartObject ( * SOComponent ) ;
}
2022-01-31 18:52:49 -05:00
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
if ( ! ensureMsgf ( Handle . IsValid ( ) , TEXT ( " SmartObject handle should be valid: %s " ) , * LexToString ( Handle ) ) )
2022-01-19 14:16:16 -05:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2021-09-28 13:33:00 -04:00
2022-01-31 18:52:49 -05:00
FSmartObjectRuntime * SORuntime = RuntimeSmartObjects . Find ( Handle ) ;
if ( ! ensureMsgf ( SORuntime ! = nullptr , TEXT ( " A SmartObjectRuntime must be created for handle %s " ) , * LexToString ( Handle ) ) )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotHandle FoundHandle = FindSlot ( * SORuntime , Filter ) ;
if ( ! FoundHandle . IsValid ( ) )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-01-31 18:52:49 -05:00
return Claim ( Handle , FoundHandle ) ;
2021-09-28 13:33:00 -04:00
}
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectRequestResult & RequestResult )
{
2022-01-19 14:16:16 -05:00
if ( ! ensureMsgf ( RequestResult . IsValid ( ) , TEXT ( " Must claim with a valid result: %s " ) , * LexToString ( RequestResult ) ) )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-01-19 14:16:16 -05:00
return Claim ( RequestResult . SmartObjectHandle , RequestResult . SlotHandle ) ;
}
2022-01-31 18:52:49 -05:00
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectHandle Handle , const FSmartObjectSlotHandle SlotHandle )
2022-01-19 14:16:16 -05:00
{
// Slot might be unregistered by the time a result is used so it is possible that it can no longer be found
if ( FSmartObjectSlotClaimState * SlotState = RuntimeSlotStates . Find ( SlotHandle ) )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
const FSmartObjectUserHandle User ( NextFreeUserID + + ) ;
const bool bClaimed = SlotState - > Claim ( User ) ;
2022-01-31 18:52:49 -05:00
const FSmartObjectClaimHandle ClaimHandle ( Handle , SlotHandle , User ) ;
2022-01-19 14:16:16 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Claim %s for handle %s " ) , bClaimed ? TEXT ( " SUCCEEDED " ) : TEXT ( " FAILED " ) , * LexToString ( ClaimHandle ) ) ;
UE_CVLOG_LOCATION ( bClaimed , this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Yellow , TEXT ( " Claim " ) ) ;
if ( bClaimed )
{
return ClaimHandle ;
}
2021-09-28 13:33:00 -04:00
}
2022-01-19 14:16:16 -05:00
return FSmartObjectClaimHandle : : InvalidHandle ;
2021-09-28 13:33:00 -04:00
}
2022-01-26 17:33:02 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition ( const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
if ( ! ClaimHandle . IsValid ( ) )
{
2022-02-09 12:15:14 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Must provide a valid claim handle " ) ) ;
2022-01-26 17:33:02 -05:00
return nullptr ;
}
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( ClaimHandle . SmartObjectHandle ) ;
if ( ! ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " A SmartObjectRuntime must be created for %s " ) , * LexToString ( ClaimHandle . SmartObjectHandle ) ) )
{
return nullptr ;
}
return GetBehaviorDefinition ( * SmartObjectRuntime , ClaimHandle , DefinitionClass ) ;
}
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
const FSmartObjectSlotIndex SlotIndex ( SmartObjectRuntime . SlotHandles . IndexOfByKey ( ClaimHandle . SlotHandle ) ) ;
return Definition . GetBehaviorDefinition ( SlotIndex , DefinitionClass ) ;
}
2021-11-26 15:48:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : Use ( const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
2021-09-28 13:33:00 -04:00
{
if ( ! ClaimHandle . IsValid ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Must use with a valid claim handle " ) ) ;
return nullptr ;
}
2022-01-19 14:16:16 -05:00
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( ClaimHandle . SmartObjectHandle ) ;
if ( ! ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " A SmartObjectRuntime must be created for %s " ) , * LexToString ( ClaimHandle . SmartObjectHandle ) ) )
2021-09-28 13:33:00 -04:00
{
return nullptr ;
}
2021-11-26 15:48:13 -05:00
return Use ( * SmartObjectRuntime , ClaimHandle , DefinitionClass ) ;
2021-09-28 13:33:00 -04:00
}
2022-01-19 14:16:16 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : Use ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
if ( ! ensureMsgf ( ClaimHandle . IsValid ( ) , TEXT ( " A valid claim handle is required to use a slot: %s " ) , * LexToString ( ClaimHandle ) ) )
2021-09-28 13:33:00 -04:00
{
return nullptr ;
}
2022-01-26 17:33:02 -05:00
const USmartObjectBehaviorDefinition * BehaviorDefinition = GetBehaviorDefinition ( SmartObjectRuntime , ClaimHandle , DefinitionClass ) ;
2022-01-19 14:16:16 -05:00
if ( BehaviorDefinition = = nullptr )
{
const UClass * ClassPtr = DefinitionClass . Get ( ) ;
2022-01-26 17:33:02 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Unable to find a behavior definition of type %s in %s " ) ,
ClassPtr ! = nullptr ? * ClassPtr - > GetName ( ) : TEXT ( " Null " ) , * LexToString ( SmartObjectRuntime . GetDefinition ( ) ) ) ;
2022-01-19 14:16:16 -05:00
return nullptr ;
}
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Start using handle %s " ) , * LexToString ( ClaimHandle ) ) ;
2021-09-28 13:33:00 -04:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , SmartObjectRuntime . GetTransform ( ) . GetLocation ( ) , 50.f , FColor : : Green , TEXT ( " Use " ) ) ;
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState & SlotState = RuntimeSlotStates . FindChecked ( ClaimHandle . SlotHandle ) ;
if ( ensureMsgf ( SlotState . GetState ( ) = = ESmartObjectSlotState : : Claimed , TEXT ( " Should have been claimed first: %s " ) , * LexToString ( ClaimHandle ) ) & &
ensureMsgf ( SlotState . User = = ClaimHandle . UserHandle , TEXT ( " Attempt to use slot %s from handle %s but already assigned to %s " ) ,
* LexToString ( SlotState ) , * LexToString ( ClaimHandle ) , * LexToString ( SlotState . User ) ) )
{
SlotState . State = ESmartObjectSlotState : : Occupied ;
return BehaviorDefinition ;
}
return nullptr ;
2021-09-28 13:33:00 -04:00
}
bool USmartObjectSubsystem : : Release ( const FSmartObjectClaimHandle & ClaimHandle )
{
if ( ! ClaimHandle . IsValid ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Must release slot using a valid handle " ) ) ;
return false ;
}
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState * SlotState = RuntimeSlotStates . Find ( ClaimHandle . SlotHandle ) ;
if ( SlotState = = nullptr )
2021-09-28 13:33:00 -04:00
{
return false ;
}
2022-01-19 14:16:16 -05:00
const bool bSuccess = SlotState - > Release ( ClaimHandle , /*bAborted*/ false ) ;
UE_CVLOG_UELOG ( bSuccess , this , LogSmartObject , Verbose , TEXT ( " Released using handle %s " ) , * LexToString ( ClaimHandle ) ) ;
UE_CVLOG_LOCATION ( bSuccess , this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Red , TEXT ( " Release " ) ) ;
2021-09-28 13:33:00 -04:00
return bSuccess ;
}
2022-01-19 14:16:16 -05:00
ESmartObjectSlotState USmartObjectSubsystem : : GetSlotState ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotClaimState * SlotState = RuntimeSlotStates . Find ( SlotHandle ) ;
return SlotState ! = nullptr ? SlotState - > GetState ( ) : ESmartObjectSlotState : : Invalid ;
}
TOptional < FVector > USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectSlotHandle SlotHandle ) const
{
TOptional < FTransform > Transform = GetSlotTransform ( SlotHandle ) ;
2021-09-28 13:33:00 -04:00
return ( Transform . IsSet ( ) ? Transform . GetValue ( ) . GetLocation ( ) : TOptional < FVector > ( ) ) ;
}
TOptional < FVector > USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
if ( ensureMsgf ( ClaimHandle . IsValid ( ) , TEXT ( " Requesting slot location from an invalid claim handle. " ) ) )
{
2022-01-19 14:16:16 -05:00
return GetSlotLocation ( ClaimHandle . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
return TOptional < FVector > ( ) ;
}
2021-11-26 15:48:13 -05:00
bool USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectClaimHandle & ClaimHandle , FVector & OutSlotLocation ) const
{
const TOptional < FVector > OptionalLocation = GetSlotLocation ( ClaimHandle ) ;
OutSlotLocation = OptionalLocation . Get ( FVector : : ZeroVector ) ;
return OptionalLocation . IsSet ( ) ;
}
2021-09-28 13:33:00 -04:00
TOptional < FVector > USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectRequestResult & Result ) const
{
if ( ensureMsgf ( Result . IsValid ( ) , TEXT ( " Requesting slot location from an invalid request result. " ) ) )
{
2022-01-19 14:16:16 -05:00
return GetSlotLocation ( Result . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
return TOptional < FVector > ( ) ;
}
2022-01-19 14:16:16 -05:00
TOptional < FTransform > USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
TOptional < FTransform > Transform ;
2022-01-19 14:16:16 -05:00
if ( ! ensureMsgf ( SlotHandle . IsValid ( ) , TEXT ( " Requesting slot transform for an invalid slot handle " ) ) )
2021-09-28 13:33:00 -04:00
{
return Transform ;
}
2022-02-03 14:03:16 -05:00
if ( ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to retrieve slot transform " ) ) )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotView View ( * EntitySubsystem , SlotHandle ) ;
const FSmartObjectSlotTransform & SlotTransform = View . GetStateData < FSmartObjectSlotTransform > ( ) ;
Transform = SlotTransform . GetTransform ( ) ;
2021-09-28 13:33:00 -04:00
}
return Transform ;
}
TOptional < FTransform > USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
if ( ensureMsgf ( ClaimHandle . IsValid ( ) , TEXT ( " Requesting slot transform from an invalid claim handle. " ) ) )
{
2022-01-19 14:16:16 -05:00
return GetSlotTransform ( ClaimHandle . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
return TOptional < FTransform > ( ) ;
}
2021-11-26 15:48:13 -05:00
bool USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle , FTransform & OutSlotTransform ) const
{
const TOptional < FTransform > OptionalLocation = GetSlotTransform ( ClaimHandle ) ;
OutSlotTransform = OptionalLocation . Get ( FTransform : : Identity ) ;
return OptionalLocation . IsSet ( ) ;
}
2021-09-28 13:33:00 -04:00
TOptional < FTransform > USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectRequestResult & Result ) const
{
if ( ensureMsgf ( Result . IsValid ( ) , TEXT ( " Requesting slot transform from an invalid request result. " ) ) )
{
2022-01-19 14:16:16 -05:00
return GetSlotTransform ( Result . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
return TOptional < FTransform > ( ) ;
}
SmartObject : improvements
- Change the way to communicate between SO Evaluator, Service and BTTask,
- Manage Activities Cooldowns,
- Manage SmartObject Slot cooldown by user,
- Add GetActivityTags in SmartObjectSubsystem.
[REVIEW] [at]loic.devaux, [at]raphael.lapierre, [at]yoan.stamant
[FYI] philippe.painchaud, patrick.carroll, guillaume.morreel, loic.devaux, felix.laplante, youssef.trad
#tests PIE
#rnx
#ROBOMERGE-AUTHOR: josselin.francois
#ROBOMERGE-SOURCE: CL 18979507 via CL 18979533 via CL 18979562 via CL 18979623 via CL 18992705 via CL 18993044
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v917-18934589)
[CL 18997335 by josselin francois in ue5-main branch]
2022-02-15 11:34:26 -05:00
const FGameplayTagContainer & USmartObjectSubsystem : : GetActivityTags ( const FSmartObjectRequestResult & Result ) const
{
if ( ensureMsgf ( Result . IsValid ( ) , TEXT ( " Requesting ActivityTags from an invalid request result. " ) ) )
{
return GetActivityTags ( Result . SmartObjectHandle ) ;
}
return FGameplayTagContainer : : EmptyContainer ;
}
const FGameplayTagContainer & USmartObjectSubsystem : : GetActivityTags ( const FSmartObjectHandle & Handle ) const
{
if ( ! ensureMsgf ( Handle . IsValid ( ) , TEXT ( " Must provide a valid smart object handle. " ) ) )
{
return FGameplayTagContainer : : EmptyContainer ;
}
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
if ( ! ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " A SmartObjectRuntime must be created for %s " ) , * LexToString ( Handle ) ) )
{
return FGameplayTagContainer : : EmptyContainer ;
}
return SmartObjectRuntime - > GetDefinition ( ) . GetActivityTags ( ) ;
}
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState * USmartObjectSubsystem : : GetMutableSlotState ( const FSmartObjectClaimHandle & ClaimHandle )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
return RuntimeSlotStates . Find ( ClaimHandle . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : RegisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle , const FOnSlotInvalidated & Callback )
{
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState * Slot = GetMutableSlotState ( ClaimHandle ) ;
2021-09-28 13:33:00 -04:00
if ( Slot ! = nullptr )
{
Slot - > OnSlotInvalidatedDelegate = Callback ;
}
}
void USmartObjectSubsystem : : UnregisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle )
{
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState * Slot = GetMutableSlotState ( ClaimHandle ) ;
2021-09-28 13:33:00 -04:00
if ( Slot ! = nullptr )
{
Slot - > OnSlotInvalidatedDelegate . Unbind ( ) ;
}
}
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
void USmartObjectSubsystem : : DebugDraw ( FDebugRenderSceneProxy * DebugProxy ) const
{
if ( ! bInitialCollectionAddedToSimulation )
{
return ;
}
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Draw ( DebugProxy ) ;
for ( auto It ( RuntimeSmartObjects . CreateConstIterator ( ) ) ; It ; + + It )
{
const FSmartObjectRuntime & Runtime = It . Value ( ) ;
DebugProxy - > Boxes . Emplace ( Runtime . Bounds , GColorList . Blue ) ;
}
}
# endif // UE_ENABLE_DEBUG_DRAWING
2022-01-19 14:16:16 -05:00
void USmartObjectSubsystem : : AddSlotDataDeferred ( const FSmartObjectClaimHandle & ClaimHandle , const FConstStructView InData ) const
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
if ( ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to add slot data " ) ) & &
ensureMsgf ( ClaimHandle . IsValid ( ) , TEXT ( " Provided ClaimHandle is not valid. Data can't be added to slot. " ) ) & &
ensureMsgf ( InData . GetScriptStruct ( ) - > IsChildOf ( FSmartObjectSlotStateData : : StaticStruct ( ) ) ,
TEXT ( " Given struct doesn't represent a valid runtime data type. Make sure to inherit from FSmartObjectSlotState or one of its child-types. " ) ) )
{
EntitySubsystem - > Defer ( ) . PushCommand ( FCommandAddFragmentInstance ( ClaimHandle . SlotHandle , InData ) ) ;
}
}
FSmartObjectSlotView USmartObjectSubsystem : : GetSlotView ( const FSmartObjectRequestResult & FindResult ) const
{
return ( ensureMsgf ( FindResult . IsValid ( ) , TEXT ( " Provided RequestResult is not valid. SlotView can't be created. " ) ) )
? GetSlotView ( FindResult . SlotHandle )
: FSmartObjectSlotView ( ) ;
}
FSmartObjectSlotView USmartObjectSubsystem : : GetSlotView ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
return ( ensureMsgf ( ClaimHandle . IsValid ( ) , TEXT ( " Provided ClaimHandle is not valid. SlotView can't be created. " ) ) )
? GetSlotView ( ClaimHandle . SlotHandle )
: FSmartObjectSlotView ( ) ;
}
FSmartObjectSlotView USmartObjectSubsystem : : GetSlotView ( const FSmartObjectSlotHandle & SlotHandle ) const
{
if ( ensureMsgf ( SlotHandle . IsValid ( ) , TEXT ( " Provided SlotHandle is not valid. SlotView can't be created. " ) ) )
{
2022-02-03 14:03:16 -05:00
if ( ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to create slot view " ) ) )
2022-01-19 14:16:16 -05:00
{
return FSmartObjectSlotView ( * EntitySubsystem , SlotHandle ) ;
}
}
return FSmartObjectSlotView ( ) ;
}
FSmartObjectSlotHandle USmartObjectSubsystem : : FindSlot ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter ) const
{
2022-01-31 18:52:49 -05:00
TArray < FSmartObjectSlotHandle > Handles ;
FindSlots ( SmartObjectRuntime , Filter , Handles , ESmartObjectSlotSearchMode : : FirstMatch ) ;
2021-09-28 13:33:00 -04:00
2022-01-31 18:52:49 -05:00
return Handles . IsEmpty ( ) ? FSmartObjectSlotHandle ( ) : Handles . Top ( ) ;
}
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutResults , const ESmartObjectSlotSearchMode SearchMode ) const
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FilterSlots " ) ;
2021-11-26 15:48:13 -05:00
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
const int32 NumSlotDefinitions = Definition . GetSlots ( ) . Num ( ) ;
if ( ! ensureMsgf ( NumSlotDefinitions > 0 , TEXT ( " Definition should contain slot definitions at this point " ) ) )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
// Validate if any available slots
2022-01-19 14:16:16 -05:00
bool bAnyFreeSlot = false ;
2021-09-28 13:33:00 -04:00
2022-01-19 14:16:16 -05:00
TBitArray < > FreeSlots ;
FreeSlots . Init ( false , SmartObjectRuntime . SlotHandles . Num ( ) ) ;
int32 SlotIndex = 0 ;
for ( const FSmartObjectSlotHandle & SlotHandle : SmartObjectRuntime . SlotHandles )
{
if ( RuntimeSlotStates . FindChecked ( SlotHandle ) . State = = ESmartObjectSlotState : : Free )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
bAnyFreeSlot = true ;
FreeSlots [ SlotIndex ] = true ;
2021-09-28 13:33:00 -04:00
}
2022-01-19 14:16:16 -05:00
SlotIndex + + ;
}
if ( bAnyFreeSlot = = false )
{
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
const FGameplayTagContainer & ObjectTags = SmartObjectRuntime . GetTags ( ) ;
auto MatchesTagQueryFunc = [ ] ( const FGameplayTagQuery & Requirements , const FGameplayTagContainer & Tags ) - > bool
{
return Requirements . IsEmpty ( ) | | Requirements . Matches ( Tags ) ;
} ;
2021-11-26 15:48:13 -05:00
if ( MatchesTagQueryFunc ( Filter . ActivityRequirements , Definition . GetActivityTags ( ) )
& & MatchesTagQueryFunc ( Definition . GetObjectTagFilter ( ) , ObjectTags )
& & MatchesTagQueryFunc ( Definition . GetUserTagFilter ( ) , Filter . UserTags ) )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
const TConstArrayView < FSmartObjectSlotDefinition > Slots = Definition . GetSlots ( ) ;
2021-09-28 13:33:00 -04:00
for ( int i = 0 ; i < Slots . Num ( ) ; + + i )
{
2022-01-19 14:16:16 -05:00
const FSmartObjectSlotDefinition & Slot = Slots [ i ] ;
2021-09-28 13:33:00 -04:00
if ( FreeSlots [ i ] = = false )
{
continue ;
}
2022-02-17 03:40:43 -05:00
if ( Filter . BehaviorDefinitionClass ! = nullptr & &
Definition . GetBehaviorDefinition ( FSmartObjectSlotIndex ( i ) , Filter . BehaviorDefinitionClass ) = = nullptr )
2021-09-28 13:33:00 -04:00
{
continue ;
}
if ( MatchesTagQueryFunc ( Slot . UserTagFilter , Filter . UserTags ) = = false )
{
continue ;
}
2022-01-31 18:52:49 -05:00
OutResults . Add ( SmartObjectRuntime . SlotHandles [ i ] ) ;
if ( SearchMode = = ESmartObjectSlotSearchMode : : FirstMatch )
{
break ;
2021-09-28 13:33:00 -04:00
}
}
2022-01-31 18:52:49 -05:00
}
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : AbortAll ( FSmartObjectRuntime & SmartObjectRuntime )
{
2022-01-19 14:16:16 -05:00
for ( const FSmartObjectSlotHandle & SlotHandle : SmartObjectRuntime . SlotHandles )
2021-09-28 13:33:00 -04:00
{
2022-01-19 14:16:16 -05:00
FSmartObjectSlotClaimState & SlotState = RuntimeSlotStates . FindChecked ( SlotHandle ) ;
switch ( SlotState . State )
2021-09-28 13:33:00 -04:00
{
case ESmartObjectSlotState : : Claimed :
case ESmartObjectSlotState : : Occupied :
{
2022-01-19 14:16:16 -05:00
FSmartObjectClaimHandle ClaimHandle ( SmartObjectRuntime . GetRegisteredID ( ) , SlotHandle , SlotState . User ) ;
SlotState . Release ( ClaimHandle , /* bAborted */ true ) ;
2021-09-28 13:33:00 -04:00
break ;
}
case ESmartObjectSlotState : : Free : // falling through on purpose
default :
2022-01-19 14:16:16 -05:00
UE_CVLOG_UELOG ( SlotState . User . IsValid ( ) , this , LogSmartObject , Warning ,
TEXT ( " Smart object %s used by %s while the slot it's assigned to is not marked Claimed nor Occupied " ) ,
* LexToString ( SmartObjectRuntime . GetDefinition ( ) ) , * LexToString ( SlotState . User ) ) ;
2021-09-28 13:33:00 -04:00
break ;
}
}
}
2022-02-17 03:40:43 -05:00
FSmartObjectRequestResult USmartObjectSubsystem : : FindSmartObject ( const FSmartObjectRequest & Request ) const
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectRequestResult > Results ;
FindSmartObjects ( Request , Results ) ;
2022-02-17 03:40:43 -05:00
2022-02-21 01:10:34 -05:00
return Results . Num ( ) ? Results . Top ( ) : FSmartObjectRequestResult ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
bool USmartObjectSubsystem : : FindSmartObjects ( const FSmartObjectRequest & Request , TArray < FSmartObjectRequestResult > & OutResults ) const
2021-09-28 13:33:00 -04:00
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FindAllResults " ) ;
2022-02-21 01:10:34 -05:00
if ( ! bInitialCollectionAddedToSimulation )
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Can't find smart objet before runtime gets initialized (i.e. InitializeRuntime gets called). " ) ) ;
return false ;
}
2021-09-28 13:33:00 -04:00
const FSmartObjectRequestFilter & Filter = Request . Filter ;
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectHandle > QueryResults ;
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Find ( Request . QueryBox , QueryResults ) ;
for ( const FSmartObjectHandle SmartObjectHandle : QueryResults )
{
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectHandle ) ;
if ( SmartObjectRuntime = = nullptr | | ! Request . QueryBox . IsInside ( SmartObjectRuntime - > GetTransform ( ) . GetLocation ( ) ) )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
continue ;
}
2022-01-31 18:52:49 -05:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
2022-02-21 01:10:34 -05:00
FindSlots ( SmartObjectHandle , Filter , SlotHandles ) ;
OutResults . Reserve ( OutResults . Num ( ) + SlotHandles . Num ( ) ) ;
2022-01-31 18:52:49 -05:00
for ( FSmartObjectSlotHandle SlotHandle : SlotHandles )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
OutResults . Emplace ( SmartObjectHandle , SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-21 01:10:34 -05:00
}
2021-11-26 15:48:13 -05:00
return ( OutResults . Num ( ) > 0 ) ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
FSmartObjectRequestResult USmartObjectSubsystem : : FindSlot ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter ) const
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
FindSlots ( Handle , Filter , SlotHandles , ESmartObjectSlotSearchMode : : FirstMatch ) ;
return SlotHandles . IsEmpty ( ) ? FSmartObjectRequestResult ( ) : FSmartObjectRequestResult ( Handle , SlotHandles . Top ( ) ) ;
}
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectHandle Handle ,
const FSmartObjectRequestFilter & Filter ,
TArray < FSmartObjectSlotHandle > & OutSlots ,
const ESmartObjectSlotSearchMode SearchMode ) const
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FindSlots " ) ;
2022-01-31 18:52:49 -05:00
if ( ! Handle . IsValid ( ) )
2021-09-28 13:33:00 -04:00
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Requesting a valid use for an invalid smart object. " ) ) ;
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
if ( Filter . Predicate & & ! Filter . Predicate ( Handle ) )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
2022-01-26 17:33:02 -05:00
// Runtime data may no longer be available (removed from simulation)
if ( SmartObjectRuntime = = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-01-31 18:52:49 -05:00
return ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
FindSlots ( * SmartObjectRuntime , Filter , OutSlots , SearchMode ) ;
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : RegisterCollectionInstances ( )
{
2021-11-02 11:12:43 -04:00
for ( TActorIterator < ASmartObjectCollection > It ( GetWorld ( ) ) ; It ; + + It )
2021-09-28 13:33:00 -04:00
{
ASmartObjectCollection * Collection = ( * It ) ;
if ( IsValid ( Collection ) & & Collection - > IsRegistered ( ) = = false )
{
2022-01-12 16:15:32 -05:00
const ESmartObjectCollectionRegistrationResult Result = RegisterCollection ( * Collection ) ;
UE_VLOG_UELOG ( Collection , LogSmartObject , Log , TEXT ( " Collection '%s' registration from USmartObjectSubsystem initialization - %s " ) , * Collection - > GetName ( ) , * UEnum : : GetValueAsString ( Result ) ) ;
2021-09-28 13:33:00 -04:00
}
}
}
2022-01-12 16:15:32 -05:00
ESmartObjectCollectionRegistrationResult USmartObjectSubsystem : : RegisterCollection ( ASmartObjectCollection & InCollection )
2021-09-28 13:33:00 -04:00
{
if ( ! IsValid ( & InCollection ) )
{
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_InvalidCollection ;
2021-09-28 13:33:00 -04:00
}
if ( InCollection . IsRegistered ( ) )
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Error , TEXT ( " Trying to register collection '%s' more than once " ) , * InCollection . GetName ( ) ) ;
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_AlreadyRegistered ;
2021-09-28 13:33:00 -04:00
}
2022-01-12 16:15:32 -05:00
ESmartObjectCollectionRegistrationResult Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2021-09-28 13:33:00 -04:00
if ( InCollection . GetLevel ( ) - > IsPersistentLevel ( ) )
{
2021-11-02 11:12:43 -04:00
ensureMsgf ( ! IsValid ( MainCollection ) , TEXT ( " Not expecting to set the main collection more than once " ) ) ;
2022-01-12 16:15:32 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Log , TEXT ( " Main collection '%s' registered with %d entries " ) , * InCollection . GetName ( ) , InCollection . GetEntries ( ) . Num ( ) ) ;
2021-09-28 13:33:00 -04:00
MainCollection = & InCollection ;
# if WITH_EDITOR
2021-11-03 15:47:51 -04:00
OnMainCollectionChanged . Broadcast ( ) ;
2021-11-02 11:12:43 -04:00
// For a collection that is automatically updated, it gets rebuilt on registration in the Edition world.
const UWorld & World = GetWorldRef ( ) ;
if ( ! World . IsGameWorld ( ) & &
! World . IsPartitionedWorld ( ) & &
! MainCollection - > IsBuildOnDemand ( ) )
2021-09-28 13:33:00 -04:00
{
RebuildCollection ( InCollection ) ;
}
# endif // WITH_EDITOR
2021-11-02 11:12:43 -04:00
2021-09-28 13:33:00 -04:00
InCollection . OnRegistered ( ) ;
2022-01-12 16:15:32 -05:00
Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2021-09-28 13:33:00 -04:00
}
else
{
2021-11-18 14:37:34 -05:00
InCollection . MarkAsGarbage ( ) ;
2022-01-12 16:15:32 -05:00
Result = ESmartObjectCollectionRegistrationResult : : Failed_NotFromPersistentLevel ;
2021-09-28 13:33:00 -04:00
}
2022-01-12 16:15:32 -05:00
return Result ;
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : UnregisterCollection ( ASmartObjectCollection & InCollection )
{
if ( MainCollection ! = & InCollection )
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Verbose , TEXT ( " Ignoring unregistration of collection '%s' since this is not the main collection. " ) , * InCollection . GetName ( ) ) ;
2021-09-28 13:33:00 -04:00
return ;
}
2022-01-12 16:15:32 -05:00
MainCollection = nullptr ;
2021-09-28 13:33:00 -04:00
InCollection . OnUnregistered ( ) ;
}
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponent ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
2022-01-19 14:16:16 -05:00
return ( IsValid ( MainCollection ) ? MainCollection - > GetSmartObjectComponent ( ClaimHandle . SmartObjectHandle ) : nullptr ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : InitializeRuntime ( )
2021-09-28 13:33:00 -04:00
{
2022-02-17 03:40:43 -05:00
const UWorld & World = GetWorldRef ( ) ;
2022-02-03 14:03:16 -05:00
EntitySubsystem = World . GetSubsystem < UMassEntitySubsystem > ( ) ;
if ( ! ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to use SmartObjects " ) ) )
{
return ;
}
2021-11-02 11:12:43 -04:00
if ( ! IsValid ( MainCollection ) )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( MainCollection ! = nullptr & & ! MainCollection - > bNetLoadOnClient & & World . IsNetMode ( NM_Client ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Collection not loaded on client. Initialization skipped in %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
}
else
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Missing collection during %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
}
2021-09-28 13:33:00 -04:00
return ;
}
2022-02-21 01:10:34 -05:00
// Initialize spatial representation structure
checkfSlow ( * SpacePartitionClass ! = nullptr , TEXT ( " Partition class is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition = NewObject < USmartObjectSpacePartition > ( this , SpacePartitionClass ) ;
SpacePartition - > SetBounds ( MainCollection - > GetBounds ( ) ) ;
2021-11-02 11:12:43 -04:00
2021-11-26 15:48:13 -05:00
// Perform all validations at once since multiple entries can share the same definition
MainCollection - > ValidateDefinitions ( ) ;
2021-11-02 11:12:43 -04:00
2021-09-28 13:33:00 -04:00
// Build all runtime from collection
for ( const FSmartObjectCollectionEntry & Entry : MainCollection - > GetEntries ( ) )
{
2021-11-26 15:48:13 -05:00
const USmartObjectDefinition * Definition = MainCollection - > GetDefinitionForEntry ( Entry ) ;
2021-09-28 13:33:00 -04:00
USmartObjectComponent * Component = Entry . GetComponent ( ) ;
2021-11-26 15:48:13 -05:00
if ( Definition = = nullptr | | Definition - > IsValid ( ) = = false )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
UE_CVLOG_UELOG ( Component ! = nullptr , Component - > GetOwner ( ) , LogSmartObject , Error ,
2021-11-26 15:48:13 -05:00
TEXT ( " Skipped runtime data creation for SmartObject %s: Invalid definition " ) , * GetNameSafe ( Component - > GetOwner ( ) ) ) ;
2021-09-28 13:33:00 -04:00
continue ;
}
2022-01-31 18:52:49 -05:00
// Create a runtime instance of that definition using that handle
2021-11-02 11:12:43 -04:00
if ( Component ! = nullptr )
{
2022-01-19 14:16:16 -05:00
Component - > SetRegisteredHandle ( Entry . GetHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
}
2021-11-26 15:48:13 -05:00
AddToSimulation ( Entry , * Definition ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-02 11:12:43 -04:00
// Until this point all runtime entries were created from the collection, start tracking newly created
RuntimeCreatedEntries . Reset ( ) ;
// Note that we use our own flag instead of relying on World.HasBegunPlay() since world might not be marked
// as BegunPlay immediately after subsystem OnWorldBeingPlay gets called (e.g. waiting game mode to be ready on clients)
bInitialCollectionAddedToSimulation = true ;
2022-02-03 14:03:16 -05:00
// Flush all entity subsystem commands pushed while adding collection entries to the simulation
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-02-11 08:53:05 -05:00
EntitySubsystem - > FlushCommands ( ) ;
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
}
# endif // UE_ENABLE_DEBUG_DRAWING
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : CleanupRuntime ( )
{
EntitySubsystem = GetWorldRef ( ) . GetSubsystem < UMassEntitySubsystem > ( ) ;
if ( ! ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to use SmartObjects " ) ) )
{
return ;
}
for ( auto It ( RuntimeSmartObjects . CreateIterator ( ) ) ; It ; + + It )
{
RemoveFromSimulation ( It . Key ( ) ) ;
}
RuntimeCreatedEntries . Reset ( ) ;
bInitialCollectionAddedToSimulation = false ;
// Flush all entity subsystem commands pushed while stopping the simulation
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-02-17 03:40:54 -05:00
EntitySubsystem - > FlushCommands ( ) ;
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
}
# endif // UE_ENABLE_DEBUG_DRAWING
2022-02-17 03:40:43 -05:00
}
void USmartObjectSubsystem : : OnWorldBeginPlay ( UWorld & World )
{
Super : : OnWorldBeginPlay ( World ) ;
InitializeRuntime ( ) ;
}
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : ComputeBounds ( const UWorld & World , ASmartObjectCollection & Collection ) const
{
FBox Bounds ( ForceInitToZero ) ;
if ( const UWorldPartition * WorldPartition = World . GetWorldPartition ( ) )
{
Bounds = WorldPartition - > GetWorldBounds ( ) ;
}
else if ( const ULevel * PersistentLevel = World . PersistentLevel . Get ( ) )
{
if ( PersistentLevel - > LevelBoundsActor . IsValid ( ) )
{
Bounds = PersistentLevel - > LevelBoundsActor . Get ( ) - > GetComponentsBoundingBox ( ) ;
}
else
{
Bounds = ALevelBounds : : CalculateLevelBounds ( PersistentLevel ) ;
}
}
else
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Unable to determine world bounds: no world partition or persistent level. " ) ) ;
}
Collection . SetBounds ( Bounds ) ;
}
2021-09-28 13:33:00 -04:00
void USmartObjectSubsystem : : RebuildCollection ( ASmartObjectCollection & InCollection )
{
InCollection . RebuildCollection ( RegisteredSOComponents ) ;
}
void USmartObjectSubsystem : : SpawnMissingCollection ( )
{
2021-11-02 11:12:43 -04:00
if ( IsValid ( MainCollection ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
2021-11-02 11:12:43 -04:00
UWorld & World = GetWorldRef ( ) ;
2021-09-28 13:33:00 -04:00
FActorSpawnParameters SpawnInfo ;
2021-11-02 11:12:43 -04:00
SpawnInfo . OverrideLevel = World . PersistentLevel ;
2021-09-28 13:33:00 -04:00
SpawnInfo . bAllowDuringConstructionScript = true ;
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Spawning missing collection for world '%s'. " ) , * World . GetName ( ) ) ;
World . SpawnActor < ASmartObjectCollection > ( ASmartObjectCollection : : StaticClass ( ) , SpawnInfo ) ;
checkf ( IsValid ( MainCollection ) , TEXT ( " MainCollection must be assigned after spawning " ) ) ;
2021-09-28 13:33:00 -04:00
}
# endif // WITH_EDITOR
# if WITH_SMARTOBJECT_DEBUG
void USmartObjectSubsystem : : DebugUnregisterAllSmartObjects ( )
{
for ( TWeakObjectPtr < USmartObjectComponent > & WeakComponent : DebugRegisteredComponents )
{
if ( const USmartObjectComponent * Cmp = WeakComponent . Get ( ) )
{
RemoveFromSimulation ( * Cmp ) ;
}
}
}
void USmartObjectSubsystem : : DebugRegisterAllSmartObjects ( )
{
for ( TWeakObjectPtr < USmartObjectComponent > & WeakComponent : DebugRegisteredComponents )
{
if ( const USmartObjectComponent * Cmp = WeakComponent . Get ( ) )
{
AddToSimulation ( * Cmp ) ;
}
}
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : DebugInitializeRuntime ( )
{
// do not initialize more than once or on a GameWorld
if ( bInitialCollectionAddedToSimulation | | GetWorldRef ( ) . IsGameWorld ( ) )
{
return ;
}
InitializeRuntime ( ) ;
}
void USmartObjectSubsystem : : DebugCleanupRuntime ( )
{
// do not cleanup more than once or on a GameWorld
if ( ! bInitialCollectionAddedToSimulation | | GetWorldRef ( ) . IsGameWorld ( ) )
{
return ;
}
CleanupRuntime ( ) ;
}
# endif // WITH_SMARTOBJECT_DEBUG