LifePlay-Revival/Source/LifePlay/LPPersonPropGuardSubsystem.cpp

224 lines
6 KiB
C++
Raw Permalink Normal View History

// 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);
}