Timers in CloudBehaviours
Schedule, cancel, and understand the limits of a server-authoritative CloudBehaviour timer.
Read time 4 minutesLast updated 14 days ago
Timers are server-authoritative. The cloud schedules a call back into one of your CloudBehaviour's own endpoints, so a player who closes the game can't dodge a turn clock.
Schedule a timer
Scheduling uses the same generated interface used for cross-behaviour calls. Inject it, name the endpoint with its real arguments, and run the call with :
I{Behaviour}ScheduleAsync(delay)[StateScope(Scope.MultiplayerSession)]public class TurnClock : CloudBehaviour{ public string CurrentTimerId; public int TurnNumber; [CloudCodeAccess(Access.SessionHost)] public async Task StartTurn([CloudCodeService] ITurnClock self, [CloudCodeService] ITimerService timers) { if (CurrentTimerId != null) { var previous = await timers.GetTimerAsync(CurrentTimerId); previous.Cancel(); // Don't leave an orphaned timer holding a slot } CurrentTimerId = await self.EndTurn(TurnNumber).ScheduleAsync(TimeSpan.FromSeconds(30)); } [CloudCodeAccess(Access.Service)] public void EndTurn(int turnNumber) { if (turnNumber != TurnNumber) return; // A stale timer fires against an old turn CurrentTimerId = null; TurnNumber++; }}
Arguments are ordinary typed parameters on the endpoint. registers a timer against the deployed endpoint and returns the timer's ID.
ScheduleAsyncStartTurn[CloudCodeAccess]EndTurnCurrentTimerIdGetTimerAsyncTo inject , register it in your module's with . Refer to Register the generated interface.
ITurnClockICloudCodeSetupconfig.AddTurnClockClient()Cancel a timer
ICall[CloudCodeService] ITimerServiceYou don't need to schedule a timer, only to cancel or query one. uses the timer service for you. Don't call directly in a CloudBehaviour: the build fails with .
ITimerServiceScheduleAsyncITimerService.RegisterTimerAsyncCCSG137GetTimerAsyncEndTurnThe following example cancels the current turn's timer:
[CloudCodeAccess(Access.SessionHost)]public async Task CancelTurn([CloudCodeService] ITimerService timers){ if (CurrentTimerId == null) return; var timer = await timers.GetTimerAsync(CurrentTimerId); timer.Cancel(); CurrentTimerId = null;}
Persist the timer ID
The timer service returns a timer's ID only once, when you schedule it, and it has no way to list your timers later. If you'll need to cancel a timer, store its ID in a persisted member. An ID kept only in memory isn't saved: it can survive between calls, but it's lost whenever Cloud Code reloads your CloudBehaviour, for example after a period of inactivity or a redeploy.
In the preceding examples, is a public field, so it persists by default, but it also synchronizes to every client in the session. To persist the ID without sending it to clients, make it private and mark it :
CurrentTimerId[CloudCodeSerializeProperty][CloudCodeSerializeProperty]private string _turnTimerId; // Persisted, never sent to a client[CloudCodeAccess(Access.SessionHost)]public async Task StartTurn([CloudCodeService] ITurnClock self){ _turnTimerId = await self.EndTurn(TurnNumber).ScheduleAsync(TimeSpan.FromSeconds(30));}[CloudCodeAccess(Access.SessionHost)]public async Task CancelTurn([CloudCodeService] ITimerService timers){ if (_turnTimerId == null) return; (await timers.GetTimerAsync(_turnTimerId)).Cancel(); _turnTimerId = null;}
Lock the target down, and don't expect a player
A scheduled endpoint is still an endpoint, so a client can call it directly. Mark it so the timer still fires while clients are blocked.
[CloudCodeAccess(Access.Service)]A timer can target only a public instance endpoint on the CloudBehaviour. A private or static method isn't on , so scheduling one doesn't compile.
I{Behaviour}What a timer callback pushes
A timer-triggered invocation pushes synchronized state and declared events to the scope's clients: the player on a -scoped CloudBehaviour, or every session member on a -scoped CloudBehaviour. If the CloudBehaviour has per-player state views, each client receives its own view.
PlayerMultiplayerSessionLimits
Timers have the following limits:
- The delay must be greater than zero and no more than 24 hours. The 24 hours are shared: the delays of all timers already pending on the same player or session, plus the new one, can't exceed 24 hours.
- A player or session can hold at most 10 pending timers at once, shared by every module and CloudBehaviour that uses that scope. A timer frees its slot when it fires or when you cancel it.
- There's no recurring timer. For a repeating clock, have the scheduled endpoint schedule the next one.
- Only schedule your own CloudBehaviour's endpoints. Calling on another CloudBehaviour's
ScheduleAsyncfails the build withI{Behaviour}.CCSG132