223 lines
6 KiB
C++
223 lines
6 KiB
C++
// Voir LPPersonPropGuardSubsystem.h.
|
|
|
|
#include "LPPersonPropGuardSubsystem.h"
|
|
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Engine/GameInstance.h"
|
|
#include "Engine/World.h"
|
|
#include "EngineUtils.h"
|
|
#include "LifePlay.h"
|
|
#include "UObject/UnrealType.h"
|
|
|
|
namespace LPPersonPropGuard
|
|
{
|
|
/** Props d'anim (spawnProp) : template NODE_AddStaticMeshComponent-0 — pas les SMC vetements *SM. */
|
|
bool IsAnimPropComponent(const UActorComponent* Comp)
|
|
{
|
|
if (!Comp)
|
|
{
|
|
return false;
|
|
}
|
|
const FString Name = Comp->GetName();
|
|
return Name.Contains(TEXT("NODE_AddStaticMeshComponent"), ESearchCase::CaseSensitive);
|
|
}
|
|
|
|
void DestroyProp(UStaticMeshComponent* SMC)
|
|
{
|
|
if (!IsValid(SMC))
|
|
{
|
|
return;
|
|
}
|
|
SMC->SetVisibility(false, false);
|
|
SMC->SetHiddenInGame(true, false);
|
|
SMC->SetStaticMesh(nullptr);
|
|
SMC->DestroyComponent();
|
|
}
|
|
|
|
FArrayProperty* PropsArray(AActor* Person)
|
|
{
|
|
return Person ? CastField<FArrayProperty>(Person->GetClass()->FindPropertyByName(TEXT("Props"))) : nullptr;
|
|
}
|
|
|
|
void ClearPropsArrayEntries(AActor* Person)
|
|
{
|
|
FArrayProperty* Arr = PropsArray(Person);
|
|
if (!Arr || !CastField<FObjectPropertyBase>(Arr->Inner))
|
|
{
|
|
return;
|
|
}
|
|
FScriptArrayHelper Helper(Arr, Arr->ContainerPtrToValuePtr<void>(Person));
|
|
for (int32 Index = Helper.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
UObject* Entry = *reinterpret_cast<UObject**>(Helper.GetRawPtr(Index));
|
|
if (UStaticMeshComponent* SMC = Cast<UStaticMeshComponent>(Entry))
|
|
{
|
|
DestroyProp(SMC);
|
|
}
|
|
Helper.RemoveValues(Index);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
TickHandle = FTSTicker::GetCoreTicker().AddTicker(
|
|
FTickerDelegate::CreateUObject(this, &ULPPersonPropGuardSubsystem::Tick), 0.2f);
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::Deinitialize()
|
|
{
|
|
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
bool ULPPersonPropGuardSubsystem::Tick(float DeltaTime)
|
|
{
|
|
(void)DeltaTime;
|
|
UGameInstance* GI = GetGameInstance();
|
|
UWorld* World = GI ? GI->GetWorld() : nullptr;
|
|
if (!World || !World->IsGameWorld())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (TActorIterator<AActor> It(World); It; ++It)
|
|
{
|
|
AActor* Candidate = *It;
|
|
if (!IsValid(Candidate) || Candidate->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
|
|
{
|
|
continue;
|
|
}
|
|
if (Candidate->GetClass()->GetName() != TEXT("Person_C"))
|
|
{
|
|
continue;
|
|
}
|
|
if (Candidate->GetGameInstance() != GI)
|
|
{
|
|
continue;
|
|
}
|
|
GuardPerson(Candidate);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
USkeletalMeshComponent* ULPPersonPropGuardSubsystem::GetPersonMesh(AActor* Person)
|
|
{
|
|
if (!Person)
|
|
{
|
|
return nullptr;
|
|
}
|
|
if (const FObjectPropertyBase* Prop = CastField<FObjectPropertyBase>(Person->GetClass()->FindPropertyByName(TEXT("Mesh"))))
|
|
{
|
|
if (USkeletalMeshComponent* FromProp = Cast<USkeletalMeshComponent>(Prop->GetObjectPropertyValue_InContainer(Person)))
|
|
{
|
|
return FromProp;
|
|
}
|
|
}
|
|
TInlineComponentArray<USkeletalMeshComponent*> Meshes(Person);
|
|
return Meshes.Num() > 0 ? Meshes[0] : nullptr;
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::CallClearProps(AActor* Person)
|
|
{
|
|
if (!Person)
|
|
{
|
|
return;
|
|
}
|
|
if (UFunction* Fn = Person->FindFunction(TEXT("clearProps")))
|
|
{
|
|
Person->ProcessEvent(Fn, nullptr);
|
|
}
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::ForceHidePropComponents(USkeletalMeshComponent* Mesh)
|
|
{
|
|
if (!IsValid(Mesh))
|
|
{
|
|
return;
|
|
}
|
|
TArray<USceneComponent*> Children;
|
|
Mesh->GetChildrenComponents(true, Children);
|
|
for (USceneComponent* Child : Children)
|
|
{
|
|
UStaticMeshComponent* SMC = Cast<UStaticMeshComponent>(Child);
|
|
if (!SMC || !LPPersonPropGuard::IsAnimPropComponent(SMC))
|
|
{
|
|
continue;
|
|
}
|
|
LPPersonPropGuard::DestroyProp(SMC);
|
|
}
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::ClearOrphanProps(AActor* Person, USkeletalMeshComponent* Mesh) // instance : log bLoggedOnce
|
|
{
|
|
FArrayProperty* Arr = LPPersonPropGuard::PropsArray(Person);
|
|
if (!Arr || !CastField<FObjectPropertyBase>(Arr->Inner))
|
|
{
|
|
return;
|
|
}
|
|
FScriptArrayHelper Helper(Arr, Arr->ContainerPtrToValuePtr<void>(Person));
|
|
bool bRemoved = false;
|
|
for (int32 Index = Helper.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
UObject* Entry = *reinterpret_cast<UObject**>(Helper.GetRawPtr(Index));
|
|
UStaticMeshComponent* SMC = Cast<UStaticMeshComponent>(Entry);
|
|
if (!IsValid(SMC))
|
|
{
|
|
Helper.RemoveValues(Index);
|
|
bRemoved = true;
|
|
continue;
|
|
}
|
|
|
|
// spawnProp attache toujours avec un SocketName ; socket None ou invalide = verre colle a l'origine du mesh.
|
|
const FName Socket = SMC->GetAttachSocketName();
|
|
const bool bBadParent = SMC->GetAttachParent() != Mesh;
|
|
const bool bBadSocket = Socket.IsNone() || (Mesh && !Mesh->DoesSocketExist(Socket));
|
|
if (!bBadParent && !bBadSocket)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LPPersonPropGuard::DestroyProp(SMC);
|
|
Helper.RemoveValues(Index);
|
|
bRemoved = true;
|
|
}
|
|
|
|
if (bRemoved && !bLoggedOnce)
|
|
{
|
|
bLoggedOnce = true;
|
|
UE_LOG(LogLifePlay, Display, TEXT("PersonPropGuard : props orphelines retirees (verre/animation sans socket)"));
|
|
}
|
|
}
|
|
|
|
void ULPPersonPropGuardSubsystem::GuardPerson(AActor* Person)
|
|
{
|
|
USkeletalMeshComponent* Mesh = GetPersonMesh(Person);
|
|
const bool bBodyGone = !IsValid(Mesh) || Mesh->GetSkeletalMeshAsset() == nullptr;
|
|
const bool bHidden = Person->IsHidden();
|
|
|
|
if (bHidden || bBodyGone)
|
|
{
|
|
if (!ClearedWhileInvalid.Contains(Person))
|
|
{
|
|
CallClearProps(Person);
|
|
// Ceinture : clearProps BP peut etre absent / Props desync ; ne touche pas aux SMC vetements.
|
|
LPPersonPropGuard::ClearPropsArrayEntries(Person);
|
|
ForceHidePropComponents(Mesh);
|
|
ClearedWhileInvalid.Add(Person);
|
|
if (!bLoggedOnce)
|
|
{
|
|
bLoggedOnce = true;
|
|
UE_LOG(LogLifePlay, Display,
|
|
TEXT("PersonPropGuard : clearProps (Person cache=%d meshVide=%d) — corrige verre orphelin"),
|
|
bHidden ? 1 : 0, bBodyGone ? 1 : 0);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
ClearedWhileInvalid.Remove(Person);
|
|
ClearOrphanProps(Person, Mesh);
|
|
}
|