Game Framework Extensions:�Technical Deep Dive
Unreal Fest Gold Coast 2024
Andrew Joy
2Bit Studios
Content
UPocketWorld
The power of UWorld in the palm of your hand.
AAnyMeshActor
Static or skeletal - why not both?
RepHelpers.h
Iris + Push Model + Net Dormancy, but easy.
UPocketWorld
�[Engine]
// Manage actors
this->GetWorld()->SpawnActor(...);
// Query physics
this->GetWorld()->LineTraceSingleByChannel(...);
// Host replication
this->GetWorld()->GetNetDriver();
UWorld
Game
UWorld
Game
Editor
UWorld
Game
Editor
Loading
UWorld
Game
Editor
Loading
Lobby
UWorld
Game
Editor
Loading
Scene Capture
Lobby
UWorld
Game
Editor
Loading
Scene Capture
Lobby
Mini
Game
UWorld
Scene Capture
Mini
Game
UPocketWorld
Use Case
Use Case
Use Case
Use Case
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
FName const InnerPackageName = *Template.GetLongPackageName();
FName const OuterPackageName = MakeUniqueObjectName(nullptr, UPackage::StaticClass(), InnerPackageName);
UPackage* const OuterPackage = CreatePackage(*OuterPackageName.ToString());
OuterPackage->SetFlags(EObjectFlags::RF_Transient);
UWorld::WorldTypePreLoadMap.FindOrAdd(OuterPackageName) = EWorldType::GamePreview;
UPackage* const InnerPackage = LoadPackage(OuterPackage, *InnerPackageName.ToString(), LOAD_None);
UWorld::WorldTypePreLoadMap.Remove(OuterPackageName);
UWorld* NewWorld = UWorld::FindWorldInPackage(InnerPackage);
if (NewWorld == nullptr)
{
NewWorld = UWorld::FollowWorldRedirectorInPackage(InnerPackage);
}
...
}
1) Duplicate World Package
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
NewWorld->InitWorld(Init);
// NewWorld->SetGameInstance(...);
// NewWorld->SetGameMode(NewWorld->URL);
NewWorld->InitializeActorsForPlay(NewWorld->URL);
NewWorld->BeginPlay();
NewWorld->bWorldWasLoadedThisTick = true;
USkyLightComponent::UpdateSkyCaptureContents(NewWorld);
...
}
2) Initialize For Play
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
NewWorld->InitWorld(Init);
// NewWorld->SetGameInstance(...);
// NewWorld->SetGameMode(NewWorld->URL);
NewWorld->InitializeActorsForPlay(NewWorld->URL);
NewWorld->BeginPlay();
NewWorld->bWorldWasLoadedThisTick = true;
USkyLightComponent::UpdateSkyCaptureContents(NewWorld);
...
}
2) Initialize For Play
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
NewWorld->InitWorld(Init);
// NewWorld->SetGameInstance(...);
// NewWorld->SetGameMode(NewWorld->URL);
NewWorld->InitializeActorsForPlay(NewWorld->URL);
NewWorld->BeginPlay();
NewWorld->bWorldWasLoadedThisTick = true;
USkyLightComponent::UpdateSkyCaptureContents(NewWorld);
...
}
2) Initialize For Play
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
NewWorld->InitWorld(Init);
// NewWorld->SetGameInstance(...);
// NewWorld->SetGameMode(NewWorld->URL);
NewWorld->InitializeActorsForPlay(NewWorld->URL);
NewWorld->BeginPlay();
NewWorld->bWorldWasLoadedThisTick = true;
USkyLightComponent::UpdateSkyCaptureContents(NewWorld);
...
}
2) Initialize For Play
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
NewWorld->InitWorld(Init);
// NewWorld->SetGameInstance(...);
// NewWorld->SetGameMode(NewWorld->URL);
NewWorld->InitializeActorsForPlay(NewWorld->URL);
NewWorld->BeginPlay();
NewWorld->bWorldWasLoadedThisTick = true;
USkyLightComponent::UpdateSkyCaptureContents(NewWorld);
...
}
2) Initialize For Play
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
// Reverse engineered from UnrealEngine.cpp::CreatePIEWorldByLoadingFromPackage
// 1) Load world into duplicate package
// 2) Initialize world for play
// 3) Register world with engine and prepare for cleanup
}
Overview
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
FWorldContext& NewWorldContext = GEngine->CreateNewWorldContext(EWorldType::Editor);
NewWorldContext.SetCurrentWorld(NewWorld);
NewWorldContext.OwningGameInstance = NewWorld->GetGameInstance();
FWorldDelegates::OnWorldCleanup.AddWeakLambda(
NewWorld,
[NewWorld](UWorld* World, bool bSessionEnded, bool bCleanupResources)
{
if (NewWorld == World)
{
GEngine->DestroyWorldContext(World);
}
});
// Clean up with NewWorld->DestroyWorld(true);
return NewWorld;
}
3) Context & Cleanup
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
FWorldContext& NewWorldContext = GEngine->CreateNewWorldContext(EWorldType::Editor);
NewWorldContext.SetCurrentWorld(NewWorld);
NewWorldContext.OwningGameInstance = NewWorld->GetGameInstance();
FWorldDelegates::OnWorldCleanup.AddWeakLambda(
NewWorld,
[NewWorld](UWorld* World, bool bSessionEnded, bool bCleanupResources)
{
if (NewWorld == World)
{
GEngine->DestroyWorldContext(World);
}
});
// Clean up with NewWorld->DestroyWorld(true);
return NewWorld;
}
3) Context & Cleanup
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
FWorldContext& NewWorldContext = GEngine->CreateNewWorldContext(EWorldType::Editor);
NewWorldContext.SetCurrentWorld(NewWorld);
NewWorldContext.OwningGameInstance = NewWorld->GetGameInstance();
FWorldDelegates::OnWorldCleanup.AddWeakLambda(
NewWorld,
[NewWorld](UWorld* World, bool bSessionEnded, bool bCleanupResources)
{
if (NewWorld == World)
{
GEngine->DestroyWorldContext(World);
}
});
// Clean up with NewWorld->DestroyWorld(true);
return NewWorld;
}
3) Context & Cleanup
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
FWorldContext& NewWorldContext = GEngine->CreateNewWorldContext(EWorldType::Editor);
NewWorldContext.SetCurrentWorld(NewWorld);
NewWorldContext.OwningGameInstance = NewWorld->GetGameInstance();
FWorldDelegates::OnWorldCleanup.AddWeakLambda(
NewWorld,
[NewWorld](UWorld* World, bool bSessionEnded, bool bCleanupResources)
{
if (NewWorld == World)
{
GEngine->DestroyWorldContext(World);
}
});
// Clean up with NewWorld->DestroyWorld(true);
return NewWorld;
}
3) Context & Cleanup
UWorld* UPocketWorld::Create(TSoftObjectPtr<UWorld> const& Template, FWorldInitializationValues const& Init)
{
...
FWorldContext& NewWorldContext = GEngine->CreateNewWorldContext(EWorldType::Editor);
NewWorldContext.SetCurrentWorld(NewWorld);
NewWorldContext.OwningGameInstance = NewWorld->GetGameInstance();
FWorldDelegates::OnWorldCleanup.AddWeakLambda(
NewWorld,
[NewWorld](UWorld* World, bool bSessionEnded, bool bCleanupResources)
{
if (NewWorld == World)
{
GEngine->DestroyWorldContext(World);
}
});
// Clean up with NewWorld->DestroyWorld(true);
return NewWorld;
}
3) Context & Cleanup
Use Case
USceneCaptureComponent2D* UIconGenerator::CreateSceneWithCapture(TSoftObjectPtr<class UWorld> World)
{
UWorld* const Scene = UPocketWorld::Create(World);
AActor* const Actor = Scene->SpawnActor<AActor>();
#if WITH_EDITOR
Actor->SetActorLabel("Scene Capture");
#endif
return Cast<USceneCaptureComponent2D>(
Actor->AddComponentByClass(
USceneCaptureComponent2D::StaticClass(),
false,
FTransform::Identity,
false));
}
Use Case
USceneCaptureComponent2D* UIconGenerator::CreateSceneWithCapture(TSoftObjectPtr<class UWorld> World)
{
UWorld* const Scene = UPocketWorld::Create(World);
AActor* const Actor = Scene->SpawnActor<AActor>();
#if WITH_EDITOR
Actor->SetActorLabel("Scene Capture");
#endif
return Cast<USceneCaptureComponent2D>(
Actor->AddComponentByClass(
USceneCaptureComponent2D::StaticClass(),
false,
FTransform::Identity,
false));
}
Use Case
USceneCaptureComponent2D* UIconGenerator::CreateSceneWithCapture(TSoftObjectPtr<class UWorld> World)
{
UWorld* const Scene = UPocketWorld::Create(World);
AActor* const Actor = Scene->SpawnActor<AActor>();
#if WITH_EDITOR
Actor->SetActorLabel("Scene Capture");
#endif
return Cast<USceneCaptureComponent2D>(
Actor->AddComponentByClass(
USceneCaptureComponent2D::StaticClass(),
false,
FTransform::Identity,
false));
}
Use Case
USceneCaptureComponent2D* UIconGenerator::CreateSceneWithCapture(TSoftObjectPtr<class UWorld> World)
{
UWorld* const Scene = UPocketWorld::Create(World);
AActor* const Actor = Scene->SpawnActor<AActor>();
#if WITH_EDITOR
Actor->SetActorLabel("Scene Capture");
#endif
return Cast<USceneCaptureComponent2D>(
Actor->AddComponentByClass(
USceneCaptureComponent2D::StaticClass(),
false,
FTransform::Identity,
false));
}
Use Case
Use Case
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Create Pocket World"))
static UWorld* UPocketWorld::Create(
TSoftObjectPtr<UWorld> Template,
bool bSimulatesPhysics = false,
bool bSupportsNavigation = false,
bool bSupportsAI = false,
bool bSupportsAudio = false)
{
return UPocketWorld::Create(
Template,
FWorldInitializationValues()
.SetTransactional(false)
.ShouldSimulatePhysics(bSimulatesPhysics)
.CreateNavigation(bSupportsNavigation)
.CreateAISystem(bSupportsAI)
.AllowAudioPlayback(bSupportsAudio)
.SetDefaultGameMode(AGameModeBase::StaticClass()));
}
Blueprint Support
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Create Pocket World"))
static UWorld* UPocketWorld::Create(
TSoftObjectPtr<UWorld> Template,
bool bSimulatesPhysics = false,
bool bSupportsNavigation = false,
bool bSupportsAI = false,
bool bSupportsAudio = false)
{
return UPocketWorld::Create(
Template,
FWorldInitializationValues()
.SetTransactional(false)
.ShouldSimulatePhysics(bSimulatesPhysics)
.CreateNavigation(bSupportsNavigation)
.CreateAISystem(bSupportsAI)
.AllowAudioPlayback(bSupportsAudio)
.SetDefaultGameMode(AGameModeBase::StaticClass()));
}
Blueprint Support
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Create Pocket World"))
static UWorld* UPocketWorld::Create(
TSoftObjectPtr<UWorld> Template,
bool bSimulatesPhysics = false,
bool bSupportsNavigation = false,
bool bSupportsAI = false,
bool bSupportsAudio = false)
{
return UPocketWorld::Create(
Template,
FWorldInitializationValues()
.SetTransactional(false)
.ShouldSimulatePhysics(bSimulatesPhysics)
.CreateNavigation(bSupportsNavigation)
.CreateAISystem(bSupportsAI)
.AllowAudioPlayback(bSupportsAudio)
.SetDefaultGameMode(AGameModeBase::StaticClass()));
}
Blueprint Support
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Create Pocket World"))
static UWorld* UPocketWorld::Create(
TSoftObjectPtr<UWorld> Template,
bool bSimulatesPhysics = false,
bool bSupportsNavigation = false,
bool bSupportsAI = false,
bool bSupportsAudio = false)
{
return UPocketWorld::Create(
Template,
FWorldInitializationValues()
.SetTransactional(false)
.ShouldSimulatePhysics(bSimulatesPhysics)
.CreateNavigation(bSupportsNavigation)
.CreateAISystem(bSupportsAI)
.AllowAudioPlayback(bSupportsAudio)
.SetDefaultGameMode(AGameModeBase::StaticClass()));
}
Blueprint Support
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Is In Pocket World", WorldContext = "Object"))
static bool UPocketWorld::Contains(UObject const* Object)
{
return Object != nullptr ? Object->GetWorld()->IsPreviewWorld() : false;
}
Feature Gating
UFUNCTION(BlueprintCallable, meta = (DisplayName = "Is In Pocket World", WorldContext = "Object"))
static bool UPocketWorld::Contains(UObject const* Object)
{
return Object != nullptr ? Object->GetWorld()->IsPreviewWorld() : false;
}
Feature Gating
UPocketWorld
�[Engine]
AAnyMeshActor
�[GameFramework]
AStaticMeshActor
UStaticMeshComponent
What?
AStaticMeshActor
UStaticMeshComponent
ASkeletalMeshActor
USkeletalMeshComponent
What?
AStaticMeshActor
UStaticMeshComponent
ASkeletalMeshActor
USkeletalMeshComponent
AAnyMeshActor
???
What?
Why?
AStaticMeshActor
UStaticMeshComponent
ASkeletalMeshActor
USkeletalMeshComponent
AAnyMeshActor
???
How?
Artist: Can I have AAnyMeshActor?
Mum: We have AAnyMeshActor at home.
AAnyMeshActor at home:
AStaticMeshActor
UStaticMeshComponent
ASkeletalMeshActor
USkeletalMeshComponent
AAnyMeshActor
UMeshComponent
How?
UCLASS()
class FOUNDATION_API AAnyMeshActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UMeshComponent> Mesh;
public:
FORCEINLINE UMeshComponent* GetMesh() const { return this->Mesh; }
AAnyMeshActor();
UFUNCTION(BlueprintCallable, Category = "Mesh")
UStreamableRenderAsset* GetMeshAsset() const;
UFUNCTION(BlueprintCallable, Category = "Mesh")
void SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass = nullptr);
UFUNCTION(BlueprintCallable, Category = "Mesh")
void CopyMesh(AActor* From);
};
AnyMeshActor.h
UCLASS()
class FOUNDATION_API AAnyMeshActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UMeshComponent> Mesh;
public:
FORCEINLINE UMeshComponent* GetMesh() const { return this->Mesh; }
AAnyMeshActor();
UFUNCTION(BlueprintCallable, Category = "Mesh")
UStreamableRenderAsset* GetMeshAsset() const;
UFUNCTION(BlueprintCallable, Category = "Mesh")
void SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass = nullptr);
UFUNCTION(BlueprintCallable, Category = "Mesh")
void CopyMesh(AActor* From);
};
AnyMeshActor.h
UCLASS()
class FOUNDATION_API AAnyMeshActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UMeshComponent> Mesh;
public:
FORCEINLINE UMeshComponent* GetMesh() const { return this->Mesh; }
AAnyMeshActor();
UFUNCTION(BlueprintCallable, Category = "Mesh")
UStreamableRenderAsset* GetMeshAsset() const;
UFUNCTION(BlueprintCallable, Category = "Mesh")
void SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass = nullptr);
UFUNCTION(BlueprintCallable, Category = "Mesh")
void CopyMesh(AActor* From);
};
AnyMeshActor.h
UCLASS()
class FOUNDATION_API AAnyMeshActor : public AActor
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components", meta = (AllowPrivateAccess = "true"))
TObjectPtr<UMeshComponent> Mesh;
public:
FORCEINLINE UMeshComponent* GetMesh() const { return this->Mesh; }
AAnyMeshActor();
UFUNCTION(BlueprintCallable, Category = "Mesh")
UStreamableRenderAsset* GetMeshAsset() const;
UFUNCTION(BlueprintCallable, Category = "Mesh")
void SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass = nullptr);
UFUNCTION(BlueprintCallable, Category = "Mesh")
void CopyMesh(AActor* From);
};
AnyMeshActor.h
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
ASlaughtermelonLandmark::ASlaughtermelonLandmark()
{
static const struct
{
ConstructorHelpers::FObjectFinder<USkeletalMesh> Mesh = TEXT("...");
ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance = TEXT("...");
} Assets;
this->SetMesh(Assets.Mesh.Object);
this->GetMesh()->SetReceivesDecals(true);
this->GetMesh<USkeletalMeshComponent>()->SetAnimInstanceClass(Assets.AnimInstance.Class);
}
AFenceBuilding::AFenceBuilding()
{
this->SetMesh(nullptr, USplineMeshComponent::StaticClass());
this->GetMesh()->SetMobility(EComponentMobility::Movable);
this->GetMesh()->SetCollisionProfileName("BlockAllDynamic");
this->GetMesh<USplineMeshComponent>()->ForwardAxis = ESplineMeshAxis::Y;
}
Use Cases
AFenceBuilding
Why?
ASlaughtermelonLandmark
AFenceBuilding
Why?
ASlaughtermelonLandmark
ALandmarkEntity
AFenceBuilding
Why?
ASlaughtermelonLandmark
ABuildingEntity
ALandmarkEntity
AFenceBuilding
Why?
ASlaughtermelonLandmark
ABuildingEntity
ALandmarkEntity
AEntityActor
AFenceBuilding
Why?
ABuildingEntity
ASlaughtermelonLandmark
ALandmarkEntity
AEntityActor
AAnyMeshActor
AEntityActor::AEntityActor()
{
this->Root = this->CreateDefaultSubobject<USceneComponent>("Root");
this->RootComponent = this->Root;
this->SetMesh(nullptr, UStaticMeshComponent::StaticClass());
this->GetMesh()->SetReceivesDecals(false);
this->GetMesh()->SetMobility(EComponentMobility::Static);
this->GetMesh()->SetupAttachment(this->Root);
}
Use Cases
AEntityActor::AEntityActor()
{
this->Root = this->CreateDefaultSubobject<USceneComponent>("Root");
this->RootComponent = this->Root;
this->SetMesh(nullptr, UStaticMeshComponent::StaticClass());
this->GetMesh()->SetReceivesDecals(false);
this->GetMesh()->SetMobility(EComponentMobility::Static);
this->GetMesh()->SetupAttachment(this->Root);
}
Use Cases
AEntityActor::AEntityActor()
{
this->Root = this->CreateDefaultSubobject<USceneComponent>("Root");
this->RootComponent = this->Root;
this->SetMesh(nullptr, UStaticMeshComponent::StaticClass());
this->GetMesh()->SetReceivesDecals(false);
this->GetMesh()->SetMobility(EComponentMobility::Static);
this->GetMesh()->SetupAttachment(this->Root);
}
Use Cases
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
if (this->Mesh == nullptr || !this->Mesh->IsA(OverrideClass))
{
if (FUObjectThreadContext::Get().IsInConstructor)
{
NewMesh = (UMeshComponent*)this->CreateDefaultSubobject(
OverrideClass->GetName(),
UMeshComponent::StaticClass(),
OverrideClass,
true,
false);
}
else
{
NewMesh = NewObject<UMeshComponent>(this, OverrideClass);
}
}
...
}
2) Create Component
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
if (this->Mesh == nullptr || !this->Mesh->IsA(OverrideClass))
{
if (FUObjectThreadContext::Get().IsInConstructor)
{
NewMesh = (UMeshComponent*)this->CreateDefaultSubobject(
OverrideClass->GetName(),
UMeshComponent::StaticClass(),
OverrideClass,
true,
false);
}
else
{
NewMesh = NewObject<UMeshComponent>(this, OverrideClass);
}
}
...
}
2) Create Component
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
if (this->Mesh == nullptr || !this->Mesh->IsA(OverrideClass))
{
if (FUObjectThreadContext::Get().IsInConstructor)
{
NewMesh = (UMeshComponent*)this->CreateDefaultSubobject(
OverrideClass->GetName(),
UMeshComponent::StaticClass(),
OverrideClass,
true,
false);
}
else
{
NewMesh = NewObject<UMeshComponent>(this, OverrideClass);
}
}
...
}
2) Create Component
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
if (this->Mesh == nullptr || !this->Mesh->IsA(OverrideClass))
{
if (FUObjectThreadContext::Get().IsInConstructor)
{
NewMesh = (UMeshComponent*)this->CreateDefaultSubobject(
OverrideClass->GetName(),
UMeshComponent::StaticClass(),
OverrideClass,
true,
false);
}
else
{
NewMesh = NewObject<UMeshComponent>(this, OverrideClass);
}
}
...
}
2) Create Component
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
if (this->Mesh == nullptr || !this->Mesh->IsA(OverrideClass))
{
if (FUObjectThreadContext::Get().IsInConstructor)
{
NewMesh = (UMeshComponent*)this->CreateDefaultSubobject(
OverrideClass->GetName(),
UMeshComponent::StaticClass(),
OverrideClass,
true,
false);
}
else
{
NewMesh = NewObject<UMeshComponent>(this, OverrideClass);
}
}
...
}
2) Create Component
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
UObject const* const OldMeshCDO = this->Mesh->GetClass()->GetDefaultObject();
for (
FProperty const* Property = UMeshComponent::StaticClass()->PropertyLink;
Property;
Property = Property->PropertyLinkNext)
{
uint8 const* const DefaultData = Property->ContainerPtrToValuePtr<uint8>(OldMeshCDO);
uint8 const* const SrcData = Property->ContainerPtrToValuePtr<uint8>(this->Mesh);
if (!Property->Identical(DefaultData, SrcData))
{
uint8* const DestData = Property->ContainerPtrToValuePtr<uint8>(NewMesh);
Property->CopyCompleteValue(DestData, SrcData);
}
}
...
}
3) Copy Properties
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
UObject const* const OldMeshCDO = this->Mesh->GetClass()->GetDefaultObject();
for (
FProperty const* Property = UMeshComponent::StaticClass()->PropertyLink;
Property;
Property = Property->PropertyLinkNext)
{
uint8 const* const DefaultData = Property->ContainerPtrToValuePtr<uint8>(OldMeshCDO);
uint8 const* const SrcData = Property->ContainerPtrToValuePtr<uint8>(this->Mesh);
if (!Property->Identical(DefaultData, SrcData))
{
uint8* const DestData = Property->ContainerPtrToValuePtr<uint8>(NewMesh);
Property->CopyCompleteValue(DestData, SrcData);
}
}
...
}
3) Copy Properties
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
UObject const* const OldMeshCDO = this->Mesh->GetClass()->GetDefaultObject();
for (
FProperty const* Property = UMeshComponent::StaticClass()->PropertyLink;
Property;
Property = Property->PropertyLinkNext)
{
uint8 const* const DefaultData = Property->ContainerPtrToValuePtr<uint8>(OldMeshCDO);
uint8 const* const SrcData = Property->ContainerPtrToValuePtr<uint8>(this->Mesh);
if (!Property->Identical(DefaultData, SrcData))
{
uint8* const DestData = Property->ContainerPtrToValuePtr<uint8>(NewMesh);
Property->CopyCompleteValue(DestData, SrcData);
}
}
...
}
3) Copy Properties
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
UObject const* const OldMeshCDO = this->Mesh->GetClass()->GetDefaultObject();
for (
FProperty const* Property = UMeshComponent::StaticClass()->PropertyLink;
Property;
Property = Property->PropertyLinkNext)
{
uint8 const* const DefaultData = Property->ContainerPtrToValuePtr<uint8>(OldMeshCDO);
uint8 const* const SrcData = Property->ContainerPtrToValuePtr<uint8>(this->Mesh);
if (!Property->Identical(DefaultData, SrcData))
{
uint8* const DestData = Property->ContainerPtrToValuePtr<uint8>(NewMesh);
Property->CopyCompleteValue(DestData, SrcData);
}
}
...
}
3) Copy Properties
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
UObject const* const OldMeshCDO = this->Mesh->GetClass()->GetDefaultObject();
for (
FProperty const* Property = UMeshComponent::StaticClass()->PropertyLink;
Property;
Property = Property->PropertyLinkNext)
{
uint8 const* const DefaultData = Property->ContainerPtrToValuePtr<uint8>(OldMeshCDO);
uint8 const* const SrcData = Property->ContainerPtrToValuePtr<uint8>(this->Mesh);
if (!Property->Identical(DefaultData, SrcData))
{
uint8* const DestData = Property->ContainerPtrToValuePtr<uint8>(NewMesh);
Property->CopyCompleteValue(DestData, SrcData);
}
}
...
}
3) Copy Properties
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
TArray<USceneComponent*> MeshChildren;
this->Mesh->GetChildrenComponents(false, MeshChildren);
ForEachObjectWithOuter(this, [this, &MeshChildren](UObject* Object)
{
if (Object->IsDefaultSubobject())
{
if (USceneComponent* const Component = Cast<USceneComponent>(Object))
{
if (Component->GetAttachParent() == this->Mesh)
{
MeshChildren.AddUnique(Component);
}
}
}
}, false);
...
}
4) Gather Children
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
TArray<USceneComponent*> MeshChildren;
this->Mesh->GetChildrenComponents(false, MeshChildren);
ForEachObjectWithOuter(this, [this, &MeshChildren](UObject* Object)
{
if (Object->IsDefaultSubobject())
{
if (USceneComponent* const Component = Cast<USceneComponent>(Object))
{
if (Component->GetAttachParent() == this->Mesh)
{
MeshChildren.AddUnique(Component);
}
}
}
}, false);
...
}
4) Gather Children
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
...
TArray<USceneComponent*> MeshChildren;
this->Mesh->GetChildrenComponents(false, MeshChildren);
ForEachObjectWithOuter(this, [this, &MeshChildren](UObject* Object)
{
if (Object->IsDefaultSubobject())
{
if (USceneComponent* const Component = Cast<USceneComponent>(Object))
{
if (Component->GetAttachParent() == this->Mesh)
{
MeshChildren.AddUnique(Component);
}
}
}
}, false);
...
}
4) Gather Children
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(UStreamableRenderAsset* NewMeshAsset, TSubclassOf<UMeshComponent> OverrideClass)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
Overview
void AAnyMeshActor::SetMesh(...)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
CopyMesh()
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
CopyMesh()
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) ???
// 2) ???
// 3) ???
// 4) ???
// 5) ???
// 6) ???
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) Create new component
// 2) Copy component properties
// 3) Gather children
// 4) Repeat recursively
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) Create new component
// 2) Copy component properties
// 3) Gather children
// 4) Repeat recursively
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) Create new component
// 2) Copy component properties
// 3) Gather children
// 4) Repeat recursively
}
CopyMesh()
Slaughtermelon
Skeletal
Spikes
Static
TNT
Static
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) Create new component
// 2) Copy component properties
// 3) Gather children
// 4) Repeat recursively
}
CopyMesh()
void AAnyMeshActor::SetMesh(...)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
void AAnyMeshActor::CopyMesh(AActor* From)
{
// 1) Create new component
// 2) Copy component properties
// 3) Gather children
// 4) Repeat recursively
}
CopyMesh()
void AAnyMeshActor::SetMesh(...)
{
// 1) Sanitize `OverrideClass`
// 2) Create new component
// 3) Copy component properties
// 4) Gather children
// 5) Reattach children
// 6) Destroy old component
}
...
if (UBlueprintGeneratedClass* const FromBlueprintLeaf = Cast<UBlueprintGeneratedClass>(From->GetClass()))
{
TArray<UBlueprintGeneratedClass const*> FromBlueprints;
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(FromBlueprintLeaf, FromBlueprints);
for (UBlueprintGeneratedClass const* const FromBlueprint : FromBlueprints)
{
if (FromBlueprint->SimpleConstructionScript)
{
for (USCS_Node* const CDONode : FromBlueprint->SimpleConstructionScript->GetRootNodes())
{
SearchNode(CDONode);
}
}
}
}
...
CopyMesh()
...
if (UBlueprintGeneratedClass* const FromBlueprintLeaf = Cast<UBlueprintGeneratedClass>(From->GetClass()))
{
TArray<UBlueprintGeneratedClass const*> FromBlueprints;
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(FromBlueprintLeaf, FromBlueprints);
for (UBlueprintGeneratedClass const* const FromBlueprint : FromBlueprints)
{
if (FromBlueprint->SimpleConstructionScript)
{
for (USCS_Node* const CDONode : FromBlueprint->SimpleConstructionScript->GetRootNodes())
{
SearchNode(CDONode);
}
}
}
}
...
CopyMesh()
...
if (UBlueprintGeneratedClass* const FromBlueprintLeaf = Cast<UBlueprintGeneratedClass>(From->GetClass()))
{
TArray<UBlueprintGeneratedClass const*> FromBlueprints;
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(FromBlueprintLeaf, FromBlueprints);
for (UBlueprintGeneratedClass const* const FromBlueprint : FromBlueprints)
{
if (FromBlueprint->SimpleConstructionScript)
{
for (USCS_Node* const CDONode : FromBlueprint->SimpleConstructionScript->GetRootNodes())
{
SearchNode(CDONode);
}
}
}
}
...
CopyMesh()
...
if (UBlueprintGeneratedClass* const FromBlueprintLeaf = Cast<UBlueprintGeneratedClass>(From->GetClass()))
{
TArray<UBlueprintGeneratedClass const*> FromBlueprints;
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(FromBlueprintLeaf, FromBlueprints);
for (UBlueprintGeneratedClass const* const FromBlueprint : FromBlueprints)
{
if (FromBlueprint->SimpleConstructionScript)
{
for (USCS_Node* const CDONode : FromBlueprint->SimpleConstructionScript->GetRootNodes())
{
SearchNode(CDONode);
}
}
}
}
...
CopyMesh()
...
if (UBlueprintGeneratedClass* const FromBlueprintLeaf = Cast<UBlueprintGeneratedClass>(From->GetClass()))
{
TArray<UBlueprintGeneratedClass const*> FromBlueprints;
UBlueprintGeneratedClass::GetGeneratedClassesHierarchy(FromBlueprintLeaf, FromBlueprints);
for (UBlueprintGeneratedClass const* const FromBlueprint : FromBlueprints)
{
if (FromBlueprint->SimpleConstructionScript)
{
for (USCS_Node* const CDONode : FromBlueprint->SimpleConstructionScript->GetRootNodes())
{
SearchNode(CDONode);
}
}
}
}
...
CopyMesh()
TFunction<USceneComponent*(USCS_Node*)> const SearchNode = [&](USCS_Node* Node) -> USceneComponent*
{
if (USceneComponent* ChildComponent = Cast<USceneComponent>(Node->GetActualComponentTemplate(FromBlueprint)))
{
FromChildren.Add(ChildComponent);
if (USceneComponent* const ParentComponent = Node->GetParentComponentTemplate(FromBlueprint))
{
ChildComponent->SetupAttachment(ParentComponent);
}
for (USCS_Node* const ChildNode : Node->ChildNodes)
{
if (USceneComponent* const NodeChildComponent = SearchNode(ChildNode))
{
NodeChildComponent->SetupAttachment(ChildComponent);
}
}
return ChildComponent;
}
return nullptr;
};
CopyMesh()
TFunction<USceneComponent*(USCS_Node*)> const SearchNode = [&](USCS_Node* Node) -> USceneComponent*
{
if (USceneComponent* ChildComponent = Cast<USceneComponent>(Node->GetActualComponentTemplate(FromBlueprint)))
{
FromChildren.Add(ChildComponent);
if (USceneComponent* const ParentComponent = Node->GetParentComponentTemplate(FromBlueprint))
{
ChildComponent->SetupAttachment(ParentComponent);
}
for (USCS_Node* const ChildNode : Node->ChildNodes)
{
if (USceneComponent* const NodeChildComponent = SearchNode(ChildNode))
{
NodeChildComponent->SetupAttachment(ChildComponent);
}
}
return ChildComponent;
}
return nullptr;
};
CopyMesh()
TFunction<USceneComponent*(USCS_Node*)> const SearchNode = [&](USCS_Node* Node) -> USceneComponent*
{
if (USceneComponent* ChildComponent = Cast<USceneComponent>(Node->GetActualComponentTemplate(FromBlueprint)))
{
FromChildren.Add(ChildComponent);
if (USceneComponent* const ParentComponent = Node->GetParentComponentTemplate(FromBlueprint))
{
ChildComponent->SetupAttachment(ParentComponent);
}
for (USCS_Node* const ChildNode : Node->ChildNodes)
{
if (USceneComponent* const NodeChildComponent = SearchNode(ChildNode))
{
NodeChildComponent->SetupAttachment(ChildComponent);
}
}
return ChildComponent;
}
return nullptr;
};
CopyMesh()
TFunction<USceneComponent*(USCS_Node*)> const SearchNode = [&](USCS_Node* Node) -> USceneComponent*
{
if (USceneComponent* ChildComponent = Cast<USceneComponent>(Node->GetActualComponentTemplate(FromBlueprint)))
{
FromChildren.Add(ChildComponent);
if (USceneComponent* const ParentComponent = Node->GetParentComponentTemplate(FromBlueprint))
{
ChildComponent->SetupAttachment(ParentComponent);
}
for (USCS_Node* const ChildNode : Node->ChildNodes)
{
if (USceneComponent* const NodeChildComponent = SearchNode(ChildNode))
{
NodeChildComponent->SetupAttachment(ChildComponent);
}
}
return ChildComponent;
}
return nullptr;
};
CopyMesh()
TFunction<USceneComponent*(USCS_Node*)> const SearchNode = [&](USCS_Node* Node) -> USceneComponent*
{
if (USceneComponent* ChildComponent = Cast<USceneComponent>(Node->GetActualComponentTemplate(FromBlueprint)))
{
FromChildren.Add(ChildComponent);
if (USceneComponent* const ParentComponent = Node->GetParentComponentTemplate(FromBlueprint))
{
ChildComponent->SetupAttachment(ParentComponent);
}
for (USCS_Node* const ChildNode : Node->ChildNodes)
{
if (USceneComponent* const NodeChildComponent = SearchNode(ChildNode))
{
NodeChildComponent->SetupAttachment(ChildComponent);
}
}
return ChildComponent;
}
return nullptr;
};
CopyMesh()
AAnyMeshActor
�[GameFramework]
RepHelpers.h
�[Iris]
ASlaughtermelon
bIsArmed
UScanAction
ScanTarget ScanProgress
ACrop
GrowthStage GrowthProgress
NetTick
ASlaughtermelon
bIsArmed
UScanAction
ScanTarget ScanProgress
ACrop
GrowthStage GrowthProgress
Push Model
ASlaughtermelon
bIsArmed
UScanAction
ScanTarget ScanProgress
ACrop
GrowthStage GrowthProgress
Push Model
void AActor::SetHidden(bool bInHidden)
{
bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
}
Push Model
void AActor::SetHidden(bool bInHidden)
{
bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
}
Push Model
ASlaughtermelon
bIsArmed
UScanAction
ScanTarget ScanProgress
ACrop
GrowthStage GrowthProgress
Net Dormancy
ASlaughtermelon
UScanAction
ScanTarget ScanProgress
ACrop
Net Dormancy
[SystemSettings]
net.PushModelSkipUndirtiedReplication=1
Net Dormancy
AMyActor::AMyActor()
{
this->bReplicates = true;
this->NetDormancy = ENetDormancy::DORM_DormantAll;
}
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->FlushNetDormancy();
this->SetHidden(true);
}
Net Dormancy
AMyActor::AMyActor()
{
this->bReplicates = true;
this->NetDormancy = ENetDormancy::DORM_DormantAll;
}
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->FlushNetDormancy();
this->SetHidden(true);
}
Net Dormancy
AMyActor::AMyActor()
{
this->bReplicates = true;
this->NetDormancy = ENetDormancy::DORM_DormantAll;
}
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->FlushNetDormancy();
this->SetHidden(true);
}
Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->FlushNetDormancy();
this->SetHidden(true);
}
void AActor::SetHidden(bool bInHidden)
{
this->bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
}
Push Model & Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->SetHidden(true);
}
void AActor::SetHidden(bool bInHidden)
{
this->FlushNetDormancy();
this->bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
}
Push Model & Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->SetHidden(true);
}
void AActor::SetHidden(bool bInHidden)
{
if (this->bHidden != bInHidden)
{
this->FlushNetDormancy();
this->bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
}
}
Push Model & Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->SetHidden(true);
}
void AActor::SetHidden(bool bInHidden)
{
if (this->bHidden != bInHidden)
{
this->FlushNetDormancy();
this->bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
this->OnRep_bHidden(!bInHidden);
}
}
Push Model & Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
this->SetHidden(true);
this->SetCanBeDamaged(false);
this->SetHasBeenClicked(true);
}
void AActor::SetHidden(bool bInHidden)
{
if (this->bHidden != bInHidden)
{
this->FlushNetDormancy();
this->bHidden = bInHidden;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
this->OnRep_bHidden(!bInHidden);
}
}
Push Model & Net Dormancy
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
REP_SET(bHidden, true);
REP_SET(bCanBeDamaged, false);
REP_SET(bHasBeenClicked, true);
}
Push Model & Net Dormancy
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)
{
Super::NotifyActorOnClicked(ButtonPressed);
REP_SET(Explosion, this->SpawnActor<AExplosion>());
}
Push Model & Net Dormancy
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
#define REP_SET(PropertyName, NewValue) \
{ \
auto const EvaluatedNewValue = NewValue; \
if (this->PropertyName != EvaluatedNewValue) \
{ \
REP_DIRTY(bIsArmed); \
\
decltype(this->PropertyName) const OldValue = this->PropertyName; \
this->PropertyName = EvaluatedNewValue; \
\
REP_NOTIFY(bIsArmed, OldValue); \
} \
}
REP_SET()
#define REP_DIRTY(PropertyName) \
RepDirty_Internal(this, GET_PUSH_ID(this), (int32)ThisClass::ENetFields_Private::PropertyName);
void RepDirty_Internal(AActor* Actor, uint64 NetPushID, int32 RepIndex)
{
Actor->FlushNetDormancy();
#if WITH_PUSH_MODEL
UEPushModelPrivate::FNetPushObjectId const TypedNetPushID(NetPushID);
UEPushModelPrivate::MarkPropertyDirty(Actor, TypedNetPushID, RepIndex);
#endif
}
REP_DIRTY()
#define REP_DIRTY(PropertyName) \
RepDirty_Internal(this, GET_PUSH_ID(this), (int32)ThisClass::ENetFields_Private::PropertyName);
void RepDirty_Internal(AActor* Actor, uint64 NetPushID, int32 RepIndex)
{
Actor->FlushNetDormancy();
#if WITH_PUSH_MODEL
UEPushModelPrivate::FNetPushObjectId const TypedNetPushID(NetPushID);
UEPushModelPrivate::MarkPropertyDirty(Actor, TypedNetPushID, RepIndex);
#endif
}
REP_DIRTY()
#define REP_DIRTY(PropertyName) \
RepDirty_Internal(this, GET_PUSH_ID(this), (int32)ThisClass::ENetFields_Private::PropertyName);
void RepDirty_Internal(AActor* Actor, uint64 NetPushID, int32 RepIndex)
{
Actor->FlushNetDormancy();
#if WITH_PUSH_MODEL
UEPushModelPrivate::FNetPushObjectId const TypedNetPushID(NetPushID);
UEPushModelPrivate::MarkPropertyDirty(Actor, TypedNetPushID, RepIndex);
#endif
}
REP_DIRTY()
#define REP_DIRTY(PropertyName) \
RepDirty_Internal(this, GET_PUSH_ID(this), (int32)ThisClass::ENetFields_Private::PropertyName);
void RepDirty_Internal(AActor* Actor, uint64 NetPushID, int32 RepIndex)
{
Actor->FlushNetDormancy();
#if WITH_PUSH_MODEL
UEPushModelPrivate::FNetPushObjectId const TypedNetPushID(NetPushID);
UEPushModelPrivate::MarkPropertyDirty(Actor, TypedNetPushID, RepIndex);
#endif
}
REP_DIRTY()
#define REP_DIRTY(PropertyName) \
RepDirty_Internal(this, GET_PUSH_ID(this), (int32)ThisClass::ENetFields_Private::PropertyName);
void RepDirty_Internal(AActor* Actor, uint64 NetPushID, int32 RepIndex)
{
Actor->FlushNetDormancy();
#if WITH_PUSH_MODEL
UEPushModelPrivate::FNetPushObjectId const TypedNetPushID(NetPushID);
UEPushModelPrivate::MarkPropertyDirty(Actor, TypedNetPushID, RepIndex);
#endif
}
REP_DIRTY()
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);
REP_DIRTY(bHidden);
REP_DIRTY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
#define REP_NOTIFY(PropertyName, ...) \
if (!this->HasAnyFlags(RF_NeedPostLoad)) \
{ \
FName const FunctionName = RepNotify_GetFunctionName( \
ThisClass::StaticClass(), \
(int32)ThisClass::ENetFields_Private::PropertyName); \
\
if (!FunctionName.IsNone()) \
{ \
if (UFunction* const Function = ThisClass::StaticClass()->FindFunctionByName(FunctionName)) \
{ \
checkf( \
Function->NumParms <= 1, \
TEXT("REP_NOTIFY does not support rep notifies with more than one parameter.")); \
\
RepNotify_ProcessEvent(this, Function, __VA_ARGS__); \
} \
} \
}
FName RepNotify_GetFunctionName(UClass* Class, int32 RepIndex)
{
return Class->ClassReps[RepIndex].Property->RepNotifyFunc;
}
REP_NOTIFY()
REP_DIRTY(PropertyName)
REP_NOTIFY(PropertyName, ...)
REP_SET(PropertyName, NewValue)
REP_ADD(PropertyName, ...)
REP_INSERT(PropertyName, NewValue, NewIndex)
REP_REMOVE(PropertyName, OldValue)
REP_REMOVEAT(PropertyName, OldIndex)
REP_EMPTY(PropertyName)
REP_CLASS(Class, Func)
REP_PROPERTY(...)
REP_PROPERTIES(...)
static void ReplicateImmediately(AActor* Actor);
RepHelpers.h
REP_DIRTY(PropertyName)
REP_NOTIFY(PropertyName, ...)
REP_SET(PropertyName, NewValue)
REP_ADD(PropertyName, ...)
REP_INSERT(PropertyName, NewValue, NewIndex)
REP_REMOVE(PropertyName, OldValue)
REP_REMOVEAT(PropertyName, OldIndex)
REP_EMPTY(PropertyName)
REP_CLASS(Class, Func)
REP_PROPERTY(...)
REP_PROPERTIES(...)
static void ReplicateImmediately(AActor* Actor);
RepHelpers.h
REP_DIRTY(PropertyName)
REP_NOTIFY(PropertyName, ...)
REP_SET(PropertyName, NewValue)
REP_ADD(PropertyName, ...)
REP_INSERT(PropertyName, NewValue, NewIndex)
REP_REMOVE(PropertyName, OldValue)
REP_REMOVEAT(PropertyName, OldIndex)
REP_EMPTY(PropertyName)
REP_CLASS(Class, Func)
REP_PROPERTY(...)
REP_PROPERTIES(...)
static void ReplicateImmediately(AActor* Actor);
RepHelpers.h
REP_DIRTY(PropertyName)
REP_NOTIFY(PropertyName, ...)
REP_SET(PropertyName, NewValue)
REP_ADD(PropertyName, ...)
REP_INSERT(PropertyName, NewValue, NewIndex)
REP_REMOVE(PropertyName, OldValue)
REP_REMOVEAT(PropertyName, OldIndex)
REP_EMPTY(PropertyName)
REP_CLASS(Class, Func)
REP_PROPERTY(...)
REP_PROPERTIES(...)
static void ReplicateImmediately(AActor* Actor);
RepHelpers.h
REP_DIRTY(PropertyName)
REP_NOTIFY(PropertyName, ...)
REP_SET(PropertyName, NewValue)
REP_ADD(PropertyName, ...)
REP_INSERT(PropertyName, NewValue, NewIndex)
REP_REMOVE(PropertyName, OldValue)
REP_REMOVEAT(PropertyName, OldIndex)
REP_EMPTY(PropertyName)
REP_CLASS(Class, Func)
REP_PROPERTY(...)
REP_PROPERTIES(...)
static void ReplicateImmediately(AActor* Actor);
RepHelpers.h
void AMyActor::Destroyed()
{
ReplicateImmediately(this);
// this->ForceNetUpdate();
Super::Destroyed();
}
Push Model & Net Dormancy
void AMyActor::Destroyed()
{
ReplicateImmediately(this);
// this->ForceNetUpdate();
Super::Destroyed();
}
Push Model & Net Dormancy
void AMyActor::Destroyed()
{
ReplicateImmediately(this);
// this->ForceNetUpdate();
Super::Destroyed();
}
Push Model & Net Dormancy
APawn
Controller
AGatherableLandmark
GathersRemaining
AExplosion
Final State
APawn
Controller
AGatherableLandmark
GathersRemaining
AExplosion
Final State
APawn
Controller
AGatherableLandmark
GathersRemaining
AExplosion
Final State
APawn
Controller
AGatherableLandmark
GathersRemaining
AExplosion
Final State
void ReplicateImmediately(AActor* Actor)
{
UNetDriver* const Driver = Actor->GetNetDriver();
for (UNetConnection* const Connection : Actor->GetNetDriver()->ClientConnections)
{
UActorChannel* Channel = Connection->FindActorChannelRef(Actor);
if (Channel == nullptr
&& !Actor->IsPendingKillPending()
&& Driver->IsLevelInitializedForActor(Actor, Connection))
{
Channel = Cast<UActorChannel>(Connection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally));
if (Channel != nullptr)
{
Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
}
}
if (Channel != nullptr)
{
Channel->bForceCompareProperties = true;
Channel->ReplicateActor();
Channel->bForceCompareProperties = false;
}
}
}
ReplicateImmediately()
void ReplicateImmediately(AActor* Actor)
{
UNetDriver* const Driver = Actor->GetNetDriver();
for (UNetConnection* const Connection : Actor->GetNetDriver()->ClientConnections)
{
UActorChannel* Channel = Connection->FindActorChannelRef(Actor);
if (Channel == nullptr
&& !Actor->IsPendingKillPending()
&& Driver->IsLevelInitializedForActor(Actor, Connection))
{
Channel = Cast<UActorChannel>(Connection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally));
if (Channel != nullptr)
{
Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
}
}
if (Channel != nullptr)
{
Channel->bForceCompareProperties = true;
Channel->ReplicateActor();
Channel->bForceCompareProperties = false;
}
}
}
ReplicateImmediately()
void ReplicateImmediately(AActor* Actor)
{
UNetDriver* const Driver = Actor->GetNetDriver();
for (UNetConnection* const Connection : Actor->GetNetDriver()->ClientConnections)
{
UActorChannel* Channel = Connection->FindActorChannelRef(Actor);
if (Channel == nullptr
&& !Actor->IsPendingKillPending()
&& Driver->IsLevelInitializedForActor(Actor, Connection))
{
Channel = Cast<UActorChannel>(Connection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally));
if (Channel != nullptr)
{
Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
}
}
if (Channel != nullptr)
{
Channel->bForceCompareProperties = true;
Channel->ReplicateActor();
Channel->bForceCompareProperties = false;
}
}
}
ReplicateImmediately()
void ReplicateImmediately(AActor* Actor)
{
UNetDriver* const Driver = Actor->GetNetDriver();
for (UNetConnection* const Connection : Actor->GetNetDriver()->ClientConnections)
{
UActorChannel* Channel = Connection->FindActorChannelRef(Actor);
if (Channel == nullptr
&& !Actor->IsPendingKillPending()
&& Driver->IsLevelInitializedForActor(Actor, Connection))
{
Channel = Cast<UActorChannel>(Connection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally));
if (Channel != nullptr)
{
Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
}
}
if (Channel != nullptr)
{
Channel->bForceCompareProperties = true;
Channel->ReplicateActor();
Channel->bForceCompareProperties = false;
}
}
}
ReplicateImmediately()
void ReplicateImmediately(AActor* Actor)
{
UNetDriver* const Driver = Actor->GetNetDriver();
for (UNetConnection* const Connection : Actor->GetNetDriver()->ClientConnections)
{
UActorChannel* Channel = Connection->FindActorChannelRef(Actor);
if (Channel == nullptr
&& !Actor->IsPendingKillPending()
&& Driver->IsLevelInitializedForActor(Actor, Connection))
{
Channel = Cast<UActorChannel>(Connection->CreateChannelByName(NAME_Actor, EChannelCreateFlags::OpenedLocally));
if (Channel != nullptr)
{
Channel->SetChannelActor(Actor, ESetChannelActorFlags::None);
}
}
if (Channel != nullptr)
{
Channel->bForceCompareProperties = true;
Channel->ReplicateActor();
Channel->bForceCompareProperties = false;
}
}
}
ReplicateImmediately()
RepHelpers.h
�[Iris]
Thank you!
�— Andrew
https://gitlab.com/2Bit/unrealfest-anz-2024