My Goal is to replicate a TArray<UHealthComponent*> in a ListenServer configuration for 2 players
For more context the TArray is inside of an ActorComponent named ShipStats attached to the ShipPawn and I replicate the ActorComponent and the TArray with:
.h
// HealthComponents of the ship
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="ShipStats|ShipComponent", Replicated)
TArray<UHealthComponent*> HealthComponents;
.cpp
void UShipStats::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(UShipStats, HealthComponents);
}
So far so good this works fine and gets replicated to all clients just fine
Next the UHealthComponents are ActorComponents too, first I tryed it with them as UObject but I got some other problems with that, and I turned on replication and added the variables like Health as UPROPERTY and replicated them too.
After starting the game I initialze the cards and add the newly created HealthComponents to the ShipStats ActorComponent
cpp
// Go through all the Health Components and set their content
for (TSharedPtr<FJsonValue> Value : JsonHealthComponents)
{
TSharedPtr<FJsonObject> JsonWeaponComponentObject = Value->AsObject();
UHealthComponent* TempHealthComponent = NewObject<UHealthComponent>();
TempHealthComponent->SetIsReplicated(true);
TempHealthComponent->RegisterComponent();
TempHealthComponent->BaseHealth = JsonWeaponComponentObject->GetIntegerField(FString("Health"));
TempHealthComponent->HealthType = GetHealthTypeFromString(JsonWeaponComponentObject->GetStringField(FString("HealthType")));
TempHealthComponent->Health = JsonWeaponComponentObject->GetIntegerField(FString("Health"));
TempHealthComponent->DamageType = GetDamageTypeFromString(JsonWeaponComponentObject->GetStringField(FString("DamageType")));
UE_LOG(LogTemp, Warning, TEXT("Added HealthComponent"));
HealthComponents.Add(TempHealthComponent);
}
All whole Initialization part is done on the server and normaly replicated down to the clients.
Now finally to my problem. Player one gets the HealthComponents and the act ingame just like intended but the second player just gets a TArray of nullpointers.
The way I displayed the ShipStats ingame i can see this in the inspector and after stopping the game I get the "Accessed None" treatment.
Here is the Blueprint that shows that the right amount of pointers are in the TArray but they are referencing none
And here is the 2nd player trying to access the nullpointer, only on player 2 because player 1 just works fine
It would be fantastic if someone has any suggestions how I can fix this problem. And if someone has some idea how I could improve on my approach feel free to roast my code as much as you want :)
I found the Solution to my problem.
Making the owning Actor take bReplicateUsingRegisteredSubObjectList and adding all the objects of the array into this SubObjectList with AddReplicatedSubObject(SubObjectComponent);
For older UE Versions I recommend this UE Forum thread: https://forums.unrealengine.com/t/replicating-tarrays-crashes-game/21237/6
And for my solution I found the documentation from Epic Games after researching the the previous mentioned thread: https://dev.epicgames.com/documentation/en-us/unreal-engine/replicated-subobjects-in-unreal-engine?application_version=5.1
I hope that this saves some of your sanity searching the Unreal documentation :)