Help My plugin's timer is not synchronized with the official server round times

GroSSoMineTTofr3

Пользователь
Messages
3
Reaction score
0
Hello,

I am developing a CounterStrikeSharp plugin for CS2.

My plugin needs to start an event at a specific time during the round. It works correctly for the first rounds, but after several rounds it becomes out of sync with the server round timer.

What is the best way to get the real round time or remaining round time directly from the server?

I would like to avoid timer drift and stay synchronized with the official round timer.

Thanks.
 
The root cause of your timer drift is that CSS timers (AddTimer) are not tied to the actual round clock — they just count real elapsed time from the moment they're created. Over several rounds, small discrepancies accumulate and things go out of sync.
The correct solution is to read the round time directly from the server's game state using CCSGameRules. This class exposes two properties that are exactly what you need:
RoundStartTime — the absolute server time at which the current round began.
RoundTime — the total duration of the round in seconds (reflects mp_roundtime).
Both values use the same internal server tick clock as Server.CurrentTime, so you can always compute exactly how many seconds have passed and how many remain:
C#:
float elapsed = Server.CurrentTime - gameRules.RoundStartTime;
float remaining = gameRules.RoundTime - elapsed;
To get the gameRules object, you find it through the cs_gamerules entity:
C#:
private CCSGameRules? GetGameRules()
{
    return Utilities.FindAllEntitiesByDesignerName<CCSGameRulesProxy>("cs_gamerules")
        .FirstOrDefault()?.GameRules;
}
Then to schedule your event at a specific point in the round without drift, you calculate the delay dynamically at round start instead of hardcoding it:
C#:
[GameEventHandler]
public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
{
    var gameRules = GetGameRules();
    if (gameRules == null) return HookResult.Continue;

    float elapsed = Server.CurrentTime - gameRules.RoundStartTime;
    float remaining = gameRules.RoundTime - elapsed;

    // Trigger your event when there are 30 seconds left on the round timer
    float targetRemaining = 30f;
    float delay = remaining - targetRemaining;

    if (delay > 0)
    {
        AddTimer(delay, () =>
        {
            // event
        });
    }

    return HookResult.Continue;
}
Because delay is computed from the live server clock every round, it self-corrects automatically — there is no accumulated error.
One practical note: avoid calling FindAllEntitiesByDesignerName on every tick or event, as it's expensive. Cache the result in a field and refresh it on OnMapStart:
C#:
private CCSGameRules? _gameRules;

public override void Load(bool hotReload)
{
    RegisterListener<Listeners.OnMapStart>(_ =>
    {
        _gameRules = Utilities.FindAllEntitiesByDesignerName<CCSGameRulesProxy>("cs_gamerules")
            .FirstOrDefault()?.GameRules;
    });
}
With this approach, all data is retrieved directly from the game’s authoritative state on the server, so it is always perfectly synchronized with the round timer used by the game itself. This would be the right solution for your situation.
 
Back
Top