And the actor was set to Replicated as well, all the properties was set to DOREPLIFETIME in GetLifetimeReplicatedProps:
#include "Entities/TestActor.h"
#include "Net/UnrealNetwork.h"
ATestActor::ATestActor()
{
PrimaryActorTick.bCanEverTick = true;
bReplicates = true;
}
void ATestActor::BeginPlay()
{
Super::BeginPlay();
// The properties here was replicated to all clients
MaxHp = 100;
Hp = MaxHp;
Defense = 10;
}
void ATestActor::TrySubHp(int32 HpSub)
{
if (HpSub <= 0)
{
return;
}
Hp -= HpSub;
if (Hp <= 0)
{
Destroy(true);
}
}
void ATestActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Replicate the properties to all clients
DOREPLIFETIME(ThisClass, MaxHp);
DOREPLIFETIME(ThisClass, Hp);
DOREPLIFETIME(ThisClass, Defense);
}
But the strange thing is that the HP property is only replicated after called BeginPlay and after called Destroy, but not replicate when it changes in TrySubHp. I wondering why is that happend?
BeginPlay() and EndPlay() are default in the Replication chain, you need to force your functions doing work on Replication stuff into the replication chain as well.
try marking the functions that performs the action as replicated as well, and put in checks for HasAuthority on function entry to ensure it is only done on the proper Client/Server (depending on chosen connection type)
for which demarcations to use look here you might also need to create a OnRep function for your given property to facilitate the GetLifetimeReplicatedProps()
Thank you for your answer. I found after I added a RPC method and called it after Hp changed, it will replicated correctly. Even the method did nothing:
UCLASS()
class GAMEPLAY_API ATestActor : public AActor
{
// ...
public:
UFUNCTION(NetMulticast,Reliable)
void EmptyMulticast();
// ...
}
The method EmptyMulticast is an empty function:
void ATestActor::EmptyMulticast_Implementation()
{
}
void ATestActor::TrySubHp(int32 HpSub)
{
if (HpSub <= 0)
{
return;
}
Hp -= HpSub;
EmptyMulticast(); // The property "Hp" will replicate when this function called.
if (Hp <= 0)
{
Destroy(true);
}
}
I’m not sure whether it is a correct way to solve this problem.
I’m very curious about how it works. Regarding the “Replication chain” you mentioned, is there any documentation about it?
the replication chain (Epic calls it a “Actor Replication flow”) is the baseline steps for how often things will trigger replication, but there are ways to override (cut in line) this flow with RPC functions.
the Engine will try to batch things together to prevent packet spam if the application is running at 60 fps, and 4 players in a Peer to Peer model that is each system receiving 180 packets each second, and in authoritative server that is the server potentially receiving 240 packets each second, and this could be per thing needing to replicate,
To prevent “most” of this packet spam the Engine attempts to batch as much of this stuff together as possible, but as a sudo-optimization the Engine only marks the property for batching when a RPC function is involved; typically we would mark the function doing the work on the Replicated property as the RPC function.
the big thing about these markups and checks for HasAuthority() is to ensure that none of the clients are injecting code and calling our C++ or blueprint functions by as strings, so that even if they are able to (it is not that hard if you know what you are doing) the function itself will only modify what that client HasAuthority() over.
There is no reason to “cut in line” anything here. What is needed is a OnRep function to go along with the replicated hp variable so the client can use it to know when the variable gets replicated. Note that OnRep functions only get called if something is expected to change so if you set the variable to a value it already is expected to have it won’t attempt to replicate it again.