1 of 182

Game Framework Extensions:�Technical Deep Dive

Unreal Fest Gold Coast 2024

Andrew Joy

2Bit Studios

2 of 182

3 of 182

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.

4 of 182

UPocketWorld

�[Engine]

5 of 182

// Manage actors

this->GetWorld()->SpawnActor(...);

// Query physics

this->GetWorld()->LineTraceSingleByChannel(...);

// Host replication

this->GetWorld()->GetNetDriver();

UWorld

6 of 182

Game

UWorld

7 of 182

Game

Editor

UWorld

8 of 182

Game

Editor

Loading

UWorld

9 of 182

Game

Editor

Loading

Lobby

UWorld

10 of 182

Game

Editor

Loading

Scene Capture

Lobby

UWorld

11 of 182

Game

Editor

Loading

Scene Capture

Lobby

Mini

Game

UWorld

12 of 182

Scene Capture

Mini

Game

UPocketWorld

13 of 182

Use Case

14 of 182

Use Case

15 of 182

Use Case

16 of 182

Use Case

17 of 182

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

18 of 182

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

19 of 182

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

20 of 182

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

21 of 182

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

22 of 182

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

23 of 182

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

24 of 182

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

25 of 182

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

26 of 182

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

27 of 182

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

28 of 182

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

29 of 182

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

30 of 182

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

31 of 182

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

32 of 182

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

33 of 182

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

34 of 182

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

35 of 182

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

36 of 182

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

37 of 182

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

38 of 182

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

39 of 182

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

40 of 182

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

41 of 182

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

42 of 182

Use Case

43 of 182

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

44 of 182

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

45 of 182

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

46 of 182

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

47 of 182

Use Case

48 of 182

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

49 of 182

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

50 of 182

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

51 of 182

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

52 of 182

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

53 of 182

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

54 of 182

UPocketWorld

�[Engine]

55 of 182

AAnyMeshActor

�[GameFramework]

56 of 182

AStaticMeshActor

UStaticMeshComponent

What?

57 of 182

AStaticMeshActor

UStaticMeshComponent

ASkeletalMeshActor

USkeletalMeshComponent

What?

58 of 182

AStaticMeshActor

UStaticMeshComponent

ASkeletalMeshActor

USkeletalMeshComponent

AAnyMeshActor

???

What?

59 of 182

Why?

60 of 182

AStaticMeshActor

UStaticMeshComponent

ASkeletalMeshActor

USkeletalMeshComponent

AAnyMeshActor

???

How?

61 of 182

Artist: Can I have AAnyMeshActor?

Mum: We have AAnyMeshActor at home.

AAnyMeshActor at home:

62 of 182

AStaticMeshActor

UStaticMeshComponent

ASkeletalMeshActor

USkeletalMeshComponent

AAnyMeshActor

UMeshComponent

How?

63 of 182

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

64 of 182

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

65 of 182

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

66 of 182

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

67 of 182

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

68 of 182

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

69 of 182

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

70 of 182

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

71 of 182

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

72 of 182

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

73 of 182

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

74 of 182

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

75 of 182

AFenceBuilding

Why?

ASlaughtermelonLandmark

76 of 182

AFenceBuilding

Why?

ASlaughtermelonLandmark

ALandmarkEntity

77 of 182

AFenceBuilding

Why?

ASlaughtermelonLandmark

ABuildingEntity

ALandmarkEntity

78 of 182

AFenceBuilding

Why?

ASlaughtermelonLandmark

ABuildingEntity

ALandmarkEntity

AEntityActor

79 of 182

AFenceBuilding

Why?

ABuildingEntity

ASlaughtermelonLandmark

ALandmarkEntity

AEntityActor

AAnyMeshActor

80 of 182

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

81 of 182

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

82 of 182

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

83 of 182

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

84 of 182

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

85 of 182

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

86 of 182

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

87 of 182

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

88 of 182

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

89 of 182

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

90 of 182

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

91 of 182

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

92 of 182

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

93 of 182

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

94 of 182

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

95 of 182

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

96 of 182

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

97 of 182

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

98 of 182

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

99 of 182

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

100 of 182

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

101 of 182

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

102 of 182

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

103 of 182

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) ???

}

104 of 182

void AAnyMeshActor::CopyMesh(AActor* From)

{

// 1) ???

// 2) ???

// 3) ???

// 4) ???

// 5) ???

// 6) ???

}

CopyMesh()

105 of 182

void AAnyMeshActor::CopyMesh(AActor* From)

{

// 1) ???

// 2) ???

// 3) ???

// 4) ???

// 5) ???

// 6) ???

}

CopyMesh()

Slaughtermelon

Skeletal

Spikes

Static

TNT

Static

106 of 182

void AAnyMeshActor::CopyMesh(AActor* From)

{

// 1) ???

// 2) ???

// 3) ???

// 4) ???

// 5) ???

// 6) ???

}

CopyMesh()

Slaughtermelon

Skeletal

Spikes

Static

TNT

Static

107 of 182

void AAnyMeshActor::CopyMesh(AActor* From)

{

// 1) ???

// 2) ???

// 3) ???

// 4) ???

// 5) ???

// 6) ???

}

CopyMesh()

Slaughtermelon

Skeletal

Spikes

Static

TNT

Static

108 of 182

void AAnyMeshActor::CopyMesh(AActor* From)

{

// 1) ???

// 2) ???

// 3) ???

// 4) ???

// 5) ???

// 6) ???

}

CopyMesh()

Slaughtermelon

Skeletal

Spikes

Static

TNT

Static

109 of 182

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

110 of 182

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

111 of 182

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

112 of 182

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

}

113 of 182

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

}

114 of 182

115 of 182

...

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()

116 of 182

...

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()

117 of 182

...

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()

118 of 182

...

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()

119 of 182

...

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()

120 of 182

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()

121 of 182

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()

122 of 182

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()

123 of 182

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()

124 of 182

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()

125 of 182

AAnyMeshActor

�[GameFramework]

126 of 182

RepHelpers.h

�[Iris]

127 of 182

ASlaughtermelon

bIsArmed

UScanAction

ScanTarget ScanProgress

ACrop

GrowthStage GrowthProgress

NetTick

128 of 182

ASlaughtermelon

bIsArmed

UScanAction

ScanTarget ScanProgress

ACrop

GrowthStage GrowthProgress

Push Model

129 of 182

ASlaughtermelon

bIsArmed

UScanAction

ScanTarget ScanProgress

ACrop

GrowthStage GrowthProgress

Push Model

130 of 182

void AActor::SetHidden(bool bInHidden)

{

bHidden = bInHidden;

MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);

}

Push Model

131 of 182

void AActor::SetHidden(bool bInHidden)

{

bHidden = bInHidden;

MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);

}

Push Model

132 of 182

ASlaughtermelon

bIsArmed

UScanAction

ScanTarget ScanProgress

ACrop

GrowthStage GrowthProgress

Net Dormancy

133 of 182

ASlaughtermelon

UScanAction

ScanTarget ScanProgress

ACrop

Net Dormancy

134 of 182

[SystemSettings]

net.PushModelSkipUndirtiedReplication=1

Net Dormancy

135 of 182

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

136 of 182

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

137 of 182

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

138 of 182

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

139 of 182

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

140 of 182

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

141 of 182

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

142 of 182

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

143 of 182

void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)

{

Super::NotifyActorOnClicked(ButtonPressed);

REP_SET(bHidden, true);

REP_SET(bCanBeDamaged, false);

REP_SET(bHasBeenClicked, true);

}

Push Model & Net Dormancy

144 of 182

#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()

145 of 182

#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()

146 of 182

void AMyActor::NotifyActorOnClicked(FKey ButtonPressed)

{

Super::NotifyActorOnClicked(ButtonPressed);

REP_SET(Explosion, this->SpawnActor<AExplosion>());

}

Push Model & Net Dormancy

147 of 182

#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()

148 of 182

#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()

149 of 182

#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()

150 of 182

#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()

151 of 182

#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()

152 of 182

#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()

153 of 182

#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()

154 of 182

#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()

155 of 182

#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()

156 of 182

MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bHidden, this);

REP_DIRTY(bHidden);

REP_DIRTY()

157 of 182

#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()

158 of 182

#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()

159 of 182

#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()

160 of 182

#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()

161 of 182

#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()

162 of 182

#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()

163 of 182

#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()

164 of 182

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

165 of 182

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

166 of 182

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

167 of 182

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

168 of 182

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

169 of 182

void AMyActor::Destroyed()

{

ReplicateImmediately(this);

// this->ForceNetUpdate();

Super::Destroyed();

}

Push Model & Net Dormancy

170 of 182

void AMyActor::Destroyed()

{

ReplicateImmediately(this);

// this->ForceNetUpdate();

Super::Destroyed();

}

Push Model & Net Dormancy

171 of 182

void AMyActor::Destroyed()

{

ReplicateImmediately(this);

// this->ForceNetUpdate();

Super::Destroyed();

}

Push Model & Net Dormancy

172 of 182

APawn

Controller

AGatherableLandmark

GathersRemaining

AExplosion

Final State

173 of 182

APawn

Controller

AGatherableLandmark

GathersRemaining

AExplosion

Final State

174 of 182

APawn

Controller

AGatherableLandmark

GathersRemaining

AExplosion

Final State

175 of 182

APawn

Controller

AGatherableLandmark

GathersRemaining

AExplosion

Final State

176 of 182

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()

177 of 182

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()

178 of 182

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()

179 of 182

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()

180 of 182

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()

181 of 182

RepHelpers.h

�[Iris]

182 of 182

Thank you!

�— Andrew

https://gitlab.com/2Bit/unrealfest-anz-2024