2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectSubsystem.h"
2023-01-19 00:48:07 -05:00
# include "Math/ColorList.h"
2021-09-28 13:33:00 -04:00
# include "SmartObjectComponent.h"
2023-03-14 06:26:29 -04:00
# include "SmartObjectTypes.h"
2021-09-28 13:33:00 -04:00
# include "EngineUtils.h"
2022-01-19 14:16:16 -05:00
# include "MassCommandBuffer.h"
2022-08-12 10:47:11 -04:00
# include "MassEntitySubsystem.h"
2022-02-21 01:10:34 -05:00
# include "SmartObjectHashGrid.h"
2023-02-02 18:43:13 -05:00
# include "WorldConditionContext.h"
2021-09-28 13:33:00 -04:00
# include "VisualLogger/VisualLogger.h"
2022-11-24 14:53:52 -05:00
# include "Engine/LevelStreaming.h"
2023-03-02 05:58:30 -05:00
# include "NavigationSystem.h"
# include "AI/Navigation/NavigationTypes.h"
2023-03-14 06:26:29 -04:00
# include "Annotations/SmartObjectSlotEntranceAnnotation.h"
2021-09-28 13:33:00 -04:00
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectSubsystem)
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
# include "SmartObjectSubsystemRenderingActor.h"
# endif
2022-01-26 17:33:02 -05:00
# if WITH_SMARTOBJECT_DEBUG
# endif
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
# include "Engine/LevelBounds.h"
# include "WorldPartition/WorldPartition.h"
# endif
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
# include "SmartObjectCollection.h"
# endif // WITH_EDITORONLY_DATA
2022-04-04 13:22:01 -04:00
namespace UE : : SmartObject
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
// Indicates that runtime shouldn't be initialized.
// This flag must be set BEFORE launching the game and not toggled after.
bool bDisableRuntime = false ;
FAutoConsoleVariableRef CVarDisableRuntime (
TEXT ( " ai.smartobject.DisableRuntime " ) ,
bDisableRuntime ,
TEXT ( " If enabled, runtime instances won't be created for baked collection entries or runtime added ones from component registration. " ) ,
ECVF_Default ) ;
2022-04-04 13:22:01 -04:00
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
2022-11-24 14:53:52 -05:00
namespace Debug
{
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 )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugRegisterAllSmartObjects ( ) ;
}
} )
) ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05: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 )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
{
Subsystem - > DebugUnregisterAllSmartObjects ( ) ;
}
} )
) ;
} // UE::SmartObject::Debug
2022-04-04 13:22:01 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
2023-03-02 05:58:30 -05:00
2022-04-04 13:22:01 -04:00
} // UE::SmartObject
2021-09-28 13:33:00 -04:00
2023-03-02 05:58:30 -05:00
//----------------------------------------------------------------------//
// FSmartObjectSlotEntryRequest
//----------------------------------------------------------------------//
2023-03-14 06:26:29 -04:00
bool FSmartObjectSlotEntranceLocationRequest : : SetNavigationDataFromActor ( const AActor & InRequesterActor , const TSubclassOf < USmartObjectSlotValidationFilter > InValidationFilter )
2023-03-02 05:58:30 -05:00
{
SearchLocation = FVector : : ZeroVector ;
2023-03-14 06:26:29 -04:00
InstigatorActor = nullptr ;
2023-03-02 05:58:30 -05:00
NavigationData = nullptr ;
NavigationFilter = nullptr ;
2023-03-14 06:26:29 -04:00
const UWorld * World = InRequesterActor . GetWorld ( ) ;
2023-03-02 05:58:30 -05:00
if ( ! World )
{
return false ;
}
2023-03-14 06:26:29 -04:00
NavigationData = UE : : SmartObject : : Annotations : : GetNavDataForActor ( * World , & InRequesterActor ) ;
2023-03-02 05:58:30 -05:00
if ( ! NavigationData )
{
return false ;
}
2023-03-14 06:26:29 -04:00
const USmartObjectSlotValidationFilter * Filter = InValidationFilter . GetDefaultObject ( ) ;
if ( Filter )
2023-03-02 05:58:30 -05:00
{
2023-03-14 06:26:29 -04:00
NavigationFilter = UNavigationQueryFilter : : GetQueryFilter ( * NavigationData , & InRequesterActor , Filter - > GetNavigationFilter ( ) ) ;
if ( Filter - > GetNavigationFilter ( ) . Get ( ) & & ! NavigationFilter )
{
return false ;
}
2023-03-02 05:58:30 -05:00
}
2023-03-14 06:26:29 -04:00
InstigatorActor = & InRequesterActor ;
ValidationFilter = InValidationFilter ;
2023-03-02 05:58:30 -05:00
return true ;
}
2021-09-28 13:33:00 -04:00
//----------------------------------------------------------------------//
// USmartObjectSubsystem
//----------------------------------------------------------------------//
2022-11-24 14:53:52 -05:00
USmartObjectSubsystem : : USmartObjectSubsystem ( )
: SmartObjectContainer ( this )
{
}
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : OnWorldComponentsUpdated ( UWorld & World )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
bIsPartitionedWorld = World . IsPartitionedWorld ( ) ;
# endif // WITH_EDITORONLY_DATA
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 ( ) )
{
2023-03-01 15:43:52 -05:00
SpacePartitionClass = LoadClass < USmartObjectSpacePartition > ( nullptr , * SpacePartitionClassName . ToString ( ) ) ;
2022-02-21 01:10:34 -05:00
UE_CVLOG_UELOG ( * SpacePartitionClass = = nullptr , this , LogSmartObject , Error , TEXT ( " Unable to load class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
2022-04-26 14:39:26 -04:00
}
2022-02-21 01:10:34 -05:00
// Class not specified or invalid, use some default
if ( SpacePartitionClass . Get ( ) = = nullptr )
2022-04-26 14:39:26 -04:00
{
2022-02-21 01:10:34 -05:00
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
2022-05-10 16:35:49 -04:00
if ( RenderingActor = = nullptr )
{
FActorSpawnParameters SpawnInfo ;
SpawnInfo . SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod : : AlwaysSpawn ;
RenderingActor = World . SpawnActor < ASmartObjectSubsystemRenderingActor > ( SpawnInfo ) ;
}
2022-02-21 01:10:34 -05:00
# 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
2022-12-08 11:50:57 -05:00
if ( ! World . IsGameWorld ( ) & & bAutoInitializeEditorInstances )
2021-11-02 11:12:43 -04:00
{
2022-11-24 14:53:52 -05:00
// calculating world bounds first since InitializeRuntime is using that data to create the USmartObjectSpacePartition
// instance. Note that we use the World-calculated bounds only for editor worlds, since Runtime SmartObjectContainer's
// bounds will rely on existing SmartObjectCollections. In editor we use world's size to not resize the
// USmartObjectSpacePartition with SO operations
2022-12-05 08:19:29 -05:00
SmartObjectContainer . SetBounds ( ComputeBounds ( World ) ) ;
2022-11-24 14:53:52 -05:00
InitializeRuntime ( ) ;
2021-11-02 11:12:43 -04:00
}
2022-02-21 01:10:34 -05:00
# endif // WITH_EDITOR
2021-09-28 13:33:00 -04:00
}
USmartObjectSubsystem * USmartObjectSubsystem : : GetCurrent ( const UWorld * World )
{
return UWorld : : GetSubsystem < USmartObjectSubsystem > ( World ) ;
}
2023-02-02 18:43:13 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : AddComponentToSimulation (
USmartObjectComponent & SmartObjectComponent ,
const FSmartObjectCollectionEntry & NewEntry ,
const bool bCommitChanges
)
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
checkf ( SmartObjectComponent . GetDefinition ( ) ! = nullptr , TEXT ( " Shouldn't reach this point with an invalid definition asset " ) ) ;
2023-02-02 18:43:13 -05:00
FSmartObjectRuntime * SmartObjectRuntime = AddCollectionEntryToSimulation ( NewEntry , * SmartObjectComponent . GetDefinition ( ) , & SmartObjectComponent , bCommitChanges ) ;
if ( SmartObjectRuntime ! = nullptr )
{
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
}
return SmartObjectRuntime ;
2022-03-16 03:47:02 -04:00
}
void USmartObjectSubsystem : : BindComponentToSimulation ( USmartObjectComponent & SmartObjectComponent )
{
2022-12-07 13:05:23 -05:00
ensureMsgf ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) , TEXT ( " %s expects input SmartObjectComponent to be already registered. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
2023-02-02 18:43:13 -05:00
// Notify the component to bind to its runtime counterpart
2022-03-16 03:47:02 -04:00
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Binding a component should only be used when an associated runtime instance exists. " ) ) )
{
2023-02-02 18:43:13 -05:00
BindComponentToSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
2022-03-16 03:47:02 -04:00
}
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : BindComponentToSimulationInternal ( USmartObjectComponent & SmartObjectComponent , FSmartObjectRuntime & SmartObjectRuntime )
{
// It is possible that the component is already linked to the runtime instance when the collection entry was initially added.
ensureMsgf ( ! SmartObjectRuntime . OwnerComponent . IsValid ( ) | | SmartObjectRuntime . OwnerComponent = = & SmartObjectComponent ,
TEXT ( " Different OwnerComponent (was %s) when binding SmartObjectComponent %s. This might indicate multiple objects using the same handle. " ) ,
* GetFullNameSafe ( SmartObjectRuntime . OwnerComponent . Get ( ) ) , * GetFullNameSafe ( & SmartObjectComponent ) ) ;
SmartObjectRuntime . OwnerComponent = & SmartObjectComponent ;
SmartObjectComponent . OnRuntimeInstanceBound ( SmartObjectRuntime ) ;
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " SmartObjectComponent %s bound to simulation. " ) , * GetFullNameSafe ( & SmartObjectComponent ) ) ;
}
2022-03-16 03:47:02 -04:00
void USmartObjectSubsystem : : UnbindComponentFromSimulation ( USmartObjectComponent & SmartObjectComponent )
{
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
2023-01-18 10:52:51 -05:00
if ( ensureMsgf ( SmartObjectRuntime ! = nullptr ,
TEXT ( " Unbinding SmartObjectComponent %s but its associated runtime instance doesn't exist. This might indicate multiple objects using the same handle. " ) ,
* GetFullNameSafe ( & SmartObjectComponent ) ) )
2022-03-16 03:47:02 -04:00
{
2022-11-24 14:53:52 -05:00
UnbindComponentFromSimulationInternal ( SmartObjectComponent , * SmartObjectRuntime ) ;
2023-01-18 10:52:51 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " SmartObjectComponent %s unbound from simulation. " ) , * GetFullNameSafe ( & SmartObjectComponent ) ) ;
2022-03-16 03:47:02 -04:00
}
}
2022-11-24 14:53:52 -05:00
void USmartObjectSubsystem : : UnbindComponentFromSimulationInternal ( USmartObjectComponent & SmartObjectComponent , FSmartObjectRuntime & SmartObjectRuntime )
{
2023-02-02 18:43:13 -05:00
SmartObjectComponent . OnRuntimeInstanceUnbound ( SmartObjectRuntime ) ;
2022-12-07 13:05:23 -05:00
SmartObjectComponent . InvalidateRegisteredHandle ( ) ;
2023-01-18 10:52:51 -05:00
SmartObjectRuntime . OwnerComponent = nullptr ;
2022-11-24 14:53:52 -05:00
}
2023-02-02 18:43:13 -05:00
FSmartObjectRuntime * USmartObjectSubsystem : : AddCollectionEntryToSimulation (
const FSmartObjectCollectionEntry & Entry ,
const USmartObjectDefinition & Definition ,
USmartObjectComponent * OwnerComponent ,
const bool bCommitChanges
)
2022-03-16 03:47:02 -04:00
{
const FSmartObjectHandle Handle = Entry . GetHandle ( ) ;
const FTransform & Transform = Entry . GetTransform ( ) ;
const FBox & Bounds = Entry . GetBounds ( ) ;
const FGameplayTagContainer & Tags = Entry . GetTags ( ) ;
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
{
2022-03-16 03:47:02 -04:00
return nullptr ;
2021-09-28 13:33:00 -04:00
}
2022-12-06 13:20:56 -05:00
// @todo temporarily commenting out the ensure while the proper fix is being developed.
//if (!ensureMsgf(RuntimeSmartObjects.Find(Handle) == nullptr, TEXT("Handle '%s' already registered in runtime simulation"), *LexToString(Handle)))
if ( RuntimeSmartObjects . Find ( Handle ) ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
return nullptr ;
2021-09-28 13:33:00 -04:00
}
2022-08-12 07:56:27 -04:00
if ( ! ensureMsgf ( EntityManager , TEXT ( " Entity subsystem required to add a smartobject to the simulation " ) ) )
2022-02-03 14:03:16 -05:00
{
2022-03-16 03:47:02 -04:00
return nullptr ;
2022-02-03 14:03:16 -05:00
}
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 ) ) ;
2022-03-02 13:25:37 -05:00
Runtime . SetRegisteredHandle ( Handle ) ;
2022-03-16 03:47:02 -04:00
Runtime . Tags = Tags ;
2023-01-18 10:52:51 -05:00
Runtime . OwnerComponent = OwnerComponent ;
UE_CVLOG_UELOG ( OwnerComponent ! = nullptr , this , LogSmartObject , Verbose , TEXT ( " SmartObjectComponent %s added to simulation. " ) , * GetFullNameSafe ( OwnerComponent ) ) ;
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-11-17 07:44:24 -05:00
FWorldConditionContextData ConditionContextData ( * Definition . GetWorldConditionSchema ( ) ) ;
2023-01-18 10:52:51 -05:00
SetupConditionContextCommonData ( ConditionContextData , Runtime ) ;
2022-11-23 09:11:13 -05:00
2023-01-18 10:52:51 -05:00
// Always initialize state (handles empty conditions)
Runtime . PreconditionState . Initialize ( * this , Definition . GetPreconditions ( ) ) ;
// Activate Object Preconditions if any
const FWorldConditionContext ObjectContext ( Runtime . PreconditionState , ConditionContextData ) ;
if ( ! ObjectContext . Activate ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Failed to activate Preconditions on SmartObject '%s'. " ) , * LexToString ( Handle ) ) ;
}
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 ;
2023-01-18 10:52:51 -05:00
const USmartObjectWorldConditionSchema * DefaultWorldConditionSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2022-02-03 14:03:16 -05:00
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 ;
2022-11-01 15:11:25 -04:00
2022-10-31 20:00:30 -04:00
const uint32 Hash = HashCombine ( Definition . GetUniqueID ( ) , SlotIndex ) ;
2023-02-21 13:03:02 -05:00
FConstSharedStruct SharedFragment = EntityManager - > GetOrCreateSharedFragmentByHash < FSmartObjectSlotDefinitionFragment > ( Hash , Definition , SlotDefinition ) ;
2022-11-01 15:11:25 -04:00
2022-02-03 14:03:16 -05:00
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-08-12 07:56:27 -04:00
const FMassEntityHandle EntityHandle = EntityManager - > ReserveEntity ( ) ;
EntityManager - > Defer ( ) . PushCommand < FMassCommandBuildEntityWithSharedFragments > ( EntityHandle , MoveTemp ( SharedFragmentValues ) , TransformFragment ) ;
2022-02-03 14:03:16 -05:00
FSmartObjectSlotHandle SlotHandle ( EntityHandle ) ;
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot & Slot = RuntimeSlots . Add ( SlotHandle , FSmartObjectRuntimeSlot ( Handle , SlotIndex ) ) ;
2023-01-18 10:52:51 -05:00
// Setup initial state from slot definition and current object state
Slot . bSlotEnabled = SlotDefinition . bEnabled ;
Slot . bObjectEnabled = Runtime . IsEnabled ( ) ;
2022-11-01 15:11:25 -04:00
Slot . Tags = SlotDefinition . RuntimeTags ;
2022-11-17 07:44:24 -05:00
2023-01-18 10:52:51 -05:00
// Always initialize state (handles empty conditions)
Slot . PreconditionState . Initialize ( * this , SlotDefinition . SelectionPreconditions ) ;
2023-01-18 01:06:45 -05:00
2023-01-18 10:52:51 -05:00
// Activate slot Preconditions if any
2023-02-02 18:43:13 -05:00
ensureMsgf ( ConditionContextData . SetContextData ( DefaultWorldConditionSchema - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
2023-01-18 10:52:51 -05:00
const FWorldConditionContext SlotContext ( Slot . PreconditionState , ConditionContextData ) ;
if ( ! SlotContext . Activate ( ) )
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " Failed to activate Preconditions on SmartObject '%s' slot '%s'. " ) , * LexToString ( Handle ) , * LexToString ( SlotHandle ) ) ;
2022-11-17 07:44:24 -05:00
}
2022-11-01 15:11:25 -04:00
2022-02-03 14:03:16 -05:00
Runtime . SlotHandles [ SlotIndex ] = SlotHandle ;
SlotIndex + + ;
}
2022-11-24 14:53:52 -05:00
if ( bCommitChanges )
2022-02-03 14:03:16 -05:00
{
// 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-08-12 07:56:27 -04:00
EntityManager - > 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 ) ;
2022-03-16 03:47:02 -04:00
return & Runtime ;
2021-09-28 13:33:00 -04:00
}
2022-10-26 19:34:40 -04:00
bool USmartObjectSubsystem : : RemoveRuntimeInstanceFromSimulation ( 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-10-26 19:34:40 -04:00
# if WITH_SMARTOBJECT_DEBUG
ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " RemoveFromSimulation is an internal call and should only be used for objects still part of the simulation " ) ) ;
# endif // WITH_SMARTOBJECT_DEBUG
if ( SmartObjectRuntime = = nullptr )
2021-09-28 13:33:00 -04:00
{
2023-01-18 10:52:51 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " %s called with %s SO Handle and no corresponding SmartObjectRuntime " )
2022-10-26 19:34:40 -04:00
, ANSI_TO_TCHAR ( __FUNCTION__ )
, Handle . IsValid ( ) ? TEXT ( " a VALID " ) : TEXT ( " an INVALID " ) ) ;
return false ;
2021-09-28 13:33:00 -04:00
}
2022-08-12 07:56:27 -04:00
if ( ! ensureMsgf ( EntityManager , TEXT ( " Entity subsystem required to remove a smartobject from the simulation " ) ) )
2022-02-21 01:10:34 -05:00
{
2022-10-26 19:34:40 -04:00
return false ;
2022-02-21 01:10:34 -05:00
}
2022-10-26 19:34:40 -04:00
DestroyRuntimeInstanceInternal ( Handle , * SmartObjectRuntime , * EntityManager . Get ( ) ) ;
// Remove object runtime data
RuntimeSmartObjects . Remove ( Handle ) ;
return true ;
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : DestroyRuntimeInstanceInternal (
const FSmartObjectHandle Handle ,
FSmartObjectRuntime & SmartObjectRuntime ,
const FMassEntityManager & EntityManagerRef
)
2022-10-26 19:34:40 -04:00
{
2022-01-26 17:33:02 -05:00
// Abort everything before removing since abort flow may require access to runtime data
2022-11-01 15:11:25 -04:00
AbortAll ( SmartObjectRuntime ) ;
2022-01-26 17:33:02 -05:00
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. " ) ) ;
2022-10-26 19:34:40 -04:00
SpacePartition - > Remove ( Handle , SmartObjectRuntime . SpatialEntryData ) ;
2022-01-19 14:16:16 -05:00
2023-01-18 10:52:51 -05:00
FWorldConditionContextData ConditionContextData ( * SmartObjectRuntime . GetDefinition ( ) . GetWorldConditionSchema ( ) ) ;
SetupConditionContextCommonData ( ConditionContextData , SmartObjectRuntime ) ;
// Deactivate object Preconditions
const FWorldConditionContext ObjectContext ( SmartObjectRuntime . PreconditionState , ConditionContextData ) ;
ObjectContext . Deactivate ( ) ;
2022-11-17 07:44:24 -05:00
2022-02-21 01:10:34 -05:00
// Destroy entities associated to slots
2023-01-18 10:52:51 -05:00
const USmartObjectWorldConditionSchema * DefaultWorldConditionSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2022-01-19 14:16:16 -05:00
TArray < FMassEntityHandle > EntitiesToDestroy ;
2022-10-26 19:34:40 -04:00
EntitiesToDestroy . Reserve ( SmartObjectRuntime . SlotHandles . Num ( ) ) ;
for ( const FSmartObjectSlotHandle SlotHandle : SmartObjectRuntime . SlotHandles )
2022-01-19 14:16:16 -05:00
{
2023-02-02 18:43:13 -05:00
ensureMsgf ( ConditionContextData . SetContextData ( DefaultWorldConditionSchema - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
2023-01-18 01:06:45 -05:00
2023-01-18 10:52:51 -05:00
// Deactivate slot Preconditions (if successfully initialized)
const FSmartObjectRuntimeSlot & RuntimeSlot = RuntimeSlots . FindChecked ( SlotHandle ) ;
const FWorldConditionContext SlotContext ( RuntimeSlot . PreconditionState , ConditionContextData ) ;
SlotContext . Deactivate ( ) ;
2022-11-17 07:44:24 -05:00
2022-11-01 15:11:25 -04:00
RuntimeSlots . Remove ( SlotHandle ) ;
2022-01-19 14:16:16 -05:00
EntitiesToDestroy . Add ( SlotHandle ) ;
}
2022-10-26 19:34:40 -04:00
EntityManagerRef . Defer ( ) . DestroyEntities ( EntitiesToDestroy ) ;
2021-09-28 13:33:00 -04:00
}
2022-10-26 19:34:40 -04:00
bool USmartObjectSubsystem : : RemoveCollectionEntryFromSimulation ( const FSmartObjectCollectionEntry & Entry )
2021-09-28 13:33:00 -04:00
{
2022-10-26 19:34:40 -04:00
return RemoveRuntimeInstanceFromSimulation ( Entry . GetHandle ( ) ) ;
2021-11-02 11:12:43 -04:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : RemoveComponentFromSimulation ( const USmartObjectComponent & SmartObjectComponent )
2021-11-02 11:12:43 -04:00
{
2022-10-26 19:34:40 -04:00
if ( RemoveRuntimeInstanceFromSimulation ( SmartObjectComponent . GetRegisteredHandle ( ) ) )
{
2023-01-18 10:52:51 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " %s call succeeded for %s " )
, ANSI_TO_TCHAR ( __FUNCTION__ )
, * GetFullNameSafe ( & SmartObjectComponent ) ) ;
2022-10-26 19:34:40 -04:00
}
else
{
2023-01-18 10:52:51 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " %s call failed for %s " )
2022-10-26 19:34:40 -04:00
, ANSI_TO_TCHAR ( __FUNCTION__ )
, * GetFullNameSafe ( & SmartObjectComponent ) ) ;
}
2021-11-02 11:12:43 -04:00
}
2022-11-01 15:11:25 -04:00
void USmartObjectSubsystem : : AbortAll ( const FSmartObjectRuntime & SmartObjectRuntime )
2022-03-02 13:25:37 -05:00
{
2022-03-17 19:07:54 -04:00
for ( const FSmartObjectSlotHandle SlotHandle : SmartObjectRuntime . SlotHandles )
2022-03-02 13:25:37 -05:00
{
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot & Slot = RuntimeSlots . FindChecked ( SlotHandle ) ;
switch ( Slot . State )
2022-03-02 13:25:37 -05:00
{
case ESmartObjectSlotState : : Claimed :
case ESmartObjectSlotState : : Occupied :
{
2022-11-01 15:11:25 -04:00
const FSmartObjectClaimHandle ClaimHandle ( SmartObjectRuntime . GetRegisteredHandle ( ) , SlotHandle , Slot . User ) ;
2023-02-02 18:43:13 -05:00
// Keep user data to be used as payload in the notification event
// since it will be released by the following call to Slot.Release
const FInstancedStruct Payload ( MoveTemp ( Slot . UserData ) ) ;
2022-11-01 15:11:25 -04:00
if ( Slot . Release ( ClaimHandle , /* bAborted */ true ) )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( SmartObjectRuntime , Slot , SlotHandle , ESmartObjectChangeReason : : OnReleased , Payload ) ;
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Slot %s released by an abort " ) , * LexToString ( ClaimHandle . SlotHandle ) ) ;
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle . SlotHandle ) . GetValue ( ) , /*Radius*/ 50.f , FColor : : Red , TEXT ( " Released by abort " ) ) ;
2022-11-01 15:11:25 -04:00
}
2022-03-02 13:25:37 -05:00
break ;
}
case ESmartObjectSlotState : : Free : // falling through on purpose
2022-03-16 03:47:02 -04:00
default :
2022-11-01 15:11:25 -04:00
UE_CVLOG_UELOG ( Slot . User . IsValid ( ) , this , LogSmartObject , Warning ,
2022-03-16 03:47:02 -04:00
TEXT ( " Smart object %s used by %s while the slot it's assigned to is not marked Claimed nor Occupied " ) ,
* LexToString ( SmartObjectRuntime . GetDefinition ( ) ) ,
2022-11-01 15:11:25 -04:00
* LexToString ( Slot . User ) ) ;
2022-03-02 13:25:37 -05:00
break ;
}
2022-11-01 15:11:25 -04:00
Slot . State = ESmartObjectSlotState : : Free ;
2022-03-02 13:25:37 -05:00
}
}
2021-11-02 11:12:43 -04:00
bool USmartObjectSubsystem : : RegisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
2022-03-30 15:13:42 -04:00
{
2022-10-13 02:49:16 -04:00
if ( SmartObjectComponent . GetDefinition ( ) = = nullptr )
{
2023-03-09 11:16:09 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register %s while its DefinitionAsset is not set. Bailing out. " ) ,
2022-10-13 02:49:16 -04:00
* GetFullNameSafe ( & SmartObjectComponent ) ) ;
return false ;
}
2022-11-04 06:56:50 -04:00
TOptional < bool > bIsValid = SmartObjectComponent . GetDefinition ( ) - > IsValid ( ) ;
if ( bIsValid . IsSet ( ) = = false )
{
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register %s while its DefinitionAsset has not been Validated. Validating now. " ) ,
* GetFullNameSafe ( & SmartObjectComponent ) ) ;
bIsValid = SmartObjectComponent . GetDefinition ( ) - > Validate ( ) ;
}
2023-01-23 18:48:38 -05:00
if ( bIsValid . GetValue ( ) = = false )
2022-11-04 03:35:03 -04:00
{
2023-03-09 11:16:09 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Attempting to register %s while its DefinitionAsset fails validation test. Bailing out. "
2023-01-23 18:48:38 -05:00
" Resave asset %s to see the errors and fix the problem. " ) ,
* GetFullNameSafe ( & SmartObjectComponent ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
2022-11-04 03:35:03 -04:00
return false ;
}
2022-10-13 02:49:16 -04:00
if ( ! RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
2022-03-30 15:13:42 -04:00
{
return RegisterSmartObjectInternal ( SmartObjectComponent ) ;
}
2023-01-23 18:48:38 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to register %s. Already registered " ) , * GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
2022-03-30 15:13:42 -04:00
return false ;
}
bool USmartObjectSubsystem : : RegisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent )
2021-11-02 11:12:43 -04:00
{
2022-03-16 03:47:02 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " Registering %s using definition %s. " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
2022-12-05 08:19:29 -05:00
// until the runtime is initialized we're not ready to register SmartObject. We collect them in PendingSmartObjectRegistration
// and process them in InitializeRuntime call.
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
if ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
// Simply bind the newly available component to its active runtime instance
BindComponentToSimulation ( SmartObjectComponent ) ;
2021-11-02 11:12:43 -04:00
}
2022-12-05 08:19:29 -05:00
else
2022-11-24 14:53:52 -05:00
{
2022-12-05 08:19:29 -05:00
bool bAlreadyInCollection = false ;
if ( const FSmartObjectCollectionEntry * Entry = SmartObjectContainer . AddSmartObject ( SmartObjectComponent , bAlreadyInCollection ) )
2022-11-24 14:53:52 -05:00
{
2022-12-07 13:05:23 -05:00
if ( bAlreadyInCollection )
2022-12-05 08:19:29 -05:00
{
2023-02-02 18:43:13 -05:00
SmartObjectComponent . SetRegisteredHandle ( Entry - > GetHandle ( ) , ESmartObjectRegistrationType : : WithCollection ) ;
2022-12-07 13:05:23 -05:00
BindComponentToSimulation ( SmartObjectComponent ) ;
2022-12-05 08:19:29 -05:00
}
2022-12-07 13:05:23 -05:00
else
{
2023-02-02 18:43:13 -05:00
SmartObjectComponent . SetRegisteredHandle ( Entry - > GetHandle ( ) , ESmartObjectRegistrationType : : Dynamic ) ;
2022-12-07 13:05:23 -05:00
AddComponentToSimulation ( SmartObjectComponent , * Entry ) ;
// This is a new entry added after runtime initialization, mark it as a runtime entry (lifetime is tied to the component)
RuntimeCreatedEntries . Add ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
# if WITH_EDITOR
OnMainCollectionDirtied . Broadcast ( ) ;
2022-12-05 08:19:29 -05:00
# endif
2022-12-07 13:05:23 -05:00
}
2021-11-02 11:12:43 -04:00
}
}
2022-10-10 12:00:30 -04:00
ensureMsgf ( RegisteredSOComponents . Find ( & SmartObjectComponent ) = = INDEX_NONE
2022-11-23 09:11:13 -05:00
, TEXT ( " Adding %s to RegisteredSOColleciton, but it has already been added. Missing unregister call? " ) , * SmartObjectComponent . GetFullName ( ) ) ;
2022-10-10 12:00:30 -04:00
RegisteredSOComponents . Add ( & SmartObjectComponent ) ;
2021-11-02 11:12:43 -04:00
}
2022-01-12 16:15:32 -05:00
else
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose ,
TEXT ( " %s not added to collection since InitializeRuntime has not been called yet. Storing SOComponent instance for registration during InitializeRuntime call. " )
2022-12-05 08:19:29 -05:00
, * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
2022-11-24 14:53:52 -05:00
PendingSmartObjectRegistration . Add ( & SmartObjectComponent ) ;
2022-01-12 16:15:32 -05:00
}
2021-11-02 11:12:43 -04:00
2021-09-28 13:33:00 -04:00
return true ;
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : RemoveSmartObject ( USmartObjectComponent & SmartObjectComponent )
{
if ( RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
{
return UnregisterSmartObjectInternal ( SmartObjectComponent , /*bDestroyRuntimeState=*/ true ) ;
}
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to remove %s since it doesn't seem registered " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
return false ;
}
2021-11-02 11:12:43 -04:00
bool USmartObjectSubsystem : : UnregisterSmartObject ( USmartObjectComponent & SmartObjectComponent )
2022-03-30 15:13:42 -04:00
{
if ( RegisteredSOComponents . Contains ( & SmartObjectComponent ) )
{
2023-02-02 18:43:13 -05:00
return UnregisterSmartObjectInternal ( SmartObjectComponent ,
/*bDestroyRuntimeState=*/ SmartObjectComponent . GetRegistrationType ( ) = = ESmartObjectRegistrationType : : Dynamic ) ;
2022-03-30 15:13:42 -04:00
}
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to unregister %s. Already unregistered " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
return false ;
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : UnregisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent , const bool bDestroyRuntimeState )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " Unregistering %s using definition %s. " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
ensure ( SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) ) ;
2021-11-02 11:12:43 -04:00
2023-01-23 18:47:59 -05:00
if ( SmartObjectComponent . GetRegistrationType ( ) = = ESmartObjectRegistrationType : : Dynamic )
2022-12-07 13:05:23 -05:00
{
RuntimeCreatedEntries . Remove ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
}
if ( bDestroyRuntimeState )
2021-11-02 11:12:43 -04:00
{
2022-12-05 08:19:29 -05:00
RemoveComponentFromSimulation ( SmartObjectComponent ) ;
2022-11-24 14:53:52 -05:00
SmartObjectContainer . RemoveSmartObject ( SmartObjectComponent ) ;
2021-11-02 11:12:43 -04:00
}
2022-12-05 08:19:29 -05:00
// otherwise we keep all the runtime entries in place - those will be removed along with the collection that has added them
else
{
// Unbind the component from its associated runtime instance
UnbindComponentFromSimulation ( SmartObjectComponent ) ;
}
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
RegisteredSOComponents . Remove ( & SmartObjectComponent ) ;
}
else
{
PendingSmartObjectRegistration . RemoveSingleSwap ( & SmartObjectComponent ) ;
}
2021-09-28 13:33:00 -04:00
return true ;
}
bool USmartObjectSubsystem : : RegisterSmartObjectActor ( const AActor & SmartObjectActor )
{
2022-03-30 15:13:42 -04:00
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
2022-11-23 09:11:13 -05:00
TEXT ( " Failed to register SmartObject components for %s. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
2021-09-28 13:33:00 -04:00
2022-03-30 15:13:42 -04:00
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( RegisterSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
2021-09-28 13:33:00 -04:00
}
bool USmartObjectSubsystem : : UnregisterSmartObjectActor ( const AActor & SmartObjectActor )
{
2022-03-30 15:13:42 -04:00
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
2022-11-23 09:11:13 -05:00
TEXT ( " Failed to unregister SmartObject components for %s. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
2021-09-28 13:33:00 -04:00
2022-03-30 15:13:42 -04:00
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( UnregisterSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-12-07 13:05:23 -05:00
bool USmartObjectSubsystem : : RemoveSmartObjectActor ( const AActor & SmartObjectActor )
{
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , & SmartObjectActor , LogSmartObject , Log ,
TEXT ( " Failed to remove SmartObject components runtime data for %s. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
int32 NumSuccess = 0 ;
for ( USmartObjectComponent * SOComponent : Components )
{
if ( RemoveSmartObject ( * SOComponent ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
}
2023-01-18 10:52:51 -05:00
bool USmartObjectSubsystem : : SetSmartObjectActorEnabled ( const AActor & SmartObjectActor , const bool bEnabled )
{
TArray < USmartObjectComponent * > Components ;
SmartObjectActor . GetComponents ( Components ) ;
UE_CVLOG_UELOG ( Components . Num ( ) = = 0 , this , LogSmartObject , Log ,
TEXT ( " Failed to change SmartObject components enable state for %s. No components found. " ) , * SmartObjectActor . GetFullName ( ) ) ;
int32 NumSuccess = 0 ;
for ( const USmartObjectComponent * SOComponent : Components )
{
if ( SetEnabled ( SOComponent - > GetRegisteredHandle ( ) , bEnabled ) )
{
NumSuccess + + ;
}
}
return NumSuccess > 0 & & NumSuccess = = Components . Num ( ) ;
}
bool USmartObjectSubsystem : : SetEnabled ( const FSmartObjectHandle Handle , const bool bEnabled )
{
FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) ;
if ( SmartObjectRuntime = = nullptr )
{
UE_VLOG_UELOG ( this , LogSmartObject , Log ,
TEXT ( " Failed to change SmartObject enable state for %s. No associated runtime instance found. " ) , * LexToString ( Handle ) ) ;
return false ;
}
if ( SmartObjectRuntime - > bEnabled = = bEnabled )
{
// Already in the proper state, nothing to notify
return true ;
}
SmartObjectRuntime - > bEnabled = bEnabled ;
// Notify if needed
if ( SmartObjectRuntime - > OnEvent . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime - > GetRegisteredHandle ( ) ;
2023-02-02 18:43:13 -05:00
Data . Reason = bEnabled ? ESmartObjectChangeReason : : OnObjectEnabled : ESmartObjectChangeReason : : OnObjectDisabled ;
2023-01-18 10:52:51 -05:00
SmartObjectRuntime - > OnEvent . Broadcast ( Data ) ;
}
// Propagate object enable state to slots and notify if needed.
for ( const FSmartObjectSlotHandle SlotHandle : SmartObjectRuntime - > SlotHandles )
{
FSmartObjectRuntimeSlot & Slot = RuntimeSlots . FindChecked ( SlotHandle ) ;
// Using 'IsEnabled' to combine slot enable and smart object enable
const bool bSlotPreviousValue = Slot . IsEnabled ( ) ;
// Always set object enabled state even if combined result might not be affected
Slot . bObjectEnabled = bEnabled ;
// Using new combined value to detect changes
if ( Slot . IsEnabled ( ) ! = bSlotPreviousValue )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( * SmartObjectRuntime , Slot , SlotHandle , Slot . IsEnabled ( ) ? ESmartObjectChangeReason : : OnSlotEnabled : ESmartObjectChangeReason : : OnSlotDisabled , Slot . UserData ) ;
2023-01-18 10:52:51 -05:00
}
}
return true ;
}
2023-02-28 10:24:32 -05:00
bool USmartObjectSubsystem : : IsEnabled ( const FSmartObjectHandle Handle ) const
{
const FSmartObjectRuntime * SmartObjectRuntime = GetRuntimeInstance ( Handle ) ;
UE_CVLOG_UELOG ( SmartObjectRuntime = = nullptr , this , LogSmartObject , Log ,
TEXT ( " Failed to get the SmartObject enable state for %s. No associated runtime instance found. " ) , * LexToString ( Handle ) ) ;
return SmartObjectRuntime & & SmartObjectRuntime - > bEnabled ;
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : SetupConditionContextCommonData ( FWorldConditionContextData & ContextData , const FSmartObjectRuntime & SmartObjectRuntime ) const
2023-01-18 10:52:51 -05:00
{
const USmartObjectWorldConditionSchema * DefaultSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2023-02-02 18:43:13 -05:00
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSmartObjectActorRef ( ) , SmartObjectRuntime . GetOwnerActor ( ) ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::GetSmartObjectActorRef to be valid. " ) ) ;
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSmartObjectHandleRef ( ) , & SmartObjectRuntime . RegisteredHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SmartObjectHandleRef to be valid. " ) ) ;
ensureMsgf ( ContextData . SetContextData ( DefaultSchema - > GetSubsystemRef ( ) , this ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SubsystemRef to be valid. " ) ) ;
}
void USmartObjectSubsystem : : BindPropertiesFromStruct ( FWorldConditionContextData & ContextData , const FConstStructView & UserData ) const
{
const UWorldConditionSchema * Schema = ContextData . GetSchema ( ) ;
check ( Schema ) ;
// @todo SO: could create a cache of layouts since user data types shouldn't vary much
// @todo SO: consider moving this into FWorldConditionContextData
for ( TFieldIterator < FProperty > It ( UserData . GetScriptStruct ( ) , EFieldIterationFlags : : None ) ; It ; + + It )
{
const FProperty * Property = * It ;
if ( const FStructProperty * StructProperty = CastField < FStructProperty > ( Property ) )
{
const FWorldConditionContextDataRef Ref = Schema - > GetContextDataRefByName ( Property - > GetFName ( ) , StructProperty - > Struct ) ;
if ( Ref . IsValid ( ) )
{
const FConstStructView StructView ( StructProperty - > Struct , UserData . GetMemory ( ) + Property - > GetOffset_ForInternal ( ) ) ;
ContextData . SetContextData ( Ref , StructView ) ;
}
}
else if ( const FObjectPropertyBase * ObjectProperty = CastField < FObjectPropertyBase > ( Property ) )
{
const FWorldConditionContextDataRef Ref = Schema - > GetContextDataRefByName ( Property - > GetFName ( ) , ObjectProperty - > PropertyClass ) ;
if ( Ref . IsValid ( ) )
{
const UObject * Object = ObjectProperty - > GetObjectPropertyValue ( UserData . GetMemory ( ) + Property - > GetOffset_ForInternal ( ) ) ;
ContextData . SetContextData ( Ref , Object ) ;
}
}
}
2023-01-18 10:52:51 -05:00
}
bool USmartObjectSubsystem : : EvaluateObjectConditions ( const FWorldConditionContextData & ConditionContextData , const FSmartObjectRuntime & SmartObjectRuntime ) const
{
// Evaluate object conditions. Note that unsuccessfully initialized conditions is supported (i.e. error during activation)
const FWorldConditionContext Context ( SmartObjectRuntime . PreconditionState , ConditionContextData ) ;
if ( ! Context . IsTrue ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Preconditions for owning smart object %s failed. " ) , * LexToString ( SmartObjectRuntime . GetRegisteredHandle ( ) ) ) ;
return false ;
}
return true ;
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : EvaluateSlotConditions (
FWorldConditionContextData & ConditionContextData ,
const FSmartObjectSlotHandle SlotHandle ,
const FSmartObjectRuntimeSlot & Slot
) const
2023-01-18 10:52:51 -05:00
{
// Add slot data to the context
const USmartObjectWorldConditionSchema * DefaultSchema = GetDefault < USmartObjectWorldConditionSchema > ( ) ;
2023-02-02 18:43:13 -05:00
ensureMsgf ( ConditionContextData . SetContextData ( DefaultSchema - > GetSlotHandleRef ( ) , & SlotHandle ) ,
TEXT ( " Expecting USmartObjectWorldConditionSchema::SlotHandleRef to be valid. " ) ) ;
2023-01-18 10:52:51 -05:00
// Evaluate slot conditions. Note that unsuccessfully initialized conditions is supported (i.e. error during activation)
const FWorldConditionContext Context ( Slot . PreconditionState , ConditionContextData ) ;
if ( ! Context . IsTrue ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Preconditions for slot %s failed. " ) , * LexToString ( SlotHandle ) ) ;
return false ;
}
return true ;
}
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-03-16 03:47:02 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
if ( SmartObjectRuntime = = nullptr )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-03-02 13:25:37 -05:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
2023-02-02 18:43:13 -05:00
FindSlots ( * SmartObjectRuntime , Filter , SlotHandles , { } ) ;
2022-03-02 13:25:37 -05:00
if ( SlotHandles . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-02-02 18:43:13 -05:00
return Claim ( SlotHandles . Top ( ) , { } ) ;
2021-09-28 13:33:00 -04:00
}
2023-02-02 18:43:13 -05:00
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectSlotHandle SlotHandle , const FConstStructView UserData )
2022-01-19 14:16:16 -05:00
{
2023-02-02 18:43:13 -05:00
if ( ! SlotHandle . IsValid ( ) )
2022-03-18 12:31:49 -04:00
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Claiming using an unset smart object slot handle. Returning invalid FSmartObjectClaimHandle. " ) ) ;
2022-03-18 12:31:49 -04:00
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
if ( Slot = = nullptr )
2022-03-16 03:47:02 -04:00
{
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2023-01-18 10:52:51 -05:00
// Fast test to see if slot can be claimed (Parent smart object is enabled AND slot is free and enabled)
if ( ! Slot - > CanBeClaimed ( ) )
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Log ,
TEXT ( " Can't claim slot handle %s since it is, or its owning smart object %s, disabled or not free. " ) , * LexToString ( SlotHandle ) , * LexToString ( Slot - > GetOwnerRuntimeObject ( ) ) ) ;
2023-01-18 10:52:51 -05:00
return FSmartObjectClaimHandle : : InvalidHandle ;
}
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) ;
if ( SmartObjectRuntime = = nullptr )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error , TEXT ( " Can't claim slot handle %s since its owning smart object instance can't be found. " ) , * LexToString ( SlotHandle ) ) ;
return FSmartObjectClaimHandle : : InvalidHandle ;
}
2022-03-18 12:31:49 -04:00
const FSmartObjectUserHandle User ( NextFreeUserID + + ) ;
2022-11-01 15:11:25 -04:00
const bool bClaimed = Slot - > Claim ( User ) ;
2022-03-18 12:31:49 -04:00
2023-02-02 18:43:13 -05:00
const FSmartObjectClaimHandle ClaimHandle ( Slot - > GetOwnerRuntimeObject ( ) , SlotHandle , User ) ;
2022-03-18 12:31:49 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Claim %s for handle %s. Slot State is '%s' " ) ,
bClaimed ? TEXT ( " SUCCEEDED " ) : TEXT ( " FAILED " ) ,
* LexToString ( ClaimHandle ) ,
2022-11-01 15:11:25 -04:00
* UEnum : : GetValueAsString ( Slot - > GetState ( ) ) ) ;
2022-03-18 12:31:49 -04:00
UE_CVLOG_LOCATION ( bClaimed , this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Yellow , TEXT ( " Claim " ) ) ;
if ( bClaimed )
2022-03-16 03:47:02 -04:00
{
2023-02-02 18:43:13 -05:00
Slot - > UserData = UserData ;
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnClaimed , Slot - > UserData ) ;
2022-03-18 12:31:49 -04:00
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
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : CanBeClaimed ( const FSmartObjectSlotHandle SlotHandle ) const
2022-09-01 09:03:19 -04:00
{
2022-11-01 15:11:25 -04:00
const FSmartObjectRuntimeSlot * Slot = GetSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
return Slot ! = nullptr & & Slot - > CanBeClaimed ( ) ;
2022-09-01 09:03:19 -04:00
}
2022-03-18 12:31:49 -04:00
bool USmartObjectSubsystem : : IsSmartObjectValid ( const FSmartObjectHandle SmartObjectHandle ) const
{
return SmartObjectHandle . IsValid ( ) & & RuntimeSmartObjects . Find ( SmartObjectHandle ) ! = nullptr ;
}
bool USmartObjectSubsystem : : IsClaimedSmartObjectValid ( const FSmartObjectClaimHandle & ClaimHandle ) const
2022-03-16 03:47:02 -04:00
{
return ClaimHandle . IsValid ( ) & & RuntimeSmartObjects . Find ( ClaimHandle . SmartObjectHandle ) ! = nullptr ;
}
2022-03-18 12:31:49 -04:00
bool USmartObjectSubsystem : : IsSlotValidVerbose ( const FSmartObjectSlotHandle SlotHandle , const TCHAR * LogContext ) const
2022-03-16 03:47:02 -04:00
{
2022-03-18 12:31:49 -04:00
UE_CVLOG_UELOG ( ! SlotHandle . IsValid ( ) , this , LogSmartObject , Log ,
TEXT ( " %s failed. SlotHandle is not set. " ) , LogContext ) ;
2022-11-01 15:11:25 -04:00
UE_CVLOG_UELOG ( SlotHandle . IsValid ( ) & & RuntimeSlots . Find ( SlotHandle ) = = nullptr , this , LogSmartObject , Log ,
2022-03-18 12:31:49 -04:00
TEXT ( " %s failed using handle '%s'. Slot is no longer part of the simulation. " ) , LogContext , * LexToString ( SlotHandle ) ) ;
return IsSmartObjectSlotValid ( SlotHandle ) ;
2022-03-16 03:47:02 -04:00
}
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot * USmartObjectSubsystem : : GetMutableSlotVerbose ( const FSmartObjectSlotHandle SlotHandle , const TCHAR * LogContext )
{
UE_CVLOG_UELOG ( ! SlotHandle . IsValid ( ) , this , LogSmartObject , Log ,
TEXT ( " %s failed. SlotHandle is not set. " ) , LogContext ) ;
UE_CVLOG_UELOG ( SlotHandle . IsValid ( ) & & RuntimeSlots . Find ( SlotHandle ) = = nullptr , this , LogSmartObject , Log ,
TEXT ( " %s failed using handle '%s'. Slot is no longer part of the simulation. " ) , LogContext , * LexToString ( SlotHandle ) ) ;
return SlotHandle . IsValid ( ) ? RuntimeSlots . Find ( SlotHandle ) : nullptr ;
}
const FSmartObjectRuntimeSlot * USmartObjectSubsystem : : GetSlotVerbose ( const FSmartObjectSlotHandle SlotHandle , const TCHAR * LogContext ) const
{
UE_CVLOG_UELOG ( ! SlotHandle . IsValid ( ) , this , LogSmartObject , Log ,
TEXT ( " %s failed. SlotHandle is not set. " ) , LogContext ) ;
UE_CVLOG_UELOG ( SlotHandle . IsValid ( ) & & RuntimeSlots . Find ( SlotHandle ) = = nullptr , this , LogSmartObject , Log ,
TEXT ( " %s failed using handle '%s'. Slot is no longer part of the simulation. " ) , LogContext , * LexToString ( SlotHandle ) ) ;
return SlotHandle . IsValid ( ) ? RuntimeSlots . Find ( SlotHandle ) : nullptr ;
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition (
const FSmartObjectClaimHandle & ClaimHandle ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass
)
2022-01-26 17:33:02 -05:00
{
2022-03-16 03:47:02 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( ClaimHandle . SmartObjectHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
2022-09-16 12:46:45 -04:00
return SmartObjectRuntime ! = nullptr ? GetBehaviorDefinition ( * SmartObjectRuntime , ClaimHandle . SlotHandle , DefinitionClass ) : nullptr ;
2022-01-26 17:33:02 -05:00
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinitionByRequestResult (
const FSmartObjectRequestResult & RequestResult ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass
)
2022-09-16 12:46:45 -04:00
{
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( RequestResult . SmartObjectHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
return SmartObjectRuntime ! = nullptr ? GetBehaviorDefinition ( * SmartObjectRuntime , RequestResult . SlotHandle , DefinitionClass ) : nullptr ;
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition (
const FSmartObjectRuntime & SmartObjectRuntime ,
const FSmartObjectSlotHandle SlotHandle ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass
)
2022-01-26 17:33:02 -05:00
{
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
2022-09-16 12:46:45 -04:00
const FSmartObjectSlotIndex SlotIndex ( SmartObjectRuntime . SlotHandles . IndexOfByKey ( SlotHandle ) ) ;
2022-01-26 17:33:02 -05:00
return Definition . GetBehaviorDefinition ( SlotIndex , DefinitionClass ) ;
}
2023-02-02 18:43:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : Use (
const FSmartObjectClaimHandle & ClaimHandle ,
const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass
)
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( ClaimHandle . SmartObjectHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
return SmartObjectRuntime ! = nullptr ? Use ( * SmartObjectRuntime , ClaimHandle , DefinitionClass ) : nullptr ;
2021-09-28 13:33:00 -04:00
}
2023-02-02 18:43:13 -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-03-18 12:31:49 -04:00
checkf ( ClaimHandle . IsValid ( ) , TEXT ( " This is an internal method that should only be called with an assigned claim handle " ) ) ;
2022-03-16 03:47:02 -04:00
2022-11-01 15:11:25 -04:00
if ( ! SmartObjectRuntime . IsEnabled ( ) )
2021-09-28 13:33:00 -04:00
{
2022-03-18 12:31:49 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Can't Use handle %s since associated object is disabled. " ) , * LexToString ( ClaimHandle ) ) ;
2021-09-28 13:33:00 -04:00
return nullptr ;
}
2022-09-16 12:46:45 -04:00
const USmartObjectBehaviorDefinition * BehaviorDefinition = GetBehaviorDefinition ( SmartObjectRuntime , ClaimHandle . SlotHandle , 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 ) ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Green , TEXT ( " Use " ) ) ;
2022-01-19 14:16:16 -05:00
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot & Slot = RuntimeSlots . FindChecked ( ClaimHandle . SlotHandle ) ;
2022-01-19 14:16:16 -05:00
2022-11-01 15:11:25 -04:00
if ( ensureMsgf ( Slot . GetState ( ) = = ESmartObjectSlotState : : Claimed , TEXT ( " Should have been claimed first: %s " ) , * LexToString ( ClaimHandle ) ) & &
ensureMsgf ( Slot . User = = ClaimHandle . UserHandle , TEXT ( " Attempt to use slot %s from handle %s but already assigned to %s " ) ,
* LexToString ( Slot ) , * LexToString ( ClaimHandle ) , * LexToString ( Slot . User ) ) )
2022-01-19 14:16:16 -05:00
{
2022-11-01 15:11:25 -04:00
Slot . State = ESmartObjectSlotState : : Occupied ;
2023-02-02 18:43:13 -05:00
OnSlotChanged ( SmartObjectRuntime , Slot , ClaimHandle . SlotHandle , ESmartObjectChangeReason : : OnOccupied , Slot . UserData ) ;
2022-01-19 14:16:16 -05:00
return BehaviorDefinition ;
}
return nullptr ;
2021-09-28 13:33:00 -04:00
}
bool USmartObjectSubsystem : : Release ( const FSmartObjectClaimHandle & ClaimHandle )
{
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( ClaimHandle . SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
if ( ! Slot )
2021-09-28 13:33:00 -04:00
{
return false ;
}
2023-02-02 18:43:13 -05:00
// Keep user data to be used as payload in the notification event
// since it will be released by the following call to Slot.Release
const FInstancedStruct Payload ( MoveTemp ( Slot - > UserData ) ) ;
2022-11-01 15:11:25 -04:00
const bool bSuccess = Slot - > Release ( ClaimHandle , /*bAborted*/ false ) ;
if ( bSuccess )
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Released using handle %s " ) , * LexToString ( ClaimHandle ) ) ;
UE_VLOG_LOCATION ( this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : White , TEXT ( " Released " ) ) ;
2022-11-01 15:11:25 -04:00
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , ClaimHandle . SlotHandle , ESmartObjectChangeReason : : OnReleased , Payload ) ;
2022-11-01 15:11:25 -04:00
}
}
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-11-01 15:11:25 -04:00
const FSmartObjectRuntimeSlot * Slot = RuntimeSlots . Find ( SlotHandle ) ;
return Slot ! = nullptr ? Slot - > GetState ( ) : ESmartObjectSlotState : : Invalid ;
2022-01-19 14:16:16 -05:00
}
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 ( ) ;
}
2022-03-18 12:31:49 -04:00
TOptional < FVector > USmartObjectSubsystem : : GetSlotLocation ( const FSmartObjectSlotHandle SlotHandle ) const
2021-09-28 13:33:00 -04:00
{
2022-03-18 12:31:49 -04:00
TOptional < FTransform > Transform = GetSlotTransform ( SlotHandle ) ;
return ( Transform . IsSet ( ) ? Transform . GetValue ( ) . GetLocation ( ) : TOptional < FVector > ( ) ) ;
}
bool USmartObjectSubsystem : : GetSlotTransform ( const FSmartObjectClaimHandle & ClaimHandle , FTransform & OutSlotTransform ) const
{
const TOptional < FTransform > OptionalTransform = GetSlotTransform ( ClaimHandle ) ;
OutSlotTransform = OptionalTransform . Get ( FTransform : : Identity ) ;
return OptionalTransform . IsSet ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-09-15 15:11:53 -04:00
bool USmartObjectSubsystem : : GetSlotTransformFromRequestResult ( const FSmartObjectRequestResult & RequestResult , FTransform & OutSlotTransform ) const
{
const TOptional < FTransform > OptionalTransform = GetSlotTransform ( RequestResult ) ;
OutSlotTransform = OptionalTransform . Get ( FTransform : : Identity ) ;
return OptionalTransform . IsSet ( ) ;
}
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-11-01 15:11:25 -04:00
if ( const FSmartObjectRuntimeSlot * Slot = GetSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
2021-09-28 13:33:00 -04:00
{
2022-08-12 07:56:27 -04:00
if ( ensureMsgf ( EntityManager , TEXT ( " Entity subsystem required to retrieve slot transform " ) ) )
2022-03-18 12:31:49 -04:00
{
2022-11-01 15:11:25 -04:00
const FSmartObjectSlotView View ( * EntityManager . Get ( ) , SlotHandle , Slot ) ;
2022-03-18 12:31:49 -04:00
const FSmartObjectSlotTransform & SlotTransform = View . GetStateData < FSmartObjectSlotTransform > ( ) ;
Transform = SlotTransform . GetTransform ( ) ;
}
2021-09-28 13:33:00 -04:00
}
return Transform ;
}
2022-09-01 09:03:19 -04:00
const FTransform & USmartObjectSubsystem : : GetSlotTransformChecked ( const FSmartObjectSlotHandle SlotHandle ) const
{
check ( EntityManager ) ;
2022-11-01 15:11:25 -04:00
const FSmartObjectSlotView View ( * EntityManager . Get ( ) , SlotHandle , nullptr ) ;
2022-09-01 09:03:19 -04:00
const FSmartObjectSlotTransform & SlotTransform = View . GetStateData < FSmartObjectSlotTransform > ( ) ;
return SlotTransform . GetTransform ( ) ;
}
2022-11-01 15:11:25 -04:00
FSmartObjectRuntime * USmartObjectSubsystem : : GetValidatedMutableRuntime ( const FSmartObjectHandle Handle , const TCHAR * Context ) const
2022-03-16 03:47:02 -04:00
{
return const_cast < FSmartObjectRuntime * > ( GetValidatedRuntime ( Handle , Context ) ) ;
}
2022-03-17 19:07:54 -04:00
const FSmartObjectRuntime * USmartObjectSubsystem : : GetValidatedRuntime ( const FSmartObjectHandle Handle , const TCHAR * Context ) const
2022-03-16 03:47:02 -04:00
{
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Handle ) ;
2022-03-18 12:31:49 -04:00
UE_CVLOG_UELOG ( ! Handle . IsValid ( ) , this , LogSmartObject , Log , TEXT ( " %s failed. Handle is not set. " ) , Context ) ;
UE_CVLOG_UELOG ( Handle . IsValid ( ) & & SmartObjectRuntime = = nullptr , this , LogSmartObject , Log ,
TEXT ( " %s failed using handle '%s'. SmartObject is no longer part of the simulation. " ) , Context , * LexToString ( Handle ) ) ;
2022-03-16 03:47:02 -04:00
return SmartObjectRuntime ;
}
2023-01-18 10:52:51 -05:00
FOnSmartObjectEvent * USmartObjectSubsystem : : GetEventDelegate ( const FSmartObjectHandle SmartObjectHandle )
{
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( SmartObjectHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
return & SmartObjectRuntime - > GetMutableEventDelegate ( ) ;
}
return nullptr ;
}
2022-03-16 03:47:02 -04:00
const FGameplayTagContainer & USmartObjectSubsystem : : GetInstanceTags ( const FSmartObjectHandle Handle ) const
{
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
return SmartObjectRuntime ! = nullptr ? SmartObjectRuntime - > GetTags ( ) : FGameplayTagContainer : : EmptyContainer ;
}
void USmartObjectSubsystem : : AddTagToInstance ( const FSmartObjectHandle Handle , const FGameplayTag & Tag )
{
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
AddTagToInstance ( * SmartObjectRuntime , Tag ) ;
}
}
void USmartObjectSubsystem : : RemoveTagFromInstance ( const FSmartObjectHandle Handle , const FGameplayTag & Tag )
{
if ( FSmartObjectRuntime * SmartObjectRuntime = GetValidatedMutableRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
RemoveTagFromInstance ( * SmartObjectRuntime , Tag ) ;
}
}
2022-11-01 15:11:25 -04:00
const FGameplayTagContainer & USmartObjectSubsystem : : GetSlotTags ( const FSmartObjectSlotHandle SlotHandle ) const
{
static const FGameplayTagContainer EmptyTags ;
if ( const FSmartObjectRuntimeSlot * Slot = GetSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
return Slot - > Tags ;
}
return EmptyTags ;
}
void USmartObjectSubsystem : : AddTagToSlot ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag & Tag )
{
if ( ! Tag . IsValid ( ) )
{
return ;
}
if ( FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
if ( ! Slot - > Tags . HasTag ( Tag ) )
{
Slot - > Tags . AddTagFast ( Tag ) ;
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagAdded , Slot - > GetUserData ( ) , Tag ) ;
2022-11-01 15:11:25 -04:00
}
}
}
}
bool USmartObjectSubsystem : : RemoveTagFromSlot ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag & Tag )
{
if ( ! Tag . IsValid ( ) )
{
return false ;
}
if ( FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
if ( Slot - > Tags . RemoveTag ( Tag ) )
{
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagRemoved , Slot - > GetUserData ( ) , Tag ) ;
2022-11-01 15:11:25 -04:00
}
return true ;
}
}
return false ;
}
2022-11-04 09:18:52 -04:00
bool USmartObjectSubsystem : : SetSlotEnabled ( const FSmartObjectSlotHandle SlotHandle , const bool bEnabled )
2022-11-01 15:11:25 -04:00
{
2022-11-04 09:18:52 -04:00
bool bPreviousValue = false ;
2022-11-01 15:11:25 -04:00
if ( FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
2023-01-18 10:52:51 -05:00
// Using 'IsEnabled' that combines both slot and smart object enabled state
bPreviousValue = Slot - > IsEnabled ( ) ;
// Always set slot enabled state even if combined result might not be affected
Slot - > bSlotEnabled = bEnabled ;
// Using new combined value to detect changes
if ( Slot - > IsEnabled ( ) ! = bPreviousValue )
2022-11-01 15:11:25 -04:00
{
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
2023-02-02 18:43:13 -05:00
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle ,
Slot - > IsEnabled ( ) ? ESmartObjectChangeReason : : OnSlotEnabled : ESmartObjectChangeReason : : OnSlotDisabled , Slot - > UserData ) ;
2022-11-01 15:11:25 -04:00
}
}
}
2022-11-04 09:18:52 -04:00
return bPreviousValue ;
2022-11-01 15:11:25 -04:00
}
bool USmartObjectSubsystem : : SendSlotEvent ( const FSmartObjectSlotHandle SlotHandle , const FGameplayTag EventTag , const FConstStructView Payload )
{
if ( const FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
2023-02-02 18:43:13 -05:00
// Runtime slot lifetime is bound to the runtime smart object so it should always be available.
const FSmartObjectRuntime & ObjectRuntime = RuntimeSmartObjects . FindChecked ( Slot - > GetOwnerRuntimeObject ( ) ) ;
if ( ObjectRuntime . GetEventDelegate ( ) . IsBound ( ) )
2022-11-01 15:11:25 -04:00
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = Slot - > GetOwnerRuntimeObject ( ) ;
Data . SlotHandle = SlotHandle ;
Data . Reason = ESmartObjectChangeReason : : OnEvent ;
Data . Tag = EventTag ;
Data . EventPayload = Payload ;
2023-02-02 18:43:13 -05:00
ObjectRuntime . GetEventDelegate ( ) . Broadcast ( Data ) ;
2022-11-01 15:11:25 -04:00
return true ;
}
}
return false ;
}
2022-03-16 03:47:02 -04:00
void USmartObjectSubsystem : : AddTagToInstance ( FSmartObjectRuntime & SmartObjectRuntime , const FGameplayTag & Tag )
{
if ( ! SmartObjectRuntime . Tags . HasTag ( Tag ) )
{
SmartObjectRuntime . Tags . AddTagFast ( Tag ) ;
2023-01-18 10:52:51 -05:00
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnTagAdded ;
Data . Tag = Tag ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
2022-03-16 03:47:02 -04:00
}
}
void USmartObjectSubsystem : : RemoveTagFromInstance ( FSmartObjectRuntime & SmartObjectRuntime , const FGameplayTag & Tag )
{
if ( SmartObjectRuntime . Tags . RemoveTag ( Tag ) )
{
2023-01-18 10:52:51 -05:00
FSmartObjectEventData Data ;
Data . SmartObjectHandle = SmartObjectRuntime . GetRegisteredHandle ( ) ;
Data . Reason = ESmartObjectChangeReason : : OnTagRemoved ;
Data . Tag = Tag ;
SmartObjectRuntime . OnEvent . Broadcast ( Data ) ;
2022-03-16 03:47:02 -04:00
}
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : OnSlotChanged (
const FSmartObjectRuntime & SmartObjectRuntime ,
const FSmartObjectRuntimeSlot & Slot ,
const FSmartObjectSlotHandle SlotHandle ,
const ESmartObjectChangeReason Reason ,
const FConstStructView Payload ,
const FGameplayTag ChangedTag
)
2021-09-28 13:33:00 -04:00
{
2023-02-02 18:43:13 -05:00
if ( SmartObjectRuntime . GetEventDelegate ( ) . IsBound ( ) )
2022-11-01 15:11:25 -04:00
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = Slot . GetOwnerRuntimeObject ( ) ;
Data . SlotHandle = SlotHandle ;
Data . Reason = Reason ;
Data . Tag = ChangedTag ;
2023-02-02 18:43:13 -05:00
Data . EventPayload = Payload ;
SmartObjectRuntime . GetEventDelegate ( ) . Broadcast ( Data ) ;
2022-11-01 15:11:25 -04:00
}
}
FSmartObjectRuntimeSlot * USmartObjectSubsystem : : GetMutableSlot ( const FSmartObjectClaimHandle & ClaimHandle )
{
return RuntimeSlots . Find ( ClaimHandle . SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : RegisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle , const FOnSlotInvalidated & Callback )
{
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot * Slot = GetMutableSlot ( ClaimHandle ) ;
2021-09-28 13:33:00 -04:00
if ( Slot ! = nullptr )
{
Slot - > OnSlotInvalidatedDelegate = Callback ;
}
}
void USmartObjectSubsystem : : UnregisterSlotInvalidationCallback ( const FSmartObjectClaimHandle & ClaimHandle )
{
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot * Slot = GetMutableSlot ( ClaimHandle ) ;
2021-09-28 13:33:00 -04:00
if ( Slot ! = nullptr )
{
Slot - > OnSlotInvalidatedDelegate . Unbind ( ) ;
}
}
2022-11-01 15:11:25 -04:00
FOnSmartObjectEvent * USmartObjectSubsystem : : GetSlotEventDelegate ( const FSmartObjectSlotHandle SlotHandle )
{
2023-02-02 18:43:13 -05:00
if ( const FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
2022-11-01 15:11:25 -04:00
{
2023-02-02 18:43:13 -05:00
if ( FSmartObjectRuntime * ObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
return & ObjectRuntime - > GetMutableEventDelegate ( ) ;
}
2022-11-01 15:11:25 -04:00
}
return nullptr ;
}
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
void USmartObjectSubsystem : : DebugDraw ( FDebugRenderSceneProxy * DebugProxy ) const
{
2022-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized )
2022-02-21 01:10:34 -05:00
{
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-03-18 12:31:49 -04:00
if ( IsSlotValidVerbose ( ClaimHandle . SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
2022-01-19 14:16:16 -05:00
{
2022-08-12 07:56:27 -04:00
if ( ensureMsgf ( EntityManager , TEXT ( " Entity subsystem required to add slot data " ) ) & &
2023-02-02 18:43:13 -05:00
ensureMsgf ( InData . IsValid ( ) & & InData . GetScriptStruct ( ) - > IsChildOf ( FSmartObjectSlotStateData : : StaticStruct ( ) ) ,
TEXT ( " Given struct is not valid or doesn't represent a valid runtime data type. Make sure to inherit from FSmartObjectSlotState or one of its child-types. " ) ) )
2022-03-18 12:31:49 -04:00
{
2022-08-12 07:56:27 -04:00
EntityManager - > Defer ( ) . PushCommand < FMassDeferredAddCommand > (
[ EntityHandle = FMassEntityHandle ( ClaimHandle . SlotHandle ) , DataView = InData ] ( FMassEntityManager & System )
2022-03-08 10:30:07 -05:00
{
2023-01-12 09:01:21 -05:00
FInstancedStruct Struct ( DataView ) ;
2022-03-08 10:30:07 -05:00
System . AddFragmentInstanceListToEntity ( EntityHandle , MakeArrayView ( & Struct , 1 ) ) ;
} ) ;
2022-11-01 15:11:25 -04:00
// @todo: This is temporary solution to make the added data immediately accessible.
ensureMsgf ( ! EntityManager - > IsProcessing ( ) , TEXT ( " EntityManager is processing, which prevents immediate data change. " ) ) ;
EntityManager - > FlushCommands ( ) ;
2022-03-18 12:31:49 -04:00
}
2022-01-19 14:16:16 -05:00
}
}
2022-03-17 19:07:54 -04:00
FSmartObjectSlotView USmartObjectSubsystem : : GetSlotView ( const FSmartObjectSlotHandle SlotHandle ) const
2022-01-19 14:16:16 -05:00
{
2022-11-01 15:11:25 -04:00
if ( const FSmartObjectRuntimeSlot * Slot = GetSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
2022-01-19 14:16:16 -05:00
{
2022-08-12 07:56:27 -04:00
if ( ensureMsgf ( EntityManager , TEXT ( " Entity subsystem required to create slot view " ) ) )
2022-01-19 14:16:16 -05:00
{
2022-11-01 15:11:25 -04:00
return FSmartObjectSlotView ( * EntityManager . Get ( ) , SlotHandle , Slot ) ;
2022-01-19 14:16:16 -05:00
}
}
return FSmartObjectSlotView ( ) ;
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutSlots , const FConstStructView UserData ) const
2022-01-19 14:16:16 -05:00
{
2022-03-16 03:47:02 -04:00
if ( const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
2022-03-02 13:25:37 -05:00
{
2023-02-02 18:43:13 -05:00
FindSlots ( * SmartObjectRuntime , Filter , OutSlots , UserData ) ;
2022-03-02 13:25:37 -05:00
}
2022-01-31 18:52:49 -05:00
}
2022-11-01 15:11:25 -04:00
void USmartObjectSubsystem : : GetAllSlots ( const FSmartObjectHandle Handle , TArray < FSmartObjectSlotHandle > & OutSlots ) const
{
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FilterSlots " ) ;
OutSlots . Reset ( ) ;
if ( const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( Handle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
OutSlots = SmartObjectRuntime - > SlotHandles ;
}
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : EvaluateConditionsForFiltering (
const FSmartObjectSlotHandle SlotHandle ,
FWorldConditionContextData & ContextData ,
const FConstStructView UserData ,
TPair < const FSmartObjectRuntime * , bool > & LastEvaluatedRuntime
) const
{
const FSmartObjectRuntimeSlot & RuntimeSlot = RuntimeSlots . FindChecked ( SlotHandle ) ;
const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( RuntimeSlot . GetOwnerRuntimeObject ( ) ) ;
if ( SmartObjectRuntime = = nullptr )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " Can't evaluate conditions for slot handle %s since its owning smart object instance can't be found. " ) , * LexToString ( SlotHandle ) ) ;
return false ;
}
// Evaluate preconditions on the parent object only once if all slots have the same one (usual case)
if ( SmartObjectRuntime ! = LastEvaluatedRuntime . Key )
{
LastEvaluatedRuntime . Key = SmartObjectRuntime ;
// Set context schema and bind user data only if not set or changed
const UWorldConditionSchema * PrevSchema = ContextData . GetSchema ( ) ;
if ( PrevSchema = = nullptr | | PrevSchema ! = SmartObjectRuntime - > GetDefinition ( ) . GetWorldConditionSchema ( ) )
{
ContextData . SetSchema ( * SmartObjectRuntime - > GetDefinition ( ) . GetWorldConditionSchema ( ) ) ;
// Setup some context data using user data
BindPropertiesFromStruct ( ContextData , UserData ) ;
}
// Setup system related data (object runtime, slot, subsystem, etc.)
SetupConditionContextCommonData ( ContextData , * SmartObjectRuntime ) ;
// Evaluate object conditions.
LastEvaluatedRuntime . Value = EvaluateObjectConditions ( ContextData , * SmartObjectRuntime ) ;
}
// Evaluate slot conditions only if parent runtime passed its own selection conditions
return LastEvaluatedRuntime . Value ? EvaluateSlotConditions ( ContextData , SlotHandle , RuntimeSlot ) : false ;
}
TArray < FSmartObjectSlotHandle > USmartObjectSubsystem : : FilterSlotsBySelectionConditions (
const TConstArrayView < FSmartObjectSlotHandle > & SlotsToFilter ,
const FConstStructView UserData
) const
{
TArray < FSmartObjectSlotHandle > Result ;
Result . Reserve ( SlotsToFilter . Num ( ) ) ;
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
for ( const FSmartObjectSlotHandle SlotHandle : SlotsToFilter )
{
if ( EvaluateConditionsForFiltering ( SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) )
{
Result . Add ( SlotHandle ) ;
}
}
Result . Shrink ( ) ;
return MoveTemp ( Result ) ;
}
TArray < FSmartObjectRequestResult > USmartObjectSubsystem : : FilterResultsBySelectionConditions (
const TConstArrayView < FSmartObjectRequestResult > & ResultsToFilter ,
const FConstStructView UserData
) const
{
TArray < FSmartObjectRequestResult > Result ;
Result . Reserve ( ResultsToFilter . Num ( ) ) ;
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
for ( const FSmartObjectRequestResult RequestResult : ResultsToFilter )
{
if ( EvaluateConditionsForFiltering ( RequestResult . SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) )
{
Result . Add ( RequestResult ) ;
}
}
Result . Shrink ( ) ;
return MoveTemp ( Result ) ;
}
bool USmartObjectSubsystem : : EvaluateSelectionConditions ( const FSmartObjectSlotHandle SlotHandle , const FConstStructView UserData ) const
{
FWorldConditionContextData ContextData ;
TPair < const FSmartObjectRuntime * , bool > LastEvaluatedSmartObjectRuntime = { nullptr , false } ;
return EvaluateConditionsForFiltering ( SlotHandle , ContextData , UserData , LastEvaluatedSmartObjectRuntime ) ;
}
2023-03-14 06:26:29 -04:00
bool USmartObjectSubsystem : : FindNavigationLocationForSlot ( const FSmartObjectSlotHandle SlotHandle , const FSmartObjectSlotEntranceLocationRequest & Request , FSmartObjectSlotNavigationLocationResult & Result ) const
2023-03-02 05:58:30 -05:00
{
Result = { } ;
2023-03-14 06:26:29 -04:00
UWorld * World = GetWorld ( ) ;
const USmartObjectSlotValidationFilter * ValidationFilter = Request . ValidationFilter ? Request . ValidationFilter . GetDefaultObject ( ) : GetDefault < USmartObjectSlotValidationFilter > ( ) ;
2023-03-02 05:58:30 -05:00
// Navdata must be valid when checking testing for navigable.
2023-03-14 06:26:29 -04:00
if ( Request . bProjectNavigationLocation & & ! Request . NavigationData )
2023-03-02 05:58:30 -05:00
{
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " Can't find entry location for slot handle %s since request's NavigationData is not set. " ) , * LexToString ( SlotHandle ) ) ;
return false ;
}
// Slot view must be valid.
const FSmartObjectSlotView SlotView = GetSlotView ( SlotHandle ) ;
if ( ! SlotView . IsValid ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " Can't find entry location for slot handle %s since its owning smart object instance can't be found. " ) , * LexToString ( SlotHandle ) ) ;
return false ;
}
struct FSlotEntryCandidate
{
FVector Location ;
FRotator Rotation ;
NavNodeRef NodeRef ;
FVector : : FReal DistanceSqr = 0.0 ;
2023-03-14 06:26:29 -04:00
const FSmartObjectSlotEntranceAnnotation * EntranceAnnotation = nullptr ;
bool bTraceGroundLocation = false ;
bool bCheckTransitionTrajectory = false ;
2023-03-02 05:58:30 -05:00
} ;
TArray < FSlotEntryCandidate , TInlineAllocator < 8 > > Candidates ;
const FTransform & SlotTransform = SlotView . GetStateData < FSmartObjectSlotTransform > ( ) . GetTransform ( ) ;
2023-03-14 06:26:29 -04:00
const bool bIncludeEntries = Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Entry ;
const bool bIncludeExits = Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Exit ;
2023-03-02 05:58:30 -05:00
const FSmartObjectSlotDefinition & Definition = SlotView . GetDefinition ( ) ;
for ( const FInstancedStruct & Data : Definition . Data )
{
2023-03-14 06:26:29 -04:00
if ( const FSmartObjectSlotEntranceAnnotation * EntranceAnnotation = Data . GetPtr < FSmartObjectSlotEntranceAnnotation > ( ) )
2023-03-02 05:58:30 -05:00
{
2023-03-14 06:26:29 -04:00
if ( EntranceAnnotation - > bIsEntry = = bIncludeEntries
| | EntranceAnnotation - > bIsExit = = bIncludeExits )
2023-03-02 05:58:30 -05:00
{
2023-03-14 06:26:29 -04:00
TOptional < FTransform > EntryTransform = EntranceAnnotation - > GetWorldTransform ( SlotTransform ) ;
if ( EntryTransform . IsSet ( ) )
{
FSlotEntryCandidate & Candidate = Candidates . AddDefaulted_GetRef ( ) ;
Candidate . Location = EntryTransform - > GetLocation ( ) ;
Candidate . Rotation = EntryTransform - > GetRotation ( ) . Rotator ( ) ;
Candidate . EntranceAnnotation = EntranceAnnotation ;
Candidate . bTraceGroundLocation = EntranceAnnotation - > bTraceGroundLocation ;
Candidate . bCheckTransitionTrajectory = EntranceAnnotation - > bCheckTransitionTrajectory ;
}
2023-03-02 05:58:30 -05:00
}
}
}
if ( Candidates . IsEmpty ( )
& & Request . bUseSlotLocationAsFallback )
{
FSlotEntryCandidate & Candidate = Candidates . AddDefaulted_GetRef ( ) ;
Candidate . Location = SlotTransform . GetLocation ( ) ;
Candidate . Rotation = SlotTransform . GetRotation ( ) . Rotator ( ) ;
}
if ( Candidates . IsEmpty ( ) )
{
return false ;
}
2023-03-14 06:26:29 -04:00
// Sort candidates so that the best candidate is first.
2023-03-02 05:58:30 -05:00
if ( Request . SelectMethod = = FSmartObjectSlotEntrySelectionMethod : : NearestToSearchLocation )
{
for ( FSlotEntryCandidate & Candidate : Candidates )
{
Candidate . DistanceSqr = FVector : : DistSquared ( Request . SearchLocation , Candidate . Location ) ;
}
Candidates . Sort ( [ ] ( const FSlotEntryCandidate & A , const FSlotEntryCandidate & B )
{
return A . DistanceSqr < B . DistanceSqr ;
} ) ;
}
2023-03-14 06:26:29 -04:00
2023-03-02 05:58:30 -05:00
check ( Candidates . Num ( ) > 0 ) ;
2023-03-14 06:26:29 -04:00
const FSmartObjectTraceParams & GroundTraceParameters = ValidationFilter - > GetGroundTraceParameters ( ) ;
const FSmartObjectTraceParams & TransitionTraceParameters = ValidationFilter - > GetTransitionTraceParameters ( ) ;
const FVector SearchExtents = FVector ( ValidationFilter - > GetSearchExtents ( ) ) ;
FCollisionQueryParams GroundTraceQueryParams ( SCENE_QUERY_STAT ( SmartObjectTrace ) , GroundTraceParameters . bTraceComplex ) ;
FCollisionQueryParams TransitionTraceQueryParams ( SCENE_QUERY_STAT ( SmartObjectTrace ) , TransitionTraceParameters . bTraceComplex ) ;
const AActor * SmartObjectActor = nullptr ;
if ( const FSmartObjectRuntime * Runtime = GetRuntimeInstance ( SlotView . GetOwnerRuntimeObject ( ) ) )
2023-03-02 05:58:30 -05:00
{
2023-03-14 06:26:29 -04:00
SmartObjectActor = Runtime - > GetOwnerActor ( ) ;
}
if ( SmartObjectActor )
{
GroundTraceQueryParams . AddIgnoredActor ( SmartObjectActor ) ;
TransitionTraceQueryParams . AddIgnoredActor ( SmartObjectActor ) ;
2023-03-02 05:58:30 -05:00
}
2023-03-14 06:26:29 -04:00
bool bHasResult = false ;
2023-03-02 05:58:30 -05:00
2023-03-14 06:26:29 -04:00
// Candidates are now in order of preference. Test each for hard requirements and pick the first that passes all checks.
for ( FSlotEntryCandidate & Candidate : Candidates )
{
const FBox SearchBounds ( Candidate . Location - SearchExtents , Candidate . Location + SearchExtents ) ;
// Check and adjust the location on navigable space.
if ( Request . bProjectNavigationLocation )
{
FNavLocation NavLocation ;
if ( ! UE : : SmartObject : : Annotations : : ProjectNavigationLocation ( * Request . NavigationData , Candidate . Location , SearchBounds , Request . NavigationFilter , Request . InstigatorActor , NavLocation ) )
{
// If no navigable area found, skip the candidate.
continue ;
}
Candidate . Location = NavLocation . Location ;
Candidate . NodeRef = NavLocation . NodeRef ;
}
// Check and adjust the location on ground.
if ( Request . bTraceGroundLocation
& & Candidate . bTraceGroundLocation )
{
FVector GroundLocation ;
if ( ! UE : : SmartObject : : Annotations : : TraceGroundLocation ( * World , Candidate . Location , SearchBounds , GroundTraceParameters , GroundTraceQueryParams , GroundLocation ) )
{
// If not ground location found, skip the candidate.
continue ;
}
Candidate . Location = GroundLocation ;
}
// Check that there's no collision during transition to slot location.
if ( Request . bCheckTransitionTrajectory
& & Candidate . bCheckTransitionTrajectory
& & Candidate . EntranceAnnotation )
{
// @todo: we're currently not using the adjusted location (Candidate.Location), consider if we should.
TArray < FSmartObjectAnnotationCollider > Colliders ;
Candidate . EntranceAnnotation - > GetTrajectoryColliders ( SlotTransform , Colliders ) ;
if ( UE : : SmartObject : : Annotations : : TestCollidersOverlap ( * World , Colliders , TransitionTraceParameters , TransitionTraceQueryParams ) )
{
// If the colliders overlap, skip the candidate.
continue ;
}
}
// Passed all validation, use it as the result.
Result . Location = Candidate . Location ;
Result . Rotation = Candidate . Rotation ;
Result . NodeRef = INVALID_NAVNODEREF ;
if ( Candidate . EntranceAnnotation )
{
Result . Tag = Candidate . EntranceAnnotation - > Tag ;
}
if ( Request . LocationType = = ESmartObjectSlotNavigationLocationType : : Exit )
{
// Reverse direction for exits.
Result . Rotation = Result . Rotation . Add ( 0.0 , 180.0 , 0.0 ) . Clamp ( ) ;
}
bHasResult = true ;
break ;
}
return bHasResult ;
2023-03-02 05:58:30 -05:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutResults , const FConstStructView UserData ) const
2022-01-31 18:52:49 -05:00
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FilterSlots " ) ;
2022-03-16 03:47:02 -04:00
// Use the high level flag, no need to dig into each slot state since they are also all disabled.
2022-11-01 15:11:25 -04:00
if ( ! SmartObjectRuntime . IsEnabled ( ) )
2022-03-16 03:47:02 -04:00
{
return ;
}
2021-11-26 15:48:13 -05:00
const USmartObjectDefinition & Definition = SmartObjectRuntime . GetDefinition ( ) ;
2022-03-02 13:25:37 -05:00
const int32 NumSlots = Definition . GetSlots ( ) . Num ( ) ;
checkf ( NumSlots > 0 , TEXT ( " Definition should contain slot definitions at this point " ) ) ;
checkf ( SmartObjectRuntime . SlotHandles . Num ( ) = = NumSlots , TEXT ( " Number of runtime slot handles should match number of slot definitions " ) ) ;
// Applying caller's predicate
if ( Filter . Predicate & & ! Filter . Predicate ( SmartObjectRuntime . GetRegisteredHandle ( ) ) )
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-03-02 13:25:37 -05:00
// Apply definition level filtering (Tags and BehaviorDefinition)
// This could be improved to cache results between a single query against multiple instances of the same definition
TArray < int32 > ValidSlotIndices ;
FindMatchingSlotDefinitionIndices ( Definition , Filter , ValidSlotIndices ) ;
2021-09-28 13:33:00 -04:00
2023-02-02 18:43:13 -05:00
FWorldConditionContextData ConditionContextData ;
ConditionContextData . SetSchema ( * Definition . GetWorldConditionSchema ( ) ) ;
2022-11-17 07:44:24 -05:00
2023-01-18 10:52:51 -05:00
// Setup default data
SetupConditionContextCommonData ( ConditionContextData , SmartObjectRuntime ) ;
2023-02-02 18:43:13 -05:00
2023-01-18 10:52:51 -05:00
// Setup additional data related to requester
2023-02-02 18:43:13 -05:00
BindPropertiesFromStruct ( ConditionContextData , UserData ) ;
2023-01-18 10:52:51 -05:00
// Check object conditions.
if ( ! EvaluateObjectConditions ( ConditionContextData , SmartObjectRuntime ) )
{
return ;
}
2022-11-17 07:44:24 -05:00
2022-11-04 11:28:57 -04:00
// Build list of available slot indices (filter out occupied or reserved slots or disabled slots)
2022-03-02 13:25:37 -05:00
for ( const int32 SlotIndex : ValidSlotIndices )
2021-09-28 13:33:00 -04:00
{
2022-11-17 07:44:24 -05:00
const FSmartObjectRuntimeSlot & RuntimeSlot = RuntimeSlots . FindChecked ( SmartObjectRuntime . SlotHandles [ SlotIndex ] ) ;
2023-01-18 10:52:51 -05:00
if ( ! RuntimeSlot . CanBeClaimed ( ) )
2021-09-28 13:33:00 -04:00
{
2022-11-17 07:44:24 -05:00
continue ;
2021-09-28 13:33:00 -04:00
}
2022-11-17 07:44:24 -05:00
const FSmartObjectSlotHandle SlotHandle = SmartObjectRuntime . SlotHandles [ SlotIndex ] ;
2023-01-18 10:52:51 -05:00
// Check slot conditions.
if ( ! EvaluateSlotConditions ( ConditionContextData , SlotHandle , RuntimeSlot ) )
2022-11-17 07:44:24 -05:00
{
2023-01-18 10:52:51 -05:00
continue ;
2023-01-18 01:06:45 -05:00
}
2023-01-18 10:52:51 -05:00
OutResults . Add ( SlotHandle ) ;
2021-09-28 13:33:00 -04:00
}
}
2022-03-02 13:25:37 -05:00
void USmartObjectSubsystem : : FindMatchingSlotDefinitionIndices ( const USmartObjectDefinition & Definition , const FSmartObjectRequestFilter & Filter , TArray < int32 > & OutValidIndices )
2021-09-28 13:33:00 -04:00
{
2022-03-02 22:33:53 -05:00
const ESmartObjectTagFilteringPolicy UserTagsFilteringPolicy = Definition . GetUserTagsFilteringPolicy ( ) ;
2022-03-02 13:25:37 -05:00
// Define our Tags filtering predicate
auto MatchesTagQueryFunc = [ ] ( const FGameplayTagQuery & Query , const FGameplayTagContainer & Tags ) { return Query . IsEmpty ( ) | | Query . Matches ( Tags ) ; } ;
// When filter policy is to use combined we can validate the user tag query of the parent object first
// since they can't be merge so we need to apply them one after the other.
// For activity requirements we have to merge parent and slot tags together before testing.
2022-03-02 22:33:53 -05:00
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Combine
2022-03-02 13:25:37 -05:00
& & ! MatchesTagQueryFunc ( Definition . GetUserTagFilter ( ) , Filter . UserTags ) )
2021-09-28 13:33:00 -04:00
{
2022-03-02 13:25:37 -05:00
return ;
}
// Apply filter to individual slots
const TConstArrayView < FSmartObjectSlotDefinition > SlotDefinitions = Definition . GetSlots ( ) ;
OutValidIndices . Reserve ( SlotDefinitions . Num ( ) ) ;
for ( int i = 0 ; i < SlotDefinitions . Num ( ) ; + + i )
{
const FSmartObjectSlotDefinition & Slot = SlotDefinitions [ i ] ;
2022-09-08 19:27:10 -04:00
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// (Deprecated property handling) Filter out mismatching behavior type (if specified)
2022-09-08 19:36:03 -04:00
if ( Filter . BehaviorDefinitionClass ! = nullptr
& & Definition . GetBehaviorDefinition ( FSmartObjectSlotIndex ( i ) , Filter . BehaviorDefinitionClass ) = = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-02 13:25:37 -05:00
continue ;
2021-09-28 13:33:00 -04:00
}
2022-09-08 19:27:10 -04:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// Filter out mismatching behavior type (if specified)
if ( ! Filter . BehaviorDefinitionClasses . IsEmpty ( ) )
{
bool bMatchesAny = false ;
for ( const TSubclassOf < USmartObjectBehaviorDefinition > & BehaviorDefinitionClass : Filter . BehaviorDefinitionClasses )
{
if ( Definition . GetBehaviorDefinition ( FSmartObjectSlotIndex ( i ) , BehaviorDefinitionClass ) ! = nullptr )
{
bMatchesAny = true ;
break ;
}
}
if ( ! bMatchesAny )
{
continue ;
}
}
2022-03-02 13:25:37 -05:00
2022-03-02 22:33:53 -05:00
// Filter out slots based on their activity tags
FGameplayTagContainer ActivityTags ;
Definition . GetSlotActivityTags ( Slot , ActivityTags ) ;
if ( ! MatchesTagQueryFunc ( Filter . ActivityRequirements , ActivityTags ) )
2022-03-02 13:25:37 -05:00
{
2022-03-02 22:33:53 -05:00
continue ;
2022-03-02 13:25:37 -05:00
}
2022-03-02 22:33:53 -05:00
// Filter out slots based on their TagQuery applied on provided User Tags
// - override: we only run query from the slot if provided otherwise we run the one from the parent object
// - combine: we run slot query (parent query was applied before processing individual slots)
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Combine
& & ! MatchesTagQueryFunc ( Slot . UserTagFilter , Filter . UserTags ) )
{
continue ;
}
if ( UserTagsFilteringPolicy = = ESmartObjectTagFilteringPolicy : : Override
& & ! MatchesTagQueryFunc ( ( Slot . UserTagFilter . IsEmpty ( ) ? Definition . GetUserTagFilter ( ) : Slot . UserTagFilter ) , Filter . UserTags ) )
{
continue ;
2022-03-02 13:25:37 -05:00
}
OutValidIndices . Add ( i ) ;
2021-09-28 13:33:00 -04:00
}
}
2023-02-02 18:43:13 -05:00
FSmartObjectRequestResult USmartObjectSubsystem : : FindSmartObject ( const FSmartObjectRequest & Request , const FConstStructView UserData ) const
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectRequestResult > Results ;
2023-02-02 18:43:13 -05:00
FindSmartObjects ( Request , Results , UserData ) ;
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
}
2023-02-02 18:43:13 -05:00
bool USmartObjectSubsystem : : FindSmartObjects ( const FSmartObjectRequest & Request , TArray < FSmartObjectRequestResult > & OutResults , const FConstStructView UserData ) 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-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized )
2022-02-21 01:10:34 -05:00
{
2022-04-04 13:22:01 -04:00
// Do not report warning if runtime was explicitly disabled by CVar
UE_CVLOG_UELOG ( ! UE : : SmartObject : : bDisableRuntime , this , LogSmartObject , Warning ,
TEXT ( " Can't find smart objet before runtime gets initialized (i.e. InitializeRuntime gets called). " ) ) ;
2022-02-21 01:10:34 -05:00
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 ) ;
2022-03-16 03:47:02 -04:00
checkf ( SmartObjectRuntime ! = nullptr , TEXT ( " Results returned by the space partition are expected to be valid. " ) ) ;
if ( ! Request . QueryBox . IsInside ( SmartObjectRuntime - > GetTransform ( ) . GetLocation ( ) ) )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
continue ;
}
2022-03-18 12:31:49 -04:00
TArray < FSmartObjectSlotHandle > SlotHandles ;
2023-02-02 18:43:13 -05:00
FindSlots ( * SmartObjectRuntime , Filter , SlotHandles , UserData ) ;
2022-02-21 01:10:34 -05:00
OutResults . Reserve ( OutResults . Num ( ) + SlotHandles . Num ( ) ) ;
2022-03-18 12:31:49 -04:00
for ( FSmartObjectSlotHandle SlotHandle : SlotHandles )
{
2022-02-21 01:10:34 -05:00
OutResults . Emplace ( SmartObjectHandle , SlotHandle ) ;
2022-03-18 12:31:49 -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
}
void USmartObjectSubsystem : : RegisterCollectionInstances ( )
{
2022-11-24 14:53:52 -05:00
for ( TActorIterator < ASmartObjectPersistentCollection > It ( GetWorld ( ) ) ; It ; + + It )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
ASmartObjectPersistentCollection * Collection = ( * It ) ;
2021-09-28 13:33:00 -04:00
if ( IsValid ( Collection ) & & Collection - > IsRegistered ( ) = = false )
{
2022-01-12 16:15:32 -05:00
const ESmartObjectCollectionRegistrationResult Result = RegisterCollection ( * Collection ) ;
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( Collection , LogSmartObject , Log ,
TEXT ( " Collection '%s' registration from USmartObjectSubsystem initialization - %s " ) , * Collection - > GetFullName ( ) , * UEnum : : GetValueAsString ( Result ) ) ;
2021-09-28 13:33:00 -04:00
}
}
}
2022-11-24 14:53:52 -05:00
ESmartObjectCollectionRegistrationResult USmartObjectSubsystem : : RegisterCollection ( ASmartObjectPersistentCollection & 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 ( ) )
{
2022-11-23 09:11:13 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Error , TEXT ( " Trying to register collection '%s' more than once " ) , * InCollection . GetFullName ( ) ) ;
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
2022-11-24 14:53:52 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Log , TEXT ( " Adding collection '%s' registered with %d entries " ) , * InCollection . GetName ( ) , InCollection . GetEntries ( ) . Num ( ) ) ;
InCollection . GetMutableSmartObjectContainer ( ) . ValidateDefinitions ( ) ;
SmartObjectContainer . Append ( InCollection . GetSmartObjectContainer ( ) ) ;
RegisteredCollections . Add ( & InCollection ) ;
2022-12-05 08:19:29 -05:00
// We want to add the new collection to the "simulation" only if the Runtime part of the subsystem has been initialized.
// SmartObjectContainer is added to simulation in one go in InitializeRuntime.
if ( bRuntimeInitialized & & EntityManager )
2022-11-24 14:53:52 -05:00
{
AddContainerToSimulation ( InCollection . GetSmartObjectContainer ( ) ) ;
}
2022-10-10 12:00:30 -04:00
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2022-11-24 14:53:52 -05:00
// Broadcast after rebuilding so listeners will be able to access up-to-date data
OnMainCollectionChanged . Broadcast ( ) ;
2021-09-28 13:33:00 -04:00
# endif // WITH_EDITOR
2021-11-02 11:12:43 -04:00
2022-11-24 14:53:52 -05:00
InCollection . OnRegistered ( ) ;
Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2022-01-12 16:15:32 -05:00
return Result ;
2021-09-28 13:33:00 -04:00
}
2022-11-24 14:53:52 -05:00
void USmartObjectSubsystem : : UnregisterCollection ( ASmartObjectPersistentCollection & InCollection )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
if ( RegisteredCollections . Remove ( & InCollection ) )
{
SmartObjectContainer . Remove ( InCollection . GetSmartObjectContainer ( ) ) ;
if ( ensure ( EntityManager ) )
{
for ( const FSmartObjectCollectionEntry & Entry : InCollection . GetSmartObjectContainer ( ) . GetEntries ( ) )
{
2022-12-05 08:19:29 -05:00
FSmartObjectRuntime SORuntime ;
// even though we did add this entry to RuntimeSmartObjects at some point it could have been removed
// when the smart object in question got disabled or removed
if ( RuntimeSmartObjects . RemoveAndCopyValue ( Entry . GetHandle ( ) , SORuntime ) )
2022-11-24 14:53:52 -05:00
{
2022-12-05 08:19:29 -05:00
if ( USmartObjectComponent * SOComponent = Entry . GetComponent ( ) )
{
UnbindComponentFromSimulationInternal ( * SOComponent , SORuntime ) ;
}
DestroyRuntimeInstanceInternal ( Entry . GetHandle ( ) , SORuntime , * EntityManager . Get ( ) ) ;
2022-11-24 14:53:52 -05:00
}
}
}
InCollection . OnUnregistered ( ) ;
}
else
2021-09-28 13:33:00 -04:00
{
2023-02-02 18:43:13 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Verbose ,
TEXT ( " Ignoring unregistration of collection '%s' since this is not one of the previously registered collections. " ) , * InCollection . GetFullName ( ) ) ;
2021-09-28 13:33:00 -04:00
return ;
}
2022-11-24 14:53:52 -05:00
}
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
void USmartObjectSubsystem : : AddContainerToSimulation ( const FSmartObjectContainer & InSmartObjectContainer )
{
2022-12-05 08:19:29 -05:00
if ( ! ensureMsgf ( bRuntimeInitialized , TEXT ( " %s called before InitializeRuntime, this is not expected to happen. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
return ;
}
2022-11-24 14:53:52 -05:00
for ( const FSmartObjectCollectionEntry & Entry : InSmartObjectContainer . GetEntries ( ) )
2022-03-16 03:47:02 -04:00
{
2022-11-24 14:53:52 -05:00
const USmartObjectDefinition * Definition = InSmartObjectContainer . GetDefinitionForEntry ( Entry ) ;
USmartObjectComponent * Component = Entry . GetComponent ( ) ;
if ( Definition = = nullptr | | Definition - > IsValid ( ) = = false )
{
UE_CVLOG_UELOG ( Component ! = nullptr , Component - > GetOwner ( ) , LogSmartObject , Error ,
TEXT ( " Skipped runtime data creation for SmartObject %s: Invalid definition " ) , * GetNameSafe ( Component - > GetOwner ( ) ) ) ;
continue ;
}
if ( Component ! = nullptr )
{
// When component is available we add it to the simulation along with its collection entry to create the runtime instance and bound them together.
2022-12-05 08:19:29 -05:00
Component - > SetRegisteredHandle ( Entry . GetHandle ( ) , ESmartObjectRegistrationType : : WithCollection ) ;
2022-11-24 14:53:52 -05:00
AddComponentToSimulation ( * Component , Entry , /*bCommitChanges=*/ false ) ;
}
else
{
// Otherwise we create the runtime instance based on the information from the collection and component will be bound later (e.g. on load)
AddCollectionEntryToSimulation ( Entry , * Definition , nullptr , /*bCommitChanges=*/ false ) ;
}
2022-03-16 03:47:02 -04:00
}
2022-11-24 14:53:52 -05:00
check ( EntityManager ) ;
EntityManager - > FlushCommands ( ) ;
2021-09-28 13:33:00 -04:00
}
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponent ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
2022-11-24 14:53:52 -05:00
return SmartObjectContainer . GetSmartObjectComponent ( ClaimHandle . SmartObjectHandle ) ;
2021-09-28 13:33:00 -04:00
}
2022-09-12 19:23:55 -04:00
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponentByRequestResult ( const FSmartObjectRequestResult & Result ) const
{
2022-11-24 14:53:52 -05:00
return SmartObjectContainer . GetSmartObjectComponent ( Result . SmartObjectHandle ) ;
2022-09-12 19:23:55 -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-08-12 07:56:27 -04:00
UMassEntitySubsystem * EntitySubsystem = World . GetSubsystem < UMassEntitySubsystem > ( ) ;
2022-02-03 14:03:16 -05:00
if ( ! ensureMsgf ( EntitySubsystem ! = nullptr , TEXT ( " Entity subsystem required to use SmartObjects " ) ) )
{
return ;
}
2022-11-07 09:57:31 -05:00
InitializeRuntime ( EntitySubsystem - > GetMutableEntityManager ( ) . AsShared ( ) ) ;
}
void USmartObjectSubsystem : : InitializeRuntime ( const TSharedPtr < FMassEntityManager > & InEntityManager )
{
check ( InEntityManager ) ;
EntityManager = InEntityManager ;
2022-02-03 14:03:16 -05:00
2022-04-04 13:22:01 -04:00
if ( UE : : SmartObject : : bDisableRuntime )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Runtime explicitly disabled by CVar. Initialization skipped in %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
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 ) ;
2022-12-05 08:19:29 -05:00
SpacePartition - > SetBounds ( SmartObjectContainer . GetBounds ( ) ) ;
2021-11-02 11:12:43 -04:00
// 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)
2022-12-05 08:19:29 -05:00
// Setting bRuntimeInitialized at this point since the following code assumes the SpatialPartition has been created
// and EntityManager cached.
bRuntimeInitialized = true ;
AddContainerToSimulation ( SmartObjectContainer ) ;
2022-11-24 14:53:52 -05:00
2023-02-02 18:43:13 -05:00
UE_CVLOG_UELOG ( PendingSmartObjectRegistration . Num ( ) > 0 , this , LogSmartObject , VeryVerbose ,
TEXT ( " SmartObjectSubsystem: Handling %d pending registrations during runtime initialization. " ) , PendingSmartObjectRegistration . Num ( ) ) ;
2022-11-30 07:17:21 -05:00
2022-11-24 14:53:52 -05:00
for ( TObjectPtr < USmartObjectComponent > & SOComponent : PendingSmartObjectRegistration )
{
2023-02-02 18:43:13 -05:00
// ensure the SOComponent is still valid - things could have happened to it between adding to PendingSmartObjectRegistration and it being processed here
2022-11-24 14:53:52 -05:00
if ( SOComponent & & IsValid ( SOComponent ) )
{
RegisterSmartObject ( * SOComponent ) ;
}
}
PendingSmartObjectRegistration . Empty ( ) ;
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-08-12 07:56:27 -04:00
EntityManager - > FlushCommands ( ) ;
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
// Refresh debug draw
if ( RenderingActor ! = nullptr )
{
RenderingActor - > MarkComponentsRenderStateDirty ( ) ;
2022-03-18 12:31:49 -04:00
}
2022-02-21 01:10:34 -05:00
# endif // UE_ENABLE_DEBUG_DRAWING
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : CleanupRuntime ( )
{
2022-03-16 03:47:02 -04:00
// Process component list first so they can be notified before we destroy their associated runtime instance
2023-02-02 18:43:13 -05:00
for ( const USmartObjectComponent * Component : RegisteredSOComponents )
2022-03-16 03:47:02 -04:00
{
2022-04-27 16:53:50 -04:00
// Make sure component was registered to simulation (e.g. Valid associated definition)
if ( Component ! = nullptr & & Component - > GetRegisteredHandle ( ) . IsValid ( ) )
2022-03-16 03:47:02 -04:00
{
RemoveComponentFromSimulation ( * Component ) ;
}
}
2022-10-26 19:34:40 -04:00
// Cleanup all remaining entries (e.g. associated to unloaded SmartObjectComponents)
2022-11-24 14:53:52 -05:00
if ( EntityManager )
2022-02-17 03:40:43 -05:00
{
2022-11-24 14:53:52 -05:00
for ( auto It ( RuntimeSmartObjects . CreateIterator ( ) ) ; It ; + + It )
{
DestroyRuntimeInstanceInternal ( It . Key ( ) , It . Value ( ) , * EntityManager ) ;
}
// 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.
EntityManager - > FlushCommands ( ) ;
}
2022-10-26 19:34:40 -04:00
RuntimeSmartObjects . Reset ( ) ;
2022-02-17 03:40:43 -05:00
RuntimeCreatedEntries . Reset ( ) ;
2022-11-24 14:53:52 -05:00
bRuntimeInitialized = false ;
2022-02-17 03:40:43 -05:00
2022-11-24 14:53:52 -05:00
RegisteredCollections . Reset ( ) ;
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 ( ) ;
}
2022-08-29 11:12:18 -04:00
void USmartObjectSubsystem : : Deinitialize ( )
{
2022-11-24 14:53:52 -05:00
CleanupRuntime ( ) ;
2022-08-29 11:12:18 -04:00
EntityManager . Reset ( ) ;
Super : : Deinitialize ( ) ;
}
2022-11-23 08:17:20 -05:00
bool USmartObjectSubsystem : : ShouldCreateSubsystem ( UObject * Outer ) const
{
if ( Super : : ShouldCreateSubsystem ( Outer ) )
{
2023-01-18 10:52:51 -05:00
if ( const UWorld * OuterWorld = Cast < UWorld > ( Outer ) )
2022-11-23 08:17:20 -05:00
{
return OuterWorld - > IsNetMode ( NM_Client ) = = false ;
}
}
return false ;
}
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2022-11-24 14:53:52 -05:00
FBox USmartObjectSubsystem : : ComputeBounds ( const UWorld & World ) const
2021-11-02 11:12:43 -04:00
{
FBox Bounds ( ForceInitToZero ) ;
if ( const UWorldPartition * WorldPartition = World . GetWorldPartition ( ) )
{
2022-06-13 10:49:55 -04:00
Bounds = WorldPartition - > GetRuntimeWorldBounds ( ) ;
2021-11-02 11:12:43 -04:00
}
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. " ) ) ;
}
2022-11-24 14:53:52 -05:00
return Bounds ;
2021-11-02 11:12:43 -04:00
}
2023-02-02 18:43:13 -05:00
void USmartObjectSubsystem : : PopulateCollection ( ASmartObjectPersistentCollection & InCollection ) const
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
TArray < USmartObjectComponent * > RelevantComponents ;
if ( GetRegisteredSmartObjectsCompatibleWithCollection ( InCollection , RelevantComponents ) > 0 )
{
InCollection . AppendToCollection ( RelevantComponents ) ;
}
2021-09-28 13:33:00 -04:00
}
2023-02-02 18:43:13 -05:00
int32 USmartObjectSubsystem : : GetRegisteredSmartObjectsCompatibleWithCollection (
const ASmartObjectPersistentCollection & InCollection ,
TArray < USmartObjectComponent * > & OutRelevantComponents
) const
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
const int32 InitialCount = OutRelevantComponents . Num ( ) ;
if ( bIsPartitionedWorld = = false )
{
const ULevel * MyLevel = InCollection . GetLevel ( ) ;
const ULevelStreaming * MyLevelStreaming = ULevelStreaming : : FindStreamingLevel ( MyLevel ) ;
const bool bCollectionShouldAlwaysBeLoaded = ( MyLevelStreaming = = nullptr ) | | MyLevelStreaming - > ShouldBeAlwaysLoaded ( ) ;
const ULevel * PreviousLevel = nullptr ;
bool bPreviousLevelValid = false ;
for ( const TObjectPtr < USmartObjectComponent > & Component : RegisteredSOComponents )
{
check ( Component ) ;
if ( Component - > GetCanBePartOfCollection ( ) = = false )
{
continue ;
}
const ULevel * OwnerLevel = Component - > GetComponentLevel ( ) ;
bool bValid = bPreviousLevelValid ;
if ( OwnerLevel ! = PreviousLevel )
{
const ULevelStreaming * LevelStreaming = ULevelStreaming : : FindStreamingLevel ( OwnerLevel ) ;
bValid = ( MyLevelStreaming = = LevelStreaming )
| | ( bCollectionShouldAlwaysBeLoaded & & LevelStreaming & & LevelStreaming - > ShouldBeAlwaysLoaded ( ) ) ;
}
if ( bValid )
{
OutRelevantComponents . Add ( Component ) ;
}
bPreviousLevelValid = bValid ;
PreviousLevel = OwnerLevel ;
}
}
else
{
TArray < const UDataLayerInstance * > DataLayers = InCollection . GetDataLayerInstances ( ) ;
const bool bPersistentLevelCollection = ( DataLayers . Num ( ) = = 0 ) ;
for ( const TObjectPtr < USmartObjectComponent > & Component : RegisteredSOComponents )
{
check ( Component ) ;
if ( Component - > GetCanBePartOfCollection ( ) = = false )
{
continue ;
}
2023-01-18 10:52:51 -05:00
if ( const AActor * Owner = Component - > GetOwner ( ) )
2022-11-24 14:53:52 -05:00
{
const bool bInPersistentLayer = ( Owner - > HasDataLayers ( ) = = false ) ;
if ( bPersistentLevelCollection = = bInPersistentLayer )
{
if ( bPersistentLevelCollection )
{
OutRelevantComponents . Add ( Component ) ;
}
else
{
for ( const UDataLayerInstance * DataLayerInstance : DataLayers )
{
if ( Owner - > ContainsDataLayer ( DataLayerInstance ) )
{
OutRelevantComponents . Add ( Component ) ;
// breaking here since at the moment we only support registering smart objects only
// with a single collection
break ;
}
}
}
}
}
}
}
return ( OutRelevantComponents . Num ( ) - InitialCount ) ;
}
void USmartObjectSubsystem : : IterativelyBuildCollections ( )
{
ensureMsgf ( bIsPartitionedWorld , TEXT ( " %s expected to be called in World Paritioned worlds " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
if ( RegisteredSOComponents . Num ( ) = = 0 )
2021-09-28 13:33:00 -04:00
{
return ;
}
2022-11-24 14:53:52 -05:00
TArray < USmartObjectComponent * > RelevantComponents ;
for ( TWeakObjectPtr < ASmartObjectPersistentCollection > & WeakCollection : RegisteredCollections )
{
if ( ASmartObjectPersistentCollection * Collection = WeakCollection . Get ( ) )
{
RelevantComponents . Reset ( ) ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
if ( GetRegisteredSmartObjectsCompatibleWithCollection ( * Collection , RelevantComponents ) > 0 )
{
Collection - > AppendToCollection ( RelevantComponents ) ;
2021-09-28 13:33:00 -04:00
2022-11-24 14:53:52 -05:00
// A component can belong to only a single collection. We remove objects added to the collection so that
// they do get added to another collection. Also, the subsequent GetRegisteredSmartObjectsCompatibleWithCollection
// calls get less data to consider.
// Note: This function is to be run as part of a WorldBuilding commandlet and as such doesn't require
// proper SmartObject unregistration.
for ( USmartObjectComponent * SOComponent : RelevantComponents )
{
RegisteredSOComponents . RemoveSingleSwap ( SOComponent ) ;
}
}
}
}
2021-09-28 13:33:00 -04:00
}
# endif // WITH_EDITOR
2022-11-24 14:53:52 -05:00
# if WITH_EDITORONLY_DATA
void USmartObjectSubsystem : : CreatePersistentCollectionFromDeprecatedData ( UWorld & World , const ADEPRECATED_SmartObjectCollection & DeprecatedCollection )
{
2022-12-05 08:19:29 -05:00
if ( DeprecatedCollection . CollectionEntries . Num ( ) = = 0 )
{
// we ignore the empty deprecated collections - we used to always create these even if no smart objects were being used
// and an empty collection is an indication of such a case. No point in creating a replacement for such a collection.
return ;
}
2022-11-24 14:53:52 -05:00
FActorSpawnParameters SpawnParams ;
SpawnParams . OverrideLevel = DeprecatedCollection . GetLevel ( ) ;
if ( ASmartObjectPersistentCollection * NewCollection = World . SpawnActor < ASmartObjectPersistentCollection > ( SpawnParams ) )
{
NewCollection - > SmartObjectContainer . Bounds = DeprecatedCollection . Bounds ;
NewCollection - > SmartObjectContainer . CollectionEntries = DeprecatedCollection . CollectionEntries ;
NewCollection - > SmartObjectContainer . RegisteredIdToObjectMap = DeprecatedCollection . RegisteredIdToObjectMap ;
NewCollection - > SmartObjectContainer . Definitions = DeprecatedCollection . Definitions ;
NewCollection - > bUpdateCollectionOnSmartObjectsChange = DeprecatedCollection . bBuildCollectionAutomatically ;
}
}
# endif // WITH_EDITORONLY_DATA
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
void USmartObjectSubsystem : : DebugUnregisterAllSmartObjects ( )
{
2023-02-02 18:43:13 -05:00
for ( const USmartObjectComponent * Cmp : RegisteredSOComponents )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
if ( Cmp ! = nullptr & & RuntimeSmartObjects . Find ( Cmp - > GetRegisteredHandle ( ) ) ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
RemoveComponentFromSimulation ( * Cmp ) ;
2021-09-28 13:33:00 -04:00
}
}
}
void USmartObjectSubsystem : : DebugRegisterAllSmartObjects ( )
{
2022-03-16 03:47:02 -04:00
for ( USmartObjectComponent * Cmp : RegisteredSOComponents )
{
if ( Cmp ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-11-24 14:53:52 -05:00
const FSmartObjectCollectionEntry * Entry = SmartObjectContainer . GetEntries ( ) . FindByPredicate (
2022-03-16 03:47:02 -04:00
[ Handle = Cmp - > GetRegisteredHandle ( ) ] ( const FSmartObjectCollectionEntry & CollectionEntry )
{
return CollectionEntry . GetHandle ( ) = = Handle ;
} ) ;
// In this debug command we register back components that were already part of the simulation but
// removed using debug command 'ai.debug.so.UnregisterAllSmartObjects'.
// We need to find associated collection entry and pass it back so the callbacks can be bound properly
if ( Entry & & RuntimeSmartObjects . Find ( Entry - > GetHandle ( ) ) = = nullptr )
{
AddComponentToSimulation ( * Cmp , * Entry ) ;
}
2021-09-28 13:33:00 -04:00
}
}
}
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : DebugInitializeRuntime ( )
{
// do not initialize more than once or on a GameWorld
2022-11-24 14:53:52 -05:00
if ( bRuntimeInitialized | | GetWorldRef ( ) . IsGameWorld ( ) )
2022-02-17 03:40:43 -05:00
{
return ;
}
InitializeRuntime ( ) ;
}
void USmartObjectSubsystem : : DebugCleanupRuntime ( )
{
// do not cleanup more than once or on a GameWorld
2022-11-24 14:53:52 -05:00
if ( ! bRuntimeInitialized | | GetWorldRef ( ) . IsGameWorld ( ) )
2022-02-17 03:40:43 -05:00
{
return ;
}
CleanupRuntime ( ) ;
}
2022-09-28 01:06:15 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
2022-12-07 13:05:23 -05:00
//----------------------------------------------------------------------//
// deprecated functions implementations
//----------------------------------------------------------------------//
PRAGMA_DISABLE_DEPRECATION_WARNINGS
bool USmartObjectSubsystem : : UnregisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent , const ESmartObjectUnregistrationMode UnregistrationMode )
{
const bool bShouldDestroyRuntimeData = ( UnregistrationMode = = ESmartObjectUnregistrationMode : : DestroyRuntimeInstance )
2023-01-23 18:47:59 -05:00
| | ( SmartObjectComponent . GetRegistrationType ( ) = = ESmartObjectRegistrationType : : Dynamic ) ;
2022-12-07 13:05:23 -05:00
return UnregisterSmartObjectInternal ( SmartObjectComponent , bShouldDestroyRuntimeData ) ;
}
2023-01-18 10:52:51 -05:00
PRAGMA_ENABLE_DEPRECATION_WARNINGS