Network Replication, Using ReplicatedUsing / RepNotify vars
Contents
Overview
Dear Community,
Someone asked for some tips on doing network replication in UE4, so I wrote up this tutorial really quick!
The net code structure I am showing here has worked great for me in real multiplayer games with up to 3 people involved who are all simultaneously using my multiplayer in-game editor to co-create the world together! In a packaged game!
Using Repnotify/ReplicatedUsing
Here are two examples from my own code base of how I replicate things that dont have built in replication support like CharacterMovement->Velocity does.
These are things you have to use repnotify for so that every client will do the action that has to be performed locally.
These two examples involve gravity, which you have to set locally.
This is all occurring a class that extends ACharacter
Two Approaches
I use two different approaches in the code below.
In one case I rely on the value itself to always be dirtied and updated properly, a bit risky sometimes, depending on your situation.
In the other case I am flipping a bool that serves to guarantee replication will occur, and sending along the actual relevant data at the same time.
I'd recommend you try this latter approach first!
Notation
Of the various notations that I use in function definitions, the only required ones are _Validate and _Implementation in the CPP file.
I use R_ to tell myself that a variable is being replicated so I can clearly see why it appears in certain places in my .cpp files.
Server Rep
The server has to call the OnRep function manually because it does not participate in replication updates since it is the one sending them.
To simplify my code base I just have the Server run the same code the clients are running as it's vars have already been updated.
You'd want to avoid running certain code on a dedicated server that is purely graphical in nature.
You can avoid running code on dedicated server by wrapping it with
if(GEngine->GetNetMode(GetWorld()) != NM_DedicatedServer)
{
//code to run on non-dedicated servers
}
Virtual OnRep
I always make OnRep functions virtual so that I can override just the OnRep part in subclasses, gaining full use of the replication structure I set up in the base class without having to re-write any of it in subclasses
.H
//~~~ Physics Gravity ~~~
UFUNCTION(reliable, server, WithValidation)
void SERVER_SetPhysicsGravityActive(bool MakeActive);
UPROPERTY(ReplicatedUsing=OnRep_SetPhysicsGravity)
bool R_PhysicsGravityActive;
UFUNCTION()
virtual void OnRep_SetPhysicsGravity();
//~~~ Gravity Scale ~~~
UFUNCTION(reliable, server, WithValidation)
void SERVER_SetGravityScale(float TheGravityScale);
UPROPERTY(ReplicatedUsing=OnRep_SetGravityScale)
bool DoRep_GravityScale;
UPROPERTY(Replicated)
float R_GravityScale;
UFUNCTION()
virtual void OnRep_SetGravityScale();
CPP
//~~~ Physics Gravity ~~~
bool AJoyCharacterBase::SERVER_SetPhysicsGravityActive_Validate(bool MakeActive)
{
return true;
}
void AJoyCharacterBase::SERVER_SetPhysicsGravityActive_Implementation(bool MakeActive)
{
//Rep
R_PhysicsGravityActive = MakeActive;
OnRep_SetPhysicsGravity();
}
void AJoyCharacterBase::OnRep_SetPhysicsGravity()
{
if(Mesh)
{
Mesh->SetEnableGravity(R_PhysicsGravityActive);
}
}
//~~~ Gravity Scale ~~~
bool AJoyCharacterBase::SERVER_SetGravityScale_Validate(float TheGravityScale)
{
return true;
}
void AJoyCharacterBase::SERVER_SetGravityScale_Implementation(float TheGravityScale)
{
//Rep
DoRep_GravityScale = !DoRep_GravityScale;
R_GravityScale = TheGravityScale;
//Server
OnRep_SetGravityScale();
}
void AJoyCharacterBase::OnRep_SetGravityScale()
{
if(!UVictoryCore::VIsValid(CharacterMovement)) return;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CharacterMovement->GravityScale = R_GravityScale;
CharacterMovement->Velocity = FVector::ZeroVector;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Replication List
void AJoyCharacterBase::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );
//Physics Gravity
DOREPLIFETIME(AJoyCharacterBase, R_PhysicsGravityActive);
//Gravity Scale, Adjustments in In-Game Editor
DOREPLIFETIME(AJoyCharacterBase, R_GravityScale);
DOREPLIFETIME(AJoyCharacterBase, DoRep_GravityScale);
}
Conditional Replication
There are several optional conditions you can place on whether a repnotify will occur!
See CoreNet.h for more info :)
DOREPLIFETIME_CONDITION( AShooterWeapon_Instant, HitNotify, COND_SkipOwner );
Use the above macro instead of the standard DOREPLIFETIME if you wish to use the conditions below!
/** Secondary condition to check before considering the replication of a lifetime property. */
enum ELifetimeCondition
{
COND_None = 0, // This property has no condition, and will send anytime it changes
COND_InitialOnly = 1, // This property will only attempt to send on the initial bunch
COND_OwnerOnly = 2, // This property will only send to the actor's owner
COND_SkipOwner = 3, // This property send to every connection EXCEPT the owner
COND_SimulatedOnly = 4, // This property will only send to simulated actors
COND_AutonomousOnly = 5, // This property will only send to autonomous actors
COND_SimulatedOrPhysics = 6, // This property will send to simulated OR bRepPhysics actors
COND_InitialOrOwner = 7, // This property will send on the initial packet, or to the actors owner
COND_Custom = 8, // This property has no particular condition, but wants the ability to toggle on/off via SetCustomIsActiveOverride
COND_Max = 9,
};
Summary
Here I've demonstrated two ways to use ReplicatedUsing / Repnotify variables, and how you can use them to replicate effects and changes to the game state that are not already replicated for you by UE4!
Enjoy!
Recommending Reading
Epic Networking Tips and Tricks
https://www.unrealengine.com/blog/network-tips-and-tricks
I recommend checking out Zoid's wiki on replication!
He goes over all the basics and terminology for you!