How do I set up Property replication in component?

I have any issue with replicating property in Components.

Component Header:

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once

#include "SystemAbilityManagerComponent.generated.h"

UCLASS(meta = (BlueprintSpawnableComponent), hidecategories = (Object, LOD, Lighting, Transform, Sockets, TextureStreaming))
class USystemAbilityManagerComponent : public UActorComponent
{
	GENERATED_UCLASS_BODY()

	virtual void InitializeComponent() OVERRIDE;

	UPROPERTY(EditAnywhere, Category = "Abilities")
		TArray<TSubclassOf <class ASystemAbility> > AbilityTypes;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Replicated, Category = "Ability|Manager")
		TArray<class ASystemAbility*> AbilityList;

	/*
	Stub function. Just for testing, probably will need change o removed, do not overuse it!
	*/
	UFUNCTION(BlueprintCallable, Category = "Ability|Manager")
		void AddAbility(TSubclassOf<class ASystemAbility> ability, APawn* Owner);

	UFUNCTION(Server, Reliable, WithValidation)
		void ServerAddAbility(TSubclassOf<class ASystemAbility> ability, APawn* Owner);

public:
	UFUNCTION(BlueprintCallable, Category = "Ability")
		class ASystemAbility* GetButtonOne();
	UFUNCTION()
		class ASystemAbility* GetButtonTwo();

	UFUNCTION()
		void OnRep_AbilityButtonOne();
		
		void SetActionButton(class ASystemAbility* AbilityIn);


//INPUT HANDLING:
	UFUNCTION(Server, Reliable, WithValidation)
		void ServerActionButtonOne();

	UPROPERTY(BlueprintReadOnly, Transient, ReplicatedUsing = OnRep_LeftHandAction, Category = "Action Buttons")
		class ASystemAbility* LeftHandAction;

	UFUNCTION()
		void OnRep_LeftHandAction(class ASystemAbility* LeftHandActionIn);
	
	UFUNCTION()
		void EquipAction(class ASystemAbility* Ability);

	UFUNCTION(Server, Reliable, WithValidation)
		void ServerEquipAction(class ASystemAbility* Ability);

	void SetCurrentAction(class ASystemAbility* Ability);

protected:
	UPROPERTY(BlueprintReadOnly, ReplicatedUsing=OnRep_AbilityButtonOne, Category = "Ability")
		class ASystemAbility* AbilityButtonOneObj;
	UPROPERTY(BlueprintReadOnly, Category = "Ability")
		class ASystemAbility* AbilityButtonTwoObj;

};

And here is implementation:

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "Game.h"
#include "../Abilities/SystemAbility.h"

#include "Net/UnrealNetwork.h"

#include "SystemAbilityManagerComponent.h"

USystemAbilityManagerComponent::USystemAbilityManagerComponent(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	SetIsReplicated(true);
	bReplicates = true;
}

void USystemAbilityManagerComponent::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(USystemAbilityManagerComponent, AbilityButtonOneObj);
	DOREPLIFETIME(USystemAbilityManagerComponent, LeftHandAction);
	DOREPLIFETIME_CONDITION(USystemAbilityManagerComponent, AbilityList, COND_OwnerOnly);
}

void USystemAbilityManagerComponent::InitializeComponent()
{

}

void USystemAbilityManagerComponent::AddAbility(TSubclassOf<ASystemAbility> ability, APawn* Owner)
{
	if (GetOwnerRole() < ROLE_Authority)
	{
		ServerAddAbility(ability, Owner);
		return;
	}

	ServerAddAbility(ability, Owner);
}


void USystemAbilityManagerComponent::ServerAddAbility_Implementation(TSubclassOf<class ASystemAbility> ability, APawn* Owner)
{
	//if pawn already have incoming ability, we don't need to spawn it, we simply return;
	if (AbilityList.Num() > 0)
	{
		for (ASystemAbility* abilityI : AbilityList)
		{
			if (abilityI->IsA(ability))
				return;
		}
	}
	if (ability && Owner)
	{
		//URPGAbilityBase* abilityObj = ConstructObject<URPGAbilityBase>(ability);

		//abilityObj->Initialize(Owner, Owner->Controller);
		FActorSpawnParameters SpawnParams;
		SpawnParams.Owner = Owner;
		SpawnParams.bNoCollisionFail = true;

		ASystemAbility* abilityTemp = GetWorld()->SpawnActor<ASystemAbility>(ability, SpawnParams);

		abilityTemp->AbilityOwner = Owner;
		abilityTemp->Initialize();

		AbilityList.AddUnique(abilityTemp);
		SetActionButton(abilityTemp);
		//AbilityList.AddUnique(abilityObj);
		/*
		this should be handled by drag&drop UI
		where user just drag ability from ability inventory to hot bar

		in more elaborate scenario there can be more layers to it. Like dragging
		Spells to spell slots (to prepare them)
		and then dragging prepared spells to hotbar.
		Either way this is just debug solution that should be changed.
		*/
	}
}
bool USystemAbilityManagerComponent::ServerAddAbility_Validate(TSubclassOf<class ASystemAbility> ability, APawn* Owner)
{
	return true;
}

void USystemAbilityManagerComponent::SetActionButton(class ASystemAbility* AbilityIn)
{
	if (!AbilityButtonOneObj || AbilityButtonOneObj->IsA(AbilityIn->GetClass()))
	{
		AbilityButtonOneObj = AbilityIn;
	}
	else if (!AbilityButtonTwoObj)
	{
		AbilityButtonTwoObj = AbilityIn;
	}
}

void USystemAbilityManagerComponent::OnRep_AbilityButtonOne()
{
	SetActionButton(AbilityButtonOneObj);
}

ASystemAbility* USystemAbilityManagerComponent::GetButtonOne()
{
	if (AbilityButtonOneObj)
	{
		return AbilityButtonOneObj;
	}
	return NULL;
}

ASystemAbility* USystemAbilityManagerComponent::GetButtonTwo()
{
	if (AbilityButtonTwoObj)
	{
		return AbilityButtonTwoObj;
	}
	return NULL;
}

void USystemAbilityManagerComponent::ServerActionButtonOne_Implementation()
{
	EquipAction(AbilityButtonOneObj);
}
bool USystemAbilityManagerComponent::ServerActionButtonOne_Validate()
{
	return true;
}

void USystemAbilityManagerComponent::EquipAction(class ASystemAbility* Ability)
{
	if (Ability)
	{
		if (GetOwnerRole() == ROLE_Authority)
		{
			SetCurrentAction(Ability);
		}
		else
		{
			ServerEquipAction(Ability);
		}
	}
}

bool USystemAbilityManagerComponent::ServerEquipAction_Validate(class ASystemAbility* Ability)
{
	return true;
}

void USystemAbilityManagerComponent::ServerEquipAction_Implementation(class ASystemAbility* Ability)
{
	EquipAction(Ability);
}

void USystemAbilityManagerComponent::OnRep_LeftHandAction(class ASystemAbility* LeftHandActionIn)
{
	SetCurrentAction(LeftHandAction);
}

void USystemAbilityManagerComponent::SetCurrentAction(class ASystemAbility* Ability)
{
	LeftHandAction = Ability;
}

The issue is with LeftHandAction andAbilityButtonOneObj, they are simply not properly replicated to client.

And here is where I tried to use LeftHandAction

void AGameCharacter::RunLeftHand()
{
	if (AbilityManager->LeftHandAction)
	{
		AbilityManager->LeftHandAction->InputPressed();
	}
}

If I tried to do it this way, LeftHandAction is NULL, because it was not replicated.

So I changed it to work like this:

void AGameCharacter::RunLeftHand()
{
	if (Role < ROLE_Authority)
	{
		ServerRunLeftHand();
	}

}
void AGameCharacter::ServerRunLeftHand_Implementation()
{
	if (AbilityManager->LeftHandAction)
	{
		AbilityManager->LeftHandAction->InputPressed();
	}
}
bool AGameCharacter::ServerRunLeftHand_Validate()
{
	return true;
}

And now it works. But, that doesn’t solve problem. Because I still need to replicte ability to client, to get some of it’s properties displayed on HUD (for example indicator if the ability is equiped at all).

If AbilityButtonOneObj and LeftHandAction are dynamic actors (spawned after play), they also need to be marked as replicated themselves for the reference to be replicated.

Could this be the issue?

Yes, they are dynamic and they are marekd as replicated actors. Both in blueprint and in C++ constructor. But that didn’t helped.

In any case, In act of desperation in my character blueprint graph, after BeginPlay I added SetIsReplicated for my AbilityManagerComponent, and it suddenly started working. Any ideas why just setting it up in C++ didn’t worked ?

You have to call SetIsReplicated from the owning actor, not on the component itself. E.g. in MyCharacter.cpp constructor: CharacterMovement->SetIsReplicated(true);

I forgot to post it here I but I also set it up in owning actor constructor.

In case someone is struggling with the same issue: Ixiguis is right:

CharacterMovement->SetIsReplicated(true);

should be called from the characters constructor which is owning the component. Simply calling it from the components constructor itself is not enough. I’ve lost a couple of hours searching for this solution so hopefully this will help someone.

1 Like