Energy replication

Hi! The situation is: I have a “Energy” variable which grows with every frame on the server and on the client. This is like smoothly energy regeneration. I can’t replicate this variable every changing, because it is very expensive. But I will need to do some synchronization when i losing energy, meaning to cast spells etc…
A simplified version of the code looks like this:

.h



UENUM()
enum EHeroReplicationMask
{
    /** For Energy replication control. */
    EnergyReplicationActive = 0x01,

};

UCLASS()
class ARENA_API AHero : public ACharacter, public ICasterBase
{
    /** In OnRepEnergySync I do some sync with UMG. */
    UPROPERTY(ReplicatedUsing = OnRepEnergySync)
    float Energy;

    /** Contains replication mask. Required for replications optimization. */
    UPROPERTY()
    uint8 ReplicationMask;
};


.cpp



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

    /** COND_SimulatedOnly for example. */
    DOREPLIFETIME_CONDITION(AHero, Energy, COND_SimulatedOnly);
}

void AHero::PreReplication(IRepChangedPropertyTracker & ChangedPropertyTracker)
{
    DOREPLIFETIME_ACTIVE_OVERRIDE(AHero, Energy, ReplicationMask & EnergyReplicationActive);

    /** Removing flag, because we need replication once. */
    EnergyReplicationActive &= ~EnergyReplicationActive;
}

/** Calling on the client and on the server. */
void AHero::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

    /** For example our DeltaTime always ticking by 0.1f on the server.  */
    Energy += DeltaTime * 10.f;
}

/** Calling only on the server. */
void AHero::ModifyEnergy(float AddEnergy)
{
    /** simplify */
    Energy = FMath::Clamp<float>(Energy - 80.f, 0.f, 1000.f);

    /** Add flag. Without this there will be no replication. */
    ReplicationMask |= EnergyReplicationActive;
}


So, we starting the game and suppose we spent the energy (60 of 1000 remain). Now we call ModifyEnergy() and our sequence of calls will be looks like this:

ModifyEnergy() → Tick() → PreReplication() → Checking the variable change and send replication to client if it was…

Where:

Energy = 0.0 after ModifyEnergy() ->
Energy = 1.0 after Tick() ->
We replication (Energy = 1.f) to client.

Next step we waiting some time and our Energy was regenerated to 30.f (on the client and server). We do the same move: calling AHero::ModifyEnergy() again, where:

Energy = 0.0 after ModifyEnergy() ->
Energy = 1.0 after Tick() ->
And here the problem. The last replication to client was 1.f too. Engine not see the change of variable and will no replication. By the end we get desync. Where:

On the client Energy = 30.0
On the server Energy = 0.0

I can’t use RPC due to lack of flexibility (means maybe i want to sync Energy only for SimulatedProxy, how i can do this with RPC?)
I am tried to use DOREPLIFETIME_CONDITION_NOTIFY with REPNOTIFY_Always, but it still was worked a same.

Ok, like idea I can initializing “Energy” variable like a uint32 and do some manipulating of bits. I mean using 31 bits for Energy value and reserving last bit for switching to provoke replication. But is there really no solution for easier? Or maybe I missing something?

Hope you understand what I was mean. Thanks!

How is it able to go out of sync if you are only changing the variable on the server?

I am not sure why you think the variable is expensive to replicate? Its just a float and will rep when the actor reps. It will only rep when the server version is different to the client version. You are adding complexity to a non issue. Micro-optimising is the worst time of optimising unless your game is hitting bandwidth cap.

I do not quite understand your question. Please, can you explain what are you mean?

You mention that the server and client are de-synced. If a replicated variable is set by the server it will eventually replicate that change to the client to sync the client. Perhaps you are also setting the variable on the client?

You are mean if I replicate the Energy on every tick it’s ok? But if my game has alot of characters with energy?

When you do something in tick you should start to ask yourself the question “Is this really necessary?” and if the answer is “yes” then “is there any low hanging fruit that helps the performance without adding a lot of complexity” and if the answer is “no” then let it run on tick until it becomes a problem.

The property will only replicate when the actor replicates, not every time the property changes. So if the update rate is 20 times per second, then that property will only be considered 20 times a seconds.

Maybe you right guys and I should not worry much about this. Thanks, for answers!