Files
UnrealEngineUWP/Engine/Plugins/Runtime/SmartObjects/Source/SmartObjectsModule/Private/SmartObjectCollection.cpp
mieszko zielinski 31a5065342 Moved GameplayBehaviors out of restricted folder over to Experimental
Moved SmartObjects out of restricted folder
Moved StateTree out of restricted folder
Moved ZoneGraph out of restricted folder
Moved ZoneGraphAnnotations out of restricted folder

#jira UE-115297

#ROBOMERGE-OWNER: mieszko.zielinski
#ROBOMERGE-AUTHOR: mieszko.zielinski
#ROBOMERGE-SOURCE: CL 17648223 via CL 17648246 via CL 17648261 via CL 17648385 via CL 17648390
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v875-17642767)
#ROBOMERGE-CONFLICT from-shelf
#ROBOMERGE[STARSHIP]: UE5-Main

[CL 17648742 by mieszko zielinski in ue5-release-engine-test branch]
2021-09-28 13:33:00 -04:00

270 lines
8.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SmartObjectCollection.h"
#include "SmartObjectTypes.h"
#include "SmartObjectSubsystem.h"
#include "SmartObjectComponent.h"
#include "Engine/World.h"
#include "VisualLogger/VisualLogger.h"
//----------------------------------------------------------------------//
// FSmartObjectCollectionEntry
//----------------------------------------------------------------------//
FSmartObjectCollectionEntry::FSmartObjectCollectionEntry(const FSmartObjectID& SmartObjectID, const USmartObjectComponent& SmartObjectComponent)
: ID(SmartObjectID)
, Path(&SmartObjectComponent)
{
}
USmartObjectComponent* FSmartObjectCollectionEntry::GetComponent() const
{
return CastChecked<USmartObjectComponent>(Path.ResolveObject(), ECastCheckedType::NullAllowed);
}
FString FSmartObjectCollectionEntry::Describe() const
{
return FString::Printf(TEXT("%s - %s"), *Path.ToString(), *ID.Describe());
}
//----------------------------------------------------------------------//
// ASmartObjectCollection
//----------------------------------------------------------------------//
ASmartObjectCollection::ASmartObjectCollection(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = false;
bNetLoadOnClient = false;
SetCanBeDamaged(false);
}
void ASmartObjectCollection::PostLoad()
{
// Handle Level load, PIE, SIE, game load, streaming.
Super::PostLoad();
RegisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
void ASmartObjectCollection::Destroyed()
{
// Handle editor delete.
UnregisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
Super::Destroyed();
}
void ASmartObjectCollection::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
// Handle Level unload, PIE end, SIE end, game end.
UnregisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
Super::EndPlay(EndPlayReason);
}
void ASmartObjectCollection::PostActorCreated()
{
// Register after being initially spawned.
Super::PostActorCreated();
RegisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
void ASmartObjectCollection::PreRegisterAllComponents()
{
Super::PreRegisterAllComponents();
// Handle UWorld::AddToWorld(), i.e. turning on level visibility
if (const ULevel* Level = GetLevel())
{
// This function gets called in editor all the time, we're only interested the case where level is being added to world.
if (Level->bIsAssociatingLevel)
{
RegisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
}
}
void ASmartObjectCollection::PostUnregisterAllComponents()
{
// Handle UWorld::RemoveFromWorld(), i.e. turning off level visibility
if (const ULevel* Level = GetLevel())
{
// This function gets called in editor all the time, we're only interested the case where level is being removed from world.
if (Level->bIsDisassociatingLevel)
{
UnregisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
}
Super::PostUnregisterAllComponents();
}
bool ASmartObjectCollection::RegisterWithSubsystem(FString Context)
{
if (bRegistered)
{
UE_VLOG_UELOG(this, LogSmartObject, Error, TEXT("\'%s\' (0x%llx) %s - Failed: already registered"), *GetName(), UPTRINT(this), *Context);
return false;
}
if (HasAnyFlags(RF_ClassDefaultObject))
{
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) %s - Failed: ignoring default object"), *GetName(), UPTRINT(this), *Context);
return false;
}
USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(GetWorld());
if (SmartObjectSubsystem == nullptr)
{
// Collection might attempt to register before the subsystem is created but at creation it will gather all collection
// and register them. For this reason we use a log instead of an error
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) %s - Failed: unable to find smart object subsystem"), *GetName(), UPTRINT(this), *Context);
return false;
}
SmartObjectSubsystem->RegisterCollection(*this);
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) %s - Succeeded"), *GetName(), UPTRINT(this), *Context);
return true;
}
bool ASmartObjectCollection::UnregisterWithSubsystem(FString Context)
{
if (!bRegistered)
{
UE_VLOG_UELOG(this, LogSmartObject, Error, TEXT("\'%s\' (0x%llx) %s - Failed: not registered"), *GetName(), UPTRINT(this), *Context);
return false;
}
USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(GetWorld());
if (SmartObjectSubsystem == nullptr)
{
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) %s - Failed: unable to find smart object subsystem"), *GetName(), UPTRINT(this), *Context);
return false;
}
SmartObjectSubsystem->UnregisterCollection(*this);
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) %s - Succeeded"), *GetName(), UPTRINT(this), *Context);
return true;
}
FSmartObjectID ASmartObjectCollection::AddSmartObject(USmartObjectComponent& SOComponent)
{
ensureMsgf(!SOComponent.GetWorld()->IsGameWorld(),
TEXT("Registration can't happen at runtime for loaded entities; they should be in the initial collection"));
const FSoftObjectPath Path(&SOComponent);
FSmartObjectID ID(GetTypeHash(Path));
UE_VLOG_UELOG(this, LogSmartObject, Verbose, TEXT("Adding SmartObject %s [%s] to collection"), *SOComponent.GetName(), *ID.Describe());
SOComponent.SetRegisteredID(ID);
CollectionEntries.Emplace(ID, SOComponent);
RegisteredIdToObjectMap.Add(ID, Path);
return ID;
}
void ASmartObjectCollection::RemoveSmartObject(USmartObjectComponent& SOComponent)
{
ensureMsgf(!SOComponent.GetWorld()->IsGameWorld(),
TEXT("Registration can't happen at runtime for loaded components; they should be in the initial collection"));
FSmartObjectID ID = SOComponent.GetRegisteredID();
if (!ID.IsValid())
{
return;
}
UE_VLOG_UELOG(this, LogSmartObject, Verbose, TEXT("Removing SmartObject %s [%s] from collection"), *SOComponent.GetName(), *ID.Describe());
const int32 Index = CollectionEntries.IndexOfByPredicate(
[&ID](const FSmartObjectCollectionEntry& Entry)
{
return Entry.GetID() == ID;
});
if (Index != INDEX_NONE)
{
CollectionEntries.RemoveAt(Index);
RegisteredIdToObjectMap.Remove(ID);
}
SOComponent.SetRegisteredID(FSmartObjectID::Invalid);
}
USmartObjectComponent* ASmartObjectCollection::GetSmartObjectComponent(const FSmartObjectID& SmartObjectID) const
{
// @todo SO: see if the map worth it or a search in the array can be enough
const FSoftObjectPath* Path = RegisteredIdToObjectMap.Find(SmartObjectID);
return Path != nullptr ? CastChecked<USmartObjectComponent>(Path->ResolveObject(), ECastCheckedType::NullAllowed) : nullptr;
}
TConstArrayView<FSmartObjectCollectionEntry> ASmartObjectCollection::GetEntries() const
{
return CollectionEntries;
}
void ASmartObjectCollection::OnRegistered()
{
bRegistered = true;
for (const FSmartObjectCollectionEntry& Entry : CollectionEntries)
{
RegisteredIdToObjectMap.Add(Entry.ID, Entry.Path);
}
}
void ASmartObjectCollection::OnUnregistered()
{
bRegistered = false;
}
#if WITH_EDITOR
void ASmartObjectCollection::PostEditUndo()
{
Super::PostEditUndo();
if (IsPendingKillPending())
{
UnregisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
else
{
RegisterWithSubsystem(ANSI_TO_TCHAR(__FUNCTION__));
}
}
void ASmartObjectCollection::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
const FName PropName = PropertyChangedEvent.Property->GetFName();
if (PropName == GET_MEMBER_NAME_CHECKED(ASmartObjectCollection, bBuildOnDemand))
{
if (!bBuildOnDemand)
{
RebuildCollection();
}
}
}
}
void ASmartObjectCollection::RebuildCollection()
{
if (USmartObjectSubsystem* SmartObjectSubsystem = USmartObjectSubsystem::GetCurrent(GetWorld()))
{
SmartObjectSubsystem->RebuildCollection(*this);
}
}
void ASmartObjectCollection::RebuildCollection(TConstArrayView<USmartObjectComponent*> Components)
{
UE_VLOG_UELOG(this, LogSmartObject, Log, TEXT("\'%s\' (0x%llx) Rebuilding collection"), *GetName(), UPTRINT(this));
CollectionEntries.Reset(Components.Num());
RegisteredIdToObjectMap.Empty(Components.Num());
for (USmartObjectComponent* const Component : Components)
{
if (Component != nullptr)
{
AddSmartObject(*Component);
}
}
}
#endif // WITH_EDITOR