Hey, I am having trouble replicating an array of objects. I have an actor (Object manager) and a (NetworkObject). The Newtwork object has the IsSupportedForNetworking function override and the replicated properties using GetLifetimeReplicatedProps. The Object manager has an array of the NetworkObject that is replicated and using GetLifetimeReplicatedProps aswell. The array is not replicating properly. I get all null pointers in the client. I have not used the ReplicateSubobjects function in any of these ones. Any ideas?
NVM, i got it working. I needed to override ReplicateSubobjects in the manager. Thanks for this post. Really helped.
Announcement
Collapse
No announcement yet.
Replicating TArrays crashes game
Collapse
X
-
ArcainOne repliedOriginally posted by Markusgod View PostI wonder, will this replication take care of creating/destroying objects when tarray size is changed?Last edited by ArcainOne; 12-19-2019, 06:56 PM.
- 1 like
Leave a comment:
-
Markusgod repliedI wonder, will this replication take care of creating/destroying objects when tarray size is changed?
Leave a comment:
-
ArcainOne repliedOkay so I finally successfully replicated a TArray of Objects, it is fairly simple once you get your head around Replication.
Begin with defining the UObject you want replicated. I actually create a UNetworkObject as a base class for all UObjects I want replicated. But the quick and dirty way is to simply override the IsSupportedForNetworking Then mark the properties you need replicated as UPROPERTY(Replicated)
Code:class UNetworkObject : UObject { virtual bool IsSupportedForNetworking() override; virtual bool ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags); // note no override because this is the FIRST declaration of this function. }
Code:bool UNetworkObject::ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) { return false; }
The following example demonstrates how to Replicate a TArray of subobjects from within another UObject using an ActorComponent (whch uses an AActor object) for replication.
MyTestObject.h
My Test object is a simple UObject that needs to replicate some information, but is complex enough that it does not warrent a simple Struct to do the job. For demonstration purposes I chose a very simple object.
Code:class UMyTestObject : UNetworkObject { UPROPERTY(Replicated) float MyTestProperty; }
Code:void UMyTestObject::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const { DOREPLIFETIME(MyTestObject, MyTestProperty); }
MyTestObjectManager.h
The TestObjectManager simply contains a bunch of MyTestObjects. Ideally this class would also provide some accessor functionality but for demonstration purposes I have left that out.
Code:class UMyTestObjectManager : UNetworkObject { UPROPERTY(Replicated) TArray<UMyTestObject*> MyTestObjects; void BuildArrayOfTestObjects(int32 numObjects); // I'm not going to define this for demonstration sake, it would just create a bunch of UMyTestObjects and add them to the array. virtual bool ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) override; // override from the base class }
Code:void UMyTestObjectManager::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const { DOREPLIFETIME(UMyTestObjectManager, MyTestObjects); } bool UMyTestObjectManager::ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) { bool wroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags); for(UMyTestObject * myObject : MyTestObjects) { if(myObject) { Channel->ReplicateObject(myObject, *Bunch, *RepFlags) } } }
"But you said UObjects don't replicate, your MyTestObjectManager is a UObject, How is it replicating?" - Well my friend we are not done.
The actual replication for this REQUIRES either an Actor OR a ActorComponent. Either way the procedure is the same.
MyActorComponent.h
MyActorComponent simply demonstrates how to do replication with a Component. You can just as easily use an AActor.
Code:class UMyActorComponent : UActorComponent { UMyActorComponent(); virtual void InitializeComponent() override; UMyTestObjectManager * myTestObjectManager; virtual bool ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) override; }
Code:UMyActorComponent::UMyActorComponent() { this->bWantsInitializeComponent = true; this->SetIsReplicated(true); } void UMyActorComponent::InitializeComponent() { this->myTestObjectManager = ConstructObject<UMyTestObjectManager>(UMyTestObjectManager::StaticClass(), this); if(this->myTestObjectManager) { this->myTestObjectManager->BuildArrayOfTestObjects(15); } } void UMyActorComponent::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const { DOREPLIFETIME(UMyActorComponent, myTestObjectManager); } bool UMyActorComponent::ReplicateSubobjects(AActorChannel * Channel, FOutBunch * Bunch, FReplicationFlags * RepFlags) { bool wroteSomething = Super::ReplicateSubobjects(Channel, Bunch, RepFlags); wroteSomething |= Channel->ReplicateSubobject(myTestObjectManager, *Bunch, *RepFlags); // replicate the subobject wroteSomething |= myTestObjectManager->ReplicateSubobjects(Channel, Bunch, RepFlags); // replicate the subobject's subobjects. return wroteSomething; }
Code:UMyActorComponent->myTestObjectManager->myTestObjects[0]->MyTestProperty = 25.22f
Code:void UMyActorComponent::SetValueAtObjectIndex(int32 objectIndex, float value) { if(this->Role == RP_Authority) { this->myTestObjectManager->SetValueAtIndex(objectIndex, value); } else { this->ServerSetValueAtObjectIndex(objectIndex, value); } } bool UMyActorComponent::ServerSetValueAtObjectIndex_Validate(int32 objectIndex, float value) { // do some logic to validate the user is not cheating return true; // return false if you want to automatically kick the cheater from your game. } void UMyActorComponent::ServerSetValueAtObjectIndex_Implementation(int32 objectIndex, float value) { this->SetValueAtObjectIndex(objectIndex, value) // no point in duplicating code, this should be the authority now. }
- 3 likes
Leave a comment:
-
ArcainOne repliedAh okay I see the confusion now. My owning actor has a UPROPERTY for the component setup before hand and that is set to be replicated from the get go. The property is initialized as NULL but is then created and set in Begin Play by the server. This seems to work fine as long as my component is not replicating a TArray within it. I setup a test in my ActorComponent where instead of an array I used a Single replicated UPROPERTY pointer that also was created on initialization of the component. This test was successful as the value was replicated to my client when it was changed on the server. This leads me to believe that as long as the property itself is set to be replicated then it can be null. My TArrays are always non pointers so having it exist before runtime is not an issue, it's contents however are a different matter haha.
Okay I'll try the DOREPTARRAY but I noticed the documentation you gave it looks like it requires the override of GetReplicationList() is that what you ment to put down?. Also a rep function OnRep_[MyArray] but no example of that function in the documentation.
My final confusion on this comes from the UAbilitySystemComponent that serializes and replicates a list of UAbility objects
Documentation - UAbilitySystemComponent - here there is a property that is replicated called AllReplicatedInstancedAbilities which is a TArray of UGameplayAbility.
In UAbilitySystemComponent::ReplicateSubobjects we see the list of AllReplicatedInstancedAbilities iterated through and added to the actor's replication channel.
In the Documentation - UGameplayAbility I see no properties marked for replication or anything special about it... Just an object derrived from UObject.
I have just started by grand foray into replication so pardon me if my supplemental questions seem elementaryThanks.
Leave a comment:
-
ArcainOne repliedOkay, I'm not entirely sure I follow you. I am not sure how I am changing what is replicated at Runtime. If I use this exact same setup with a single UObject instead of an array of UObjects the replication happens without a problem. It is when I make the "myObjects" variable to a TArray<UMyObject*> instead f a normal UMyObject * (In the component).
So ultimately my question is how do you properly replicate an array of UObjects?
Leave a comment:
-
ArcainOne started a topic Replicating TArrays crashes gameReplicating TArrays crashes game
I created an ActorComponent "MyActorComponent" with an Array of UObjects "UMyObject" that need to be replicated. I overrode the IsSupportedForNetworking and the GetLifetimeRepProperties on the UObject.
(this is part psuedo code)
class MyUObject : public UObject
{}
virtual void IsSupportedForNetworking() override
{return true;}
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Replicated, Category="My Object"
int32 bReplicatedUProperty;
void GetLifetimeReplicationProps(outLifetimeProps) const
{}
Super::GetLifetimeReplicationProps(outLifetimeProps);
DOREPLIFETIME(MyUObject, bReplicatedUProperty);
Then in MyActorComponent I do pretty much the samething; SetIsReplicated(true) and override GetLifetimeReplicationProps and override the ReplicateSubobjects function
class MyActorComponent
{}
MyActorComponent(SubobjectBuilderThinggy)
{}
this->SetIsReplicated(true);
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Replicated, Category="My Actor Component")
TArray<UMyObject*> myObjects;
void GetLifetimeReplicationProps(outLifetimeProps) const
{}
Super::GetLifetimeReplicationProps(outLifetimeProps);
DOREPLIFETIME(MyUObject, bReplicatedUProperty);
virtual bool ReplicateSubobjects(ActorChannel, Bunch, RepFlags) override
{}
bool wroteSomething = Super::ReplicateSubobjects(ActorChannel, Bunch, RepFlags);
for(int32 i = 0; i < myObjects.Num(); ++i)
{}
wroteSomething |= ActorChannel->ReplicateSubobject(myObjects[i], *Bunch, *RepFlags);
return wroteSomething
My Character Actor Creates the Component at BeginPlay and sets it to a property that is replicated and in the override of GetLifetimeReplicationProps is setup with DOREPLIFETIME. When I run the game the replication of the TArray crashes the game. When change the TArray to a single object and fix the corresponding objects, the game runs and replicates the value of the single object without problem.
Honestly I would really just love an overview of how anyone else has done TArray<UObject> replication.Tags: None
Leave a comment: