Download

Update WorldTimerManager before RPC's execute?

Can we change the Timer Manager so that it updates all active timers BEFORE RPC’s are executed? I’m building a relatively simple Cooldown system, and the Server checks to see if the client can fire a weapon when they receive an RPC by checking whether it’s still in cooldown or not.

The problem is, the RPC is executed first before the Timer Manager has updated all timers that frame - so it always triggers as false. I have to do some pretty gross workaround to prevent this from happening.



bool UECGame_AbilityBase::IsInCooldown(const float Tolerance /*= 0.f*/) const
{
    // Server can't do the 'Cooldown' check normally for remote clients, because it might be running at a different rate to the clients
    // Timer manager may not have ticked this frame when we receive the RPC, so we need to add delta from the previous frame.
    if (OwningOrb->Role == ROLE_Authority && !OwningOrb->IsLocallyControlled())
    {
        if (Tolerance < 0.f)
        {
            return false;
        }
        else
        {
            const UWorld* MyWorld = GetWorld();
            const float CoolValue = MyWorld->GetTimerManager().GetTimerRemaining(TH_CooldownTimer);
            if (CoolValue <= 0.f)
            {
                return false;
            }
            else
            {
                const float WorldDelta = MyWorld->GetDeltaSeconds();
                return CoolValue < (WorldDelta + Tolerance) ? false : true;
            }
        }
    }
    else
    {
        return GetWorld()->GetTimerManager().GetTimerRemaining(TH_CooldownTimer) > 0.f;
    }
}


Note that if I do this using an old-school style timer (e.g, save off the time to a float and compare it to GetWorld()->GetTimeSeconds()) then it works fine - but of course I lose the ability to call a function when the time expires. I feel like this is a bit of an oversight in the timer system.

I use SetTimer and IsTimerActive for my cooldowns without any problems. If the server is under heavy load and the tick rate drops to say 10 ticks/second you still have a 100ms resolution to your timer. How short are your cooldowns, ours are in the couple of seconds range?

Much shorter in most cases, 0.1 - 0.3 seconds etc - but some are up to 10 seconds long. This occurs both in PIE and a packaged build accross different machines, and the network is under very low load in general.

Looking deeper into the code in UnrealEngine.cpp, the Network Tick is dispatched before the Timer Tick, so the timer still has a very short amount of time left to execute when the RPC arrives, and the server still thinks it’s in cool-down unless I add the delta from the previous frame.

While it does work and solves the problem, it just feels a bit gross. Out of interest, when you “set” timers for cooldowns - which Tick Group does your cooldown object belong too?

I get your point now, but for me that is slighty different than a “cooldown”. To avoid messing up same-frame RPC from the clients due to lag or whatever I use a queue on the server and dequeue on tick.

The tick group is TG_PostPhysics, but as I use the timer as blocker regardless of its current time I assume it doesn’t matter with our longer delays?

Ah yeah I think the missing link there is the queuing up part… perhaps I need to look into that on my end as well!