2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SmartObjectSubsystem.h"
2021-11-26 15:48:13 -05:00
# include "SmartObjectDefinition.h"
2021-09-28 13:33:00 -04:00
# include "SmartObjectComponent.h"
# include "SmartObjectCollection.h"
# include "EngineUtils.h"
2022-01-19 14:16:16 -05:00
# include "MassCommandBuffer.h"
2022-08-12 10:47:11 -04:00
# include "MassEntityManager.h"
# include "MassEntitySubsystem.h"
2022-02-21 01:10:34 -05:00
# include "SmartObjectHashGrid.h"
2021-09-28 13:33:00 -04:00
# include "VisualLogger/VisualLogger.h"
2022-02-17 03:40:43 -05:00
# include "ProfilingDebugging/CpuProfilerTrace.h"
2021-09-28 13:33:00 -04:00
2022-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
# include "MassExecutor.h"
# endif
2021-11-02 11:12:43 -04:00
# if WITH_EDITOR
# include "Engine/LevelBounds.h"
# include "WorldPartition/WorldPartition.h"
# endif
2022-04-04 13:22:01 -04:00
namespace UE : : SmartObject
2021-09-28 13:33:00 -04:00
{
2022-04-04 13:22:01 -04: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 ) ;
2021-09-28 13:33:00 -04:00
# if WITH_SMARTOBJECT_DEBUG
2022-04-04 13:22:01 -04:00
namespace Debug
{
2022-03-30 15:13:42 -04:00
static FAutoConsoleCommandWithWorld RegisterAllSmartObjectsCmd
(
TEXT ( " ai.debug.so.RegisterAllSmartObjects " ) ,
TEXT ( " Force register all objects registered in the subsystem to simulate & debug runtime flows (will ignore already registered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
2021-09-28 13:33:00 -04:00
{
2022-03-30 15:13:42 -04:00
Subsystem - > DebugRegisterAllSmartObjects ( ) ;
}
} )
) ;
2021-09-28 13:33:00 -04:00
2022-03-30 15:13:42 -04:00
static FAutoConsoleCommandWithWorld UnregisterAllSmartObjectsCmd
(
TEXT ( " ai.debug.so.UnregisterAllSmartObjects " ) ,
TEXT ( " Force unregister all objects registered in the subsystem to simulate & debug runtime flows (will ignore already unregistered components). " ) ,
FConsoleCommandWithWorldDelegate : : CreateLambda ( [ ] ( const UWorld * InWorld )
{
if ( USmartObjectSubsystem * Subsystem = USmartObjectSubsystem : : GetCurrent ( InWorld ) )
2021-09-28 13:33:00 -04:00
{
2022-03-30 15:13:42 -04:00
Subsystem - > DebugUnregisterAllSmartObjects ( ) ;
}
} )
) ;
} // UE::SmartObject::Debug
2022-04-04 13:22:01 -04:00
# endif // WITH_SMARTOBJECT_DEBUG
} // UE::SmartObject
2021-09-28 13:33:00 -04:00
//----------------------------------------------------------------------//
// USmartObjectSubsystem
//----------------------------------------------------------------------//
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : OnWorldComponentsUpdated ( UWorld & World )
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
// Load class required to instantiate the space partition structure
UE_CVLOG_UELOG ( ! SpacePartitionClassName . IsValid ( ) , this , LogSmartObject , Error , TEXT ( " A valid space partition class name is required. " ) ) ;
if ( SpacePartitionClassName . IsValid ( ) )
{
SpacePartitionClass = LoadClass < USmartObjectSpacePartition > ( this , * SpacePartitionClassName . ToString ( ) ) ;
UE_CVLOG_UELOG ( * SpacePartitionClass = = nullptr , this , LogSmartObject , Error , TEXT ( " Unable to load class %s " ) , * SpacePartitionClassName . ToString ( ) ) ;
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
SpawnMissingCollection ( ) ;
2021-09-28 13:33:00 -04:00
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
ComputeBounds ( World , * MainCollection ) ;
}
2022-02-21 01:10:34 -05:00
# endif // WITH_EDITOR
2021-09-28 13:33:00 -04:00
}
USmartObjectSubsystem * USmartObjectSubsystem : : GetCurrent ( const UWorld * World )
{
return UWorld : : GetSubsystem < USmartObjectSubsystem > ( World ) ;
}
2022-03-16 03:47:02 -04:00
FSmartObjectRuntime * USmartObjectSubsystem : : AddComponentToSimulation ( USmartObjectComponent & SmartObjectComponent , const FSmartObjectCollectionEntry & NewEntry )
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 " ) ) ;
FSmartObjectRuntime * SmartObjectRuntime = AddCollectionEntryToSimulation ( NewEntry , * SmartObjectComponent . GetDefinition ( ) ) ;
if ( SmartObjectRuntime ! = nullptr )
{
SmartObjectComponent . OnRuntimeInstanceCreated ( * SmartObjectRuntime ) ;
}
return SmartObjectRuntime ;
}
void USmartObjectSubsystem : : BindComponentToSimulation ( USmartObjectComponent & SmartObjectComponent )
{
// Notify the component to bind to its runtime counterpart
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Binding a component should only be used when an associated runtime instance exists. " ) ) )
{
SmartObjectComponent . OnRuntimeInstanceBound ( * SmartObjectRuntime ) ;
}
}
void USmartObjectSubsystem : : UnbindComponentFromSimulation ( USmartObjectComponent & SmartObjectComponent )
{
// Notify the component to unbind from its runtime counterpart
FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
if ( ensureMsgf ( SmartObjectRuntime ! = nullptr , TEXT ( " Unbinding a component should only be used when an associated runtime instance exists. " ) ) )
{
SmartObjectComponent . OnRuntimeInstanceUnbound ( * SmartObjectRuntime ) ;
}
}
FSmartObjectRuntime * USmartObjectSubsystem : : AddCollectionEntryToSimulation ( const FSmartObjectCollectionEntry & Entry , const USmartObjectDefinition & Definition )
{
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-01-31 18:52:49 -05:00
if ( ! ensureMsgf ( RuntimeSmartObjects . Find ( Handle ) = = nullptr , TEXT ( " Handle '%s' already registered in runtime simulation " ) , * LexToString ( Handle ) ) )
2021-09-28 13:33:00 -04:00
{
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 ;
2021-09-28 13:33:00 -04:00
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
Runtime . Bounds = Bounds ;
# endif
2022-01-19 14:16:16 -05:00
// Create runtime data and entity for each slot
2022-02-03 14:03:16 -05:00
int32 SlotIndex = 0 ;
for ( const FSmartObjectSlotDefinition & SlotDefinition : Definition . GetSlots ( ) )
2022-01-19 14:16:16 -05:00
{
2022-02-03 14:03:16 -05:00
// Build our shared fragment
FMassArchetypeSharedFragmentValues SharedFragmentValues ;
2022-11-01 15:11:25 -04:00
2022-10-31 20:00:30 -04:00
const uint32 Hash = HashCombine ( Definition . GetUniqueID ( ) , SlotIndex ) ;
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 ) ) ;
// Setup initial state from slot definition
Slot . bEnabled = SlotDefinition . bEnabled ;
Slot . Tags = SlotDefinition . RuntimeTags ;
2022-02-03 14:03:16 -05:00
Runtime . SlotHandles [ SlotIndex ] = SlotHandle ;
SlotIndex + + ;
}
// For objects added to simulation after initial collection, we need to flush the command buffer
if ( bInitialCollectionAddedToSimulation )
{
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-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
{
2022-10-26 19:34:40 -04:00
// temporary debug data logging to help fixing FORT-515024
UE_LOG ( LogSmartObject , Error , TEXT ( " %s called with %s SO Handle and no corresponding SmartObjectRuntime " )
, 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 ;
}
void USmartObjectSubsystem : : DestroyRuntimeInstanceInternal ( const FSmartObjectHandle Handle , const FSmartObjectRuntime & SmartObjectRuntime , FMassEntityManager & EntityManagerRef )
{
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
2022-02-21 01:10:34 -05:00
// Destroy entities associated to slots
2022-01-19 14:16:16 -05:00
TArray < FMassEntityHandle > EntitiesToDestroy ;
2022-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
{
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
}
2022-03-16 03:47:02 -04:00
void USmartObjectSubsystem : : RemoveComponentFromSimulation ( USmartObjectComponent & SmartObjectComponent )
2021-11-02 11:12:43 -04:00
{
2022-10-26 19:34:40 -04:00
if ( RemoveRuntimeInstanceFromSimulation ( SmartObjectComponent . GetRegisteredHandle ( ) ) )
{
SmartObjectComponent . OnRuntimeInstanceDestroyed ( ) ;
}
else
{
UE_LOG ( LogSmartObject , Warning , TEXT ( " %s call failed for %s " )
, 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 ) ;
if ( Slot . Release ( ClaimHandle , /* bAborted */ true ) )
{
OnSlotChanged ( SmartObjectRuntime , Slot , SlotHandle , ESmartObjectChangeReason : : OnReleased ) ;
}
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 )
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Attempting to register %s while its DefinitionAsset is not set. Bailing out. " ) ,
* 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 ( ) ;
}
if ( bIsValid . Get ( false ) = = false )
2022-11-04 03:35:03 -04:00
{
UE_VLOG_UELOG ( this , LogSmartObject , Warning , TEXT ( " Attempting to register %s while its DefinitionAsset fails validation test. Bailing out. " ) ,
* GetFullNameSafe ( & SmartObjectComponent ) ) ;
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 ) ;
}
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to register %s. Already registered " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
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 ( ) ) ) ;
2021-11-02 11:12:43 -04:00
// Main collection may not assigned until world components are updated (active level set and actors registered)
// In this case objects will be part of the loaded collection or collection will be rebuilt from registered components
if ( IsValid ( MainCollection ) )
{
const UWorld & World = GetWorldRef ( ) ;
bool bAddToCollection = true ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
2022-10-10 12:00:30 -04:00
// For collections not built automatically we wait an explicit build request to clear and repopulate
if ( MainCollection - > ShouldBuildCollectionAutomatically ( ) = = false )
2021-11-02 11:12:43 -04:00
{
bAddToCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection that is built on demand only. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
// For partition world we don't alter the collection unless we are explicitly building the collection
else if ( World . IsPartitionedWorld ( ) & & ! MainCollection - > IsBuildingForWorldPartition ( ) )
{
bAddToCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection that is owned by partitioned world. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
}
2021-09-28 13:33:00 -04:00
# endif // WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( bAddToCollection )
{
2022-03-16 03:47:02 -04:00
bool bAlreadyInCollection = false ;
const FSmartObjectCollectionEntry * Entry = MainCollection - > AddSmartObject ( SmartObjectComponent , bAlreadyInCollection ) ;
2021-11-02 11:12:43 -04:00
2022-02-25 14:18:42 -05:00
# if WITH_EDITOR
2022-03-16 03:47:02 -04:00
if ( Entry ! = nullptr & & ! bAlreadyInCollection )
2022-02-25 14:18:42 -05:00
{
OnMainCollectionDirtied . Broadcast ( ) ;
}
# endif
2022-03-16 03:47:02 -04:00
// At runtime we only consider registrations after collection was pushed to the simulation. All existing entries were added on WorldBeginPlay
if ( World . IsGameWorld ( ) & & bInitialCollectionAddedToSimulation & & Entry ! = nullptr )
2021-11-02 11:12:43 -04:00
{
2022-03-16 03:47:02 -04:00
if ( bAlreadyInCollection )
{
// Simply bind the newly available component to its active runtime instance
BindComponentToSimulation ( SmartObjectComponent ) ;
}
else
{
// This is a new entry added after runtime initialization, mark it as a runtime entry (lifetime is tied to the component)
AddComponentToSimulation ( SmartObjectComponent , * Entry ) ;
RuntimeCreatedEntries . Add ( SmartObjectComponent . GetRegisteredHandle ( ) ) ;
}
2021-11-02 11:12:43 -04:00
}
}
2022-10-10 12:00:30 -04:00
ensureMsgf ( RegisteredSOComponents . Find ( & SmartObjectComponent ) = = INDEX_NONE
, TEXT ( " Adding %s to RegisteredSOColleciton, but it has already been added. Missing unregister call? " ) , * SmartObjectComponent . GetName ( ) ) ;
RegisteredSOComponents . Add ( & SmartObjectComponent ) ;
2021-11-02 11:12:43 -04:00
}
2022-01-12 16:15:32 -05:00
else
{
2022-04-26 14:39:26 -04:00
if ( bInitialCollectionAddedToSimulation )
{
// Report error regarding missing Collection only when SmartObjects are registered (i.e. Collection is required)
UE_VLOG_UELOG ( this , LogSmartObject , Error ,
TEXT ( " %s not added to collection since Main Collection doesn't exist in world '%s'. You need to open and save that world to create the missing collection. " ) ,
* GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetWorldRef ( ) . GetFullName ( ) ) ;
}
else
{
2022-10-10 12:00:30 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not added to collection since Main Collection is not set yet. Storing SOComponent instance for registration once a collection is set. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
PendingSmartObjectRegistration . Add ( & SmartObjectComponent ) ;
2022-04-26 14:39:26 -04:00
}
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 ;
}
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 ) )
{
return UnregisterSmartObjectInternal ( SmartObjectComponent , ESmartObjectUnregistrationMode : : DestroyRuntimeInstance ) ;
}
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Failed to unregister %s. Already unregistered " ) ,
* GetFullNameSafe ( SmartObjectComponent . GetOwner ( ) ) ,
* GetFullNameSafe ( SmartObjectComponent . GetDefinition ( ) ) ) ;
return false ;
}
bool USmartObjectSubsystem : : UnregisterSmartObjectInternal ( USmartObjectComponent & SmartObjectComponent , const ESmartObjectUnregistrationMode UnregistrationMode )
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 ( ) ) ) ;
2021-11-02 11:12:43 -04:00
if ( IsValid ( MainCollection ) )
{
const UWorld & World = GetWorldRef ( ) ;
bool bRemoveFromCollection = true ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
if ( ! World . IsGameWorld ( ) )
{
2022-10-10 12:00:30 -04:00
// For collections not built automatically we wait an explicit build request to clear and repopulate
if ( MainCollection - > ShouldBuildCollectionAutomatically ( ) = = false )
2021-11-02 11:12:43 -04:00
{
bRemoveFromCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not removed from collection that is built on demand only. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
// For partition world we never remove from the collection since it is built incrementally
2022-10-10 12:00:30 -04:00
else if ( World . IsPartitionedWorld ( ) )
2021-11-02 11:12:43 -04:00
{
bRemoveFromCollection = false ;
UE_VLOG_UELOG ( this , LogSmartObject , VeryVerbose , TEXT ( " %s not removed from collection that is owned by partitioned world. " ) , * GetNameSafe ( SmartObjectComponent . GetOwner ( ) ) ) ;
}
}
# endif // WITH_EDITOR
// At runtime, only entries created outside the initial collection are removed from simulation and collection
2022-04-29 14:19:34 -04:00
if ( World . IsGameWorld ( )
& & bInitialCollectionAddedToSimulation
& & SmartObjectComponent . GetRegisteredHandle ( ) . IsValid ( ) ) // Make sure component was registered to simulation (e.g. Valid associated definition)
2021-11-02 11:12:43 -04:00
{
2022-03-30 15:13:42 -04:00
bRemoveFromCollection = RuntimeCreatedEntries . Remove ( SmartObjectComponent . GetRegisteredHandle ( ) ) ! = 0
| | UnregistrationMode = = ESmartObjectUnregistrationMode : : DestroyRuntimeInstance ;
2021-11-02 11:12:43 -04:00
if ( bRemoveFromCollection )
{
2022-03-16 03:47:02 -04:00
RemoveComponentFromSimulation ( SmartObjectComponent ) ;
}
else
{
// Unbind the component from its associated runtime instance
UnbindComponentFromSimulation ( SmartObjectComponent ) ;
2021-11-02 11:12:43 -04:00
}
}
if ( bRemoveFromCollection )
{
MainCollection - > RemoveSmartObject ( SmartObjectComponent ) ;
}
2021-09-28 13:33:00 -04:00
}
2022-03-16 03:47:02 -04:00
RegisteredSOComponents . Remove ( & 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 ,
TEXT ( " Failed to register SmartObject components for %s. No components found. " ) , * SmartObjectActor . GetName ( ) ) ;
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 ,
TEXT ( " Failed to unregister SmartObject components for %s. No components found. " ) , * SmartObjectActor . GetName ( ) ) ;
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-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 ;
2022-03-16 03:47:02 -04: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 ;
}
2022-03-02 13:25:37 -05:00
return Claim ( Handle , SlotHandles . Top ( ) ) ;
2021-09-28 13:33:00 -04:00
}
2022-01-31 18:52:49 -05:00
FSmartObjectClaimHandle USmartObjectSubsystem : : Claim ( const FSmartObjectHandle Handle , const FSmartObjectSlotHandle SlotHandle )
2022-01-19 14:16:16 -05:00
{
2022-03-18 12:31:49 -04:00
if ( ! Handle . IsValid ( ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Claiming using an unset smart object handle. Returning invalid FSmartObjectClaimHandle. " ) ) ;
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 ;
}
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
const FSmartObjectClaimHandle ClaimHandle ( Handle , SlotHandle , User ) ;
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
{
2022-11-01 15:11:25 -04:00
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnClaimed ) ;
}
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
}
2022-09-01 09:03:19 -04:00
bool USmartObjectSubsystem : : CanBeClaimed ( FSmartObjectSlotHandle SlotHandle ) const
{
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 ;
}
2022-01-26 17:33:02 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinition ( const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
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
}
2022-09-16 12:46:45 -04:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : GetBehaviorDefinitionByRequestResult ( const FSmartObjectRequestResult & RequestResult , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
{
const FSmartObjectRuntime * SmartObjectRuntime = GetValidatedRuntime ( RequestResult . SmartObjectHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
return SmartObjectRuntime ! = nullptr ? GetBehaviorDefinition ( * SmartObjectRuntime , RequestResult . SlotHandle , DefinitionClass ) : nullptr ;
}
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 ) ;
}
2021-11-26 15:48:13 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : Use ( const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
2021-09-28 13:33:00 -04:00
{
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
}
2022-01-19 14:16:16 -05:00
const USmartObjectBehaviorDefinition * USmartObjectSubsystem : : Use ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectClaimHandle & ClaimHandle , const TSubclassOf < USmartObjectBehaviorDefinition > & DefinitionClass )
2021-09-28 13:33:00 -04:00
{
2022-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 ) ) ;
2021-09-28 13:33:00 -04:00
UE_VLOG_LOCATION ( this , LogSmartObject , Display , SmartObjectRuntime . GetTransform ( ) . GetLocation ( ) , 50.f , FColor : : Green , TEXT ( " Use " ) ) ;
2022-01-19 14:16:16 -05:00
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 ;
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 ;
}
2022-11-01 15:11:25 -04:00
const bool bSuccess = Slot - > Release ( ClaimHandle , /*bAborted*/ false ) ;
2022-01-19 14:16:16 -05:00
UE_CVLOG_UELOG ( bSuccess , this , LogSmartObject , Verbose , TEXT ( " Released using handle %s " ) , * LexToString ( ClaimHandle ) ) ;
UE_CVLOG_LOCATION ( bSuccess , this , LogSmartObject , Display , GetSlotLocation ( ClaimHandle ) . GetValue ( ) , 50.f , FColor : : Red , TEXT ( " Release " ) ) ;
2022-11-01 15:11:25 -04:00
if ( bSuccess )
{
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
OnSlotChanged ( * SmartObjectRuntime , * Slot , ClaimHandle . SlotHandle , ESmartObjectChangeReason : : OnReleased ) ;
}
}
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 ;
}
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 ( ) ) )
{
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagAdded , Tag ) ;
}
}
}
}
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 ( ) ) )
{
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , ESmartObjectChangeReason : : OnTagRemoved , Tag ) ;
}
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__ ) ) )
{
2022-11-04 09:18:52 -04:00
bPreviousValue = Slot - > bEnabled ;
2022-11-01 15:11:25 -04:00
if ( Slot - > bEnabled ! = bEnabled )
{
Slot - > bEnabled = bEnabled ;
if ( const FSmartObjectRuntime * SmartObjectRuntime = RuntimeSmartObjects . Find ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
OnSlotChanged ( * SmartObjectRuntime , * Slot , SlotHandle , Slot - > bEnabled ? ESmartObjectChangeReason : : OnEnabled : ESmartObjectChangeReason : : OnDisabled ) ;
}
}
}
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__ ) ) )
{
if ( Slot - > GetEventDelegate ( ) . IsBound ( )
& & RuntimeSmartObjects . Contains ( Slot - > GetOwnerRuntimeObject ( ) ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = Slot - > GetOwnerRuntimeObject ( ) ;
Data . SlotHandle = SlotHandle ;
Data . Reason = ESmartObjectChangeReason : : OnEvent ;
Data . Tag = EventTag ;
Data . EventPayload = Payload ;
Slot - > GetEventDelegate ( ) . Broadcast ( Data ) ;
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 ) ;
SmartObjectRuntime . OnTagChangedDelegate . ExecuteIfBound ( Tag , 1 ) ;
2022-11-01 15:11:25 -04:00
UpdateRuntimeInstanceStatus ( SmartObjectRuntime , ESmartObjectChangeReason : : OnTagAdded ) ;
2022-03-16 03:47:02 -04:00
}
}
void USmartObjectSubsystem : : RemoveTagFromInstance ( FSmartObjectRuntime & SmartObjectRuntime , const FGameplayTag & Tag )
{
if ( SmartObjectRuntime . Tags . RemoveTag ( Tag ) )
{
SmartObjectRuntime . OnTagChangedDelegate . ExecuteIfBound ( Tag , 0 ) ;
2022-11-01 15:11:25 -04:00
UpdateRuntimeInstanceStatus ( SmartObjectRuntime , ESmartObjectChangeReason : : OnTagRemoved ) ;
2022-03-16 03:47:02 -04:00
}
}
2022-11-01 15:11:25 -04:00
void USmartObjectSubsystem : : UpdateRuntimeInstanceStatus ( FSmartObjectRuntime & SmartObjectRuntime , const ESmartObjectChangeReason Reason )
2022-03-16 03:47:02 -04:00
{
const FGameplayTagQuery & Requirements = SmartObjectRuntime . GetDefinition ( ) . GetObjectTagFilter ( ) ;
2022-11-01 15:11:25 -04:00
const bool bEnabled = ( Requirements . IsEmpty ( ) | | Requirements . Matches ( SmartObjectRuntime . Tags ) ) ;
2022-03-16 03:47:02 -04:00
2022-11-01 15:11:25 -04:00
if ( SmartObjectRuntime . bEnabled ! = bEnabled )
2022-03-16 03:47:02 -04:00
{
2022-11-01 15:11:25 -04:00
// Update slot enable states.
for ( const FSmartObjectSlotHandle SlotHandle : SmartObjectRuntime . SlotHandles )
2022-03-16 03:47:02 -04:00
{
2022-11-01 15:11:25 -04:00
FSmartObjectRuntimeSlot & Slot = RuntimeSlots . FindChecked ( SlotHandle ) ;
Slot . bEnabled = bEnabled ;
OnSlotChanged ( SmartObjectRuntime , Slot , SlotHandle , bEnabled ? ESmartObjectChangeReason : : OnEnabled : ESmartObjectChangeReason : : OnDisabled ) ;
2022-03-16 03:47:02 -04:00
}
2022-11-01 15:11:25 -04:00
SmartObjectRuntime . bEnabled = bEnabled ;
2022-03-16 03:47:02 -04:00
}
}
2022-11-01 15:11:25 -04:00
void USmartObjectSubsystem : : OnSlotChanged ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRuntimeSlot & Slot ,
const FSmartObjectSlotHandle SlotHandle , const ESmartObjectChangeReason Reason , const FGameplayTag ChangedTag ) const
2021-09-28 13:33:00 -04:00
{
2022-11-01 15:11:25 -04:00
if ( Slot . GetEventDelegate ( ) . IsBound ( ) )
{
FSmartObjectEventData Data ;
Data . SmartObjectHandle = Slot . GetOwnerRuntimeObject ( ) ;
Data . SlotHandle = SlotHandle ;
Data . Reason = Reason ;
Data . Tag = ChangedTag ;
Slot . GetEventDelegate ( ) . Broadcast ( Data ) ;
}
}
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 )
{
if ( FSmartObjectRuntimeSlot * Slot = GetMutableSlotVerbose ( SlotHandle , ANSI_TO_TCHAR ( __FUNCTION__ ) ) )
{
return & Slot - > GetMutableEventDelegate ( ) ;
}
return nullptr ;
}
2022-02-21 01:10:34 -05:00
# if UE_ENABLE_DEBUG_DRAWING
void USmartObjectSubsystem : : DebugDraw ( FDebugRenderSceneProxy * DebugProxy ) const
{
if ( ! bInitialCollectionAddedToSimulation )
{
return ;
}
checkfSlow ( SpacePartition ! = nullptr , TEXT ( " Space partition is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition - > Draw ( DebugProxy ) ;
for ( auto It ( RuntimeSmartObjects . CreateConstIterator ( ) ) ; It ; + + It )
{
const FSmartObjectRuntime & Runtime = It . Value ( ) ;
DebugProxy - > Boxes . Emplace ( Runtime . Bounds , GColorList . Blue ) ;
}
}
# endif // UE_ENABLE_DEBUG_DRAWING
2022-01-19 14:16:16 -05:00
void USmartObjectSubsystem : : AddSlotDataDeferred ( const FSmartObjectClaimHandle & ClaimHandle , const FConstStructView InData ) const
2021-09-28 13:33:00 -04:00
{
2022-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 " ) ) & &
2022-03-18 12:31:49 -04:00
ensureMsgf ( InData . GetScriptStruct ( ) - > IsChildOf ( FSmartObjectSlotStateData : : StaticStruct ( ) ) ,
TEXT ( " Given struct doesn't represent a valid runtime data type. Make sure to inherit from FSmartObjectSlotState or one of its child-types. " ) ) )
{
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
{
FInstancedStruct Struct = DataView ;
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 ( ) ;
}
2022-03-02 13:25:37 -05:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectHandle Handle , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutSlots ) 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
{
2022-03-16 03:47:02 -04:00
FindSlots ( * SmartObjectRuntime , Filter , OutSlots ) ;
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 ;
}
}
2022-03-02 13:25:37 -05:00
void USmartObjectSubsystem : : FindSlots ( const FSmartObjectRuntime & SmartObjectRuntime , const FSmartObjectRequestFilter & Filter , TArray < FSmartObjectSlotHandle > & OutResults ) 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 predicate on runtime instance tags (affecting all slots and not affected by the filtering policy)
if ( ! Definition . GetObjectTagFilter ( ) . IsEmpty ( )
& & ! Definition . GetObjectTagFilter ( ) . Matches ( SmartObjectRuntime . GetTags ( ) ) )
2022-01-19 14:16:16 -05: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
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-04 11:28:57 -04:00
const FSmartObjectRuntimeSlot & RuntimeSlot = RuntimeSlots . FindChecked ( SmartObjectRuntime . SlotHandles [ SlotIndex ] ) ;
if ( RuntimeSlot . IsEnabled ( ) & & RuntimeSlot . GetState ( ) = = ESmartObjectSlotState : : Free )
2021-09-28 13:33:00 -04:00
{
2022-03-02 13:25:37 -05:00
OutResults . Add ( SmartObjectRuntime . SlotHandles [ SlotIndex ] ) ;
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
}
}
2022-02-17 03:40:43 -05:00
FSmartObjectRequestResult USmartObjectSubsystem : : FindSmartObject ( const FSmartObjectRequest & Request ) const
2021-09-28 13:33:00 -04:00
{
2022-02-21 01:10:34 -05:00
TArray < FSmartObjectRequestResult > Results ;
FindSmartObjects ( Request , Results ) ;
2022-02-17 03:40:43 -05:00
2022-02-21 01:10:34 -05:00
return Results . Num ( ) ? Results . Top ( ) : FSmartObjectRequestResult ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-17 03:40:43 -05:00
bool USmartObjectSubsystem : : FindSmartObjects ( const FSmartObjectRequest & Request , TArray < FSmartObjectRequestResult > & OutResults ) const
2021-09-28 13:33:00 -04:00
{
2022-02-17 03:40:43 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE_STR ( " SmartObject_FindAllResults " ) ;
2022-02-21 01:10:34 -05:00
if ( ! bInitialCollectionAddedToSimulation )
{
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 ;
2022-03-16 03:47:02 -04:00
FindSlots ( * SmartObjectRuntime , Filter , SlotHandles ) ;
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 ( )
{
2021-11-02 11:12:43 -04:00
for ( TActorIterator < ASmartObjectCollection > It ( GetWorld ( ) ) ; It ; + + It )
2021-09-28 13:33:00 -04:00
{
ASmartObjectCollection * Collection = ( * It ) ;
if ( IsValid ( Collection ) & & Collection - > IsRegistered ( ) = = false )
{
2022-01-12 16:15:32 -05:00
const ESmartObjectCollectionRegistrationResult Result = RegisterCollection ( * Collection ) ;
UE_VLOG_UELOG ( Collection , LogSmartObject , Log , TEXT ( " Collection '%s' registration from USmartObjectSubsystem initialization - %s " ) , * Collection - > GetName ( ) , * UEnum : : GetValueAsString ( Result ) ) ;
2021-09-28 13:33:00 -04:00
}
}
}
2022-01-12 16:15:32 -05:00
ESmartObjectCollectionRegistrationResult USmartObjectSubsystem : : RegisterCollection ( ASmartObjectCollection & InCollection )
2021-09-28 13:33:00 -04:00
{
if ( ! IsValid ( & InCollection ) )
{
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_InvalidCollection ;
2021-09-28 13:33:00 -04:00
}
if ( InCollection . IsRegistered ( ) )
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Error , TEXT ( " Trying to register collection '%s' more than once " ) , * InCollection . GetName ( ) ) ;
2022-01-12 16:15:32 -05:00
return ESmartObjectCollectionRegistrationResult : : Failed_AlreadyRegistered ;
2021-09-28 13:33:00 -04:00
}
2022-01-12 16:15:32 -05:00
ESmartObjectCollectionRegistrationResult Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2021-09-28 13:33:00 -04:00
if ( InCollection . GetLevel ( ) - > IsPersistentLevel ( ) )
{
2021-11-02 11:12:43 -04:00
ensureMsgf ( ! IsValid ( MainCollection ) , TEXT ( " Not expecting to set the main collection more than once " ) ) ;
2022-01-12 16:15:32 -05:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Log , TEXT ( " Main collection '%s' registered with %d entries " ) , * InCollection . GetName ( ) , InCollection . GetEntries ( ) . Num ( ) ) ;
2021-09-28 13:33:00 -04:00
MainCollection = & InCollection ;
2022-10-10 12:00:30 -04:00
for ( TObjectPtr < USmartObjectComponent > & SOComponent : PendingSmartObjectRegistration )
{
// ensure the SOComponent is still valid - things could have happened to it between adding to PendingSmartObjectRegistration and it beind processed here
if ( SOComponent & & IsValid ( SOComponent ) )
{
RegisterSmartObject ( * SOComponent ) ;
}
}
PendingSmartObjectRegistration . Empty ( ) ;
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
// For a collection that is automatically updated, it gets rebuilt on registration in the Edition world.
const UWorld & World = GetWorldRef ( ) ;
if ( ! World . IsGameWorld ( ) & &
! World . IsPartitionedWorld ( ) & &
2022-10-10 12:00:30 -04:00
MainCollection - > ShouldBuildCollectionAutomatically ( ) )
2021-09-28 13:33:00 -04:00
{
RebuildCollection ( InCollection ) ;
}
2022-02-25 14:18:42 -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
2021-09-28 13:33:00 -04:00
InCollection . OnRegistered ( ) ;
2022-01-12 16:15:32 -05:00
Result = ESmartObjectCollectionRegistrationResult : : Succeeded ;
2021-09-28 13:33:00 -04:00
}
else
{
2022-05-09 15:04:45 -04:00
InCollection . Destroy ( ) ;
2022-01-12 16:15:32 -05:00
Result = ESmartObjectCollectionRegistrationResult : : Failed_NotFromPersistentLevel ;
2021-09-28 13:33:00 -04:00
}
2022-01-12 16:15:32 -05:00
return Result ;
2021-09-28 13:33:00 -04:00
}
void USmartObjectSubsystem : : UnregisterCollection ( ASmartObjectCollection & InCollection )
{
if ( MainCollection ! = & InCollection )
{
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( & InCollection , LogSmartObject , Verbose , TEXT ( " Ignoring unregistration of collection '%s' since this is not the main collection. " ) , * InCollection . GetName ( ) ) ;
2021-09-28 13:33:00 -04:00
return ;
}
2022-03-16 03:47:02 -04:00
if ( bInitialCollectionAddedToSimulation )
{
CleanupRuntime ( ) ;
}
2022-01-12 16:15:32 -05:00
MainCollection = nullptr ;
2021-09-28 13:33:00 -04:00
InCollection . OnUnregistered ( ) ;
}
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponent ( const FSmartObjectClaimHandle & ClaimHandle ) const
{
2022-01-19 14:16:16 -05:00
return ( IsValid ( MainCollection ) ? MainCollection - > GetSmartObjectComponent ( ClaimHandle . SmartObjectHandle ) : nullptr ) ;
2021-09-28 13:33:00 -04:00
}
2022-09-12 19:23:55 -04:00
USmartObjectComponent * USmartObjectSubsystem : : GetSmartObjectComponentByRequestResult ( const FSmartObjectRequestResult & Result ) const
{
return ( IsValid ( MainCollection ) ? MainCollection - > GetSmartObjectComponent ( Result . SmartObjectHandle ) : nullptr ) ;
}
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 ) ;
const UWorld & World = GetWorldRef ( ) ;
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 ;
}
2021-11-02 11:12:43 -04:00
if ( ! IsValid ( MainCollection ) )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( MainCollection ! = nullptr & & ! MainCollection - > bNetLoadOnClient & & World . IsNetMode ( NM_Client ) )
{
UE_VLOG_UELOG ( this , LogSmartObject , Verbose , TEXT ( " Collection not loaded on client. Initialization skipped in %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
}
else
{
2022-04-26 14:39:26 -04:00
// Report error regarding missing Collection only when SmartObjects are present in the world (i.e. Collection is required)
UE_CVLOG_UELOG ( RegisteredSOComponents . Num ( ) , this , LogSmartObject , Error ,
TEXT ( " Missing collection in world '%s' during %s. You need to open and save that world to create the missing collection. " ) ,
* GetWorldRef ( ) . GetFullName ( ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) ) ;
2021-11-02 11:12:43 -04:00
}
2021-09-28 13:33:00 -04:00
return ;
}
2022-02-21 01:10:34 -05:00
// Initialize spatial representation structure
checkfSlow ( * SpacePartitionClass ! = nullptr , TEXT ( " Partition class is expected to be valid since we use the plugins default in OnWorldComponentsUpdated. " ) ) ;
SpacePartition = NewObject < USmartObjectSpacePartition > ( this , SpacePartitionClass ) ;
SpacePartition - > SetBounds ( MainCollection - > GetBounds ( ) ) ;
2021-11-02 11:12:43 -04:00
2022-04-04 13:22:01 -04:00
// Build all runtime from collection
2021-11-26 15:48:13 -05:00
// Perform all validations at once since multiple entries can share the same definition
MainCollection - > ValidateDefinitions ( ) ;
2021-11-02 11:12:43 -04:00
2021-09-28 13:33:00 -04:00
for ( const FSmartObjectCollectionEntry & Entry : MainCollection - > GetEntries ( ) )
{
2021-11-26 15:48:13 -05:00
const USmartObjectDefinition * Definition = MainCollection - > GetDefinitionForEntry ( Entry ) ;
2021-09-28 13:33:00 -04:00
USmartObjectComponent * Component = Entry . GetComponent ( ) ;
2021-11-26 15:48:13 -05:00
if ( Definition = = nullptr | | Definition - > IsValid ( ) = = false )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
UE_CVLOG_UELOG ( Component ! = nullptr , Component - > GetOwner ( ) , LogSmartObject , Error ,
2021-11-26 15:48:13 -05:00
TEXT ( " Skipped runtime data creation for SmartObject %s: Invalid definition " ) , * GetNameSafe ( Component - > GetOwner ( ) ) ) ;
2021-09-28 13:33:00 -04:00
continue ;
}
2021-11-02 11:12:43 -04:00
if ( Component ! = nullptr )
{
2022-03-16 03:47:02 -04:00
// 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-01-19 14:16:16 -05:00
Component - > SetRegisteredHandle ( Entry . GetHandle ( ) ) ;
2022-03-16 03:47:02 -04:00
AddComponentToSimulation ( * Component , Entry ) ;
}
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 ) ;
2021-11-02 11:12:43 -04:00
}
2021-09-28 13:33:00 -04:00
}
2021-11-02 11:12:43 -04:00
// Until this point all runtime entries were created from the collection, start tracking newly created
RuntimeCreatedEntries . Reset ( ) ;
// Note that we use our own flag instead of relying on World.HasBegunPlay() since world might not be marked
// as BegunPlay immediately after subsystem OnWorldBeingPlay gets called (e.g. waiting game mode to be ready on clients)
bInitialCollectionAddedToSimulation = true ;
2022-02-03 14:03:16 -05:00
// Flush all entity subsystem commands pushed while adding collection entries to the simulation
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-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-08-12 07:56:27 -04:00
EntityManager = UE : : Mass : : Utils : : GetEntityManagerChecked ( GetWorldRef ( ) ) . AsShared ( ) ;
if ( ! ensureMsgf ( EntityManager , TEXT ( " Entity manager required to use SmartObjects " ) ) )
2022-02-17 03:40:43 -05:00
{
return ;
}
2022-03-16 03:47:02 -04:00
// Process component list first so they can be notified before we destroy their associated runtime instance
for ( USmartObjectComponent * Component : RegisteredSOComponents )
{
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)
for ( auto It ( RuntimeSmartObjects . CreateIterator ( ) ) ; It ; + + It )
2022-02-17 03:40:43 -05:00
{
2022-10-26 19:34:40 -04:00
DestroyRuntimeInstanceInternal ( It . Key ( ) , It . Value ( ) , * EntityManager ) ;
}
RuntimeSmartObjects . Reset ( ) ;
2022-02-17 03:40:43 -05:00
RuntimeCreatedEntries . Reset ( ) ;
bInitialCollectionAddedToSimulation = false ;
// Flush all entity subsystem commands pushed while stopping the simulation
// This is the temporary way to force our commands to be processed until MassEntitySubsystem
// offers a threadsafe solution to push and flush commands in our own execution context.
2022-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 ( ) ;
}
# 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 ( )
{
EntityManager . Reset ( ) ;
Super : : Deinitialize ( ) ;
}
2021-09-28 13:33:00 -04:00
# if WITH_EDITOR
2021-11-02 11:12:43 -04:00
void USmartObjectSubsystem : : ComputeBounds ( const UWorld & World , ASmartObjectCollection & Collection ) const
{
FBox Bounds ( ForceInitToZero ) ;
if ( const UWorldPartition * WorldPartition = World . GetWorldPartition ( ) )
{
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. " ) ) ;
}
Collection . SetBounds ( Bounds ) ;
}
2021-09-28 13:33:00 -04:00
void USmartObjectSubsystem : : RebuildCollection ( ASmartObjectCollection & InCollection )
{
InCollection . RebuildCollection ( RegisteredSOComponents ) ;
}
2022-11-07 09:57:31 -05:00
void USmartObjectSubsystem : : SpawnMissingCollection ( )
2021-09-28 13:33:00 -04:00
{
2021-11-02 11:12:43 -04:00
if ( IsValid ( MainCollection ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
2021-11-02 11:12:43 -04:00
UWorld & World = GetWorldRef ( ) ;
2021-09-28 13:33:00 -04:00
FActorSpawnParameters SpawnInfo ;
2021-11-02 11:12:43 -04:00
SpawnInfo . OverrideLevel = World . PersistentLevel ;
2021-09-28 13:33:00 -04:00
SpawnInfo . bAllowDuringConstructionScript = true ;
2021-11-02 11:12:43 -04:00
UE_VLOG_UELOG ( this , LogSmartObject , Log , TEXT ( " Spawning missing collection for world '%s'. " ) , * World . GetName ( ) ) ;
World . SpawnActor < ASmartObjectCollection > ( ASmartObjectCollection : : StaticClass ( ) , SpawnInfo ) ;
checkf ( IsValid ( MainCollection ) , TEXT ( " MainCollection must be assigned after spawning " ) ) ;
2021-09-28 13:33:00 -04:00
}
# endif // WITH_EDITOR
# if WITH_SMARTOBJECT_DEBUG
void USmartObjectSubsystem : : DebugUnregisterAllSmartObjects ( )
{
2022-03-16 03:47:02 -04:00
for ( 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
if ( MainCollection = = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
return ;
}
for ( USmartObjectComponent * Cmp : RegisteredSOComponents )
{
if ( Cmp ! = nullptr )
2021-09-28 13:33:00 -04:00
{
2022-03-16 03:47:02 -04:00
const FSmartObjectCollectionEntry * Entry = MainCollection - > GetEntries ( ) . FindByPredicate (
[ 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
if ( bInitialCollectionAddedToSimulation | | GetWorldRef ( ) . IsGameWorld ( ) )
{
return ;
}
InitializeRuntime ( ) ;
}
2022-06-14 15:16:17 -04:00
void USmartObjectSubsystem : : DebugRebuildCollection ( )
{
2022-11-07 09:57:31 -05:00
# if WITH_EDITOR
2022-06-14 15:16:17 -04:00
if ( MainCollection ! = nullptr )
{
RebuildCollection ( * MainCollection ) ;
}
# endif // WITH_EDITOR
2022-11-07 09:57:31 -05:00
}
2022-06-14 15:16:17 -04:00
2022-02-17 03:40:43 -05:00
void USmartObjectSubsystem : : DebugCleanupRuntime ( )
{
// do not cleanup more than once or on a GameWorld
if ( ! bInitialCollectionAddedToSimulation | | GetWorldRef ( ) . IsGameWorld ( ) )
{
return ;
}
CleanupRuntime ( ) ;
}
2022-09-28 01:06:15 -04:00
# endif // WITH_SMARTOBJECT_DEBUG