Documentation

Cloud Code

Version: 3.0.0  
Unity 6.7 (6000.7)+
Open Unity Dashboard

Cloud Code

Version: 3.0.0  
Unity 6.7 (6000.7)+

Cross-behaviour calls in CloudBehaviours

Call an endpoint on another CloudBehaviour from inside your own, in another scope instance or another module.
Read time 5 minutesLast updated 12 days ago

Important
This page describes an experimental feature that might change significantly before release. It's not recommended to use or rely on experimental features in production environments due to potential instability.
From inside a CloudBehaviour, you can call an endpoint on another CloudBehaviour. How you make the call depends on where the target runs:

Target

How to call it

A CloudBehaviour in another scope instance, in the same moduleUse the generated interface and
ForScope
. Refer to Call a CloudBehaviour in another scope instance.
A CloudBehaviour in another scope instance, in another moduleCall it as a Cloud Code module endpoint. Refer to Call a CloudBehaviour in another module.
The same CloudBehaviour in the current scope instanceCall the method directly, the same as any other C# method.
A different CloudBehaviour in the current scope instanceYou can't call it and wait for its result. Refer to Call the current scope instance.
To run an endpoint on the same CloudBehaviour later instead of now, schedule a timer. Refer to Timers in CloudBehaviours.

Call a CloudBehaviour in another scope instance

The Unity Editor generates an
I{Behaviour}
interface for every CloudBehaviour in your module. Inject the interface with
[CloudCodeService]
, then call a member on the interface to get back an
ICall
that hasn't run yet. To run the
ICall
, pass the target's scope ID to
ForScope(scopeId)
.
An
ICall
does nothing until you run it. If you create an
ICall
and never call
ForScope
or
ScheduleAsync
on it, the build fails with
CCSG133
.
The following example shows a
MultiplayerSession
-scoped
MatchRoom
that updates each player's
PlayerRecord
at the end of a match.
PlayerRecord
is
Player
-scoped because it belongs to one player and outlives any single match:
[StateScope(Scope.Player)]public class PlayerRecord : CloudBehaviour{ public int MatchesPlayed; public int PersonalBest; [CloudCodeAccess(Access.Service)] public int RecordMatch(int score) { MatchesPlayed++; if (score > PersonalBest) PersonalBest = score; return PersonalBest; }}[StateScope(Scope.MultiplayerSession)]public class MatchRoom : CloudBehaviour{ [CloudCodeSerializeProperty] private Dictionary<string, int> _scores = new(); public async Task EndMatch([CloudCodeService] IPlayerRecord playerRecord) { foreach (var entry in _scores) { var best = await playerRecord.RecordMatch(entry.Value).ForScope(entry.Key); Logger.LogInformation("{PlayerId} personal best is now {Best}", entry.Key, best); } }}
ForScope
takes the scope ID of the target CloudBehaviour's own
[StateScope]
, not the caller's scope ID. In this example,
entry.Key
is a player ID, which matches the
Scope.Player
scope of
PlayerRecord
.
ForScope
can't target the scope instance that's already running your code. If you pass
Context.ScopeId
, the build fails with
CCSG138
, because a scope instance can't start a second call while it's still running the current one. Refer to Call the current scope instance.
Awaiting the call returns the endpoint's return value directly. The caller doesn't receive the target's synchronized state. If the call changes that state, Cloud Code pushes the new state to the clients in the target's scope, the same as after any other endpoint call, unless the target has turned off
AutoSynchronize
. In this example, each player receives their updated
PlayerRecord
. For more information, refer to Control synchronization manually.

Get a scope ID

Read the scope ID to pass to
ForScope
from the CloudBehaviour's
Context
:

Target scope

Scope ID

Notes

Scope.Player
Context.PlayerId
The ID of the player making the current call. For the other players in a session, use
Context.Session.PlayerIds
.
Scope.MultiplayerSession
Context.Session.Id
Only available when the calling CloudBehaviour is
MultiplayerSession
-scoped.
Context.Session
is
null
otherwise.
When a service such as a timer or another CloudBehaviour calls the endpoint,
Context.PlayerId
is
null
. In those calls, pass the scope ID as an endpoint argument instead.

Restrict access to the target endpoint

A cross-behaviour call arrives with no player identity. The player ID you pass to
ForScope
selects only which scope instance runs the endpoint. The player ID doesn't make the call on that player's behalf.
The target is still an endpoint that a client can call directly. Mark the target
[CloudCodeAccess(Access.Service)]
so no player can call it directly. Refer to Access control in CloudBehaviours.

Call a CloudBehaviour in another module

The generated interface only reaches CloudBehaviours in the same module. To call a CloudBehaviour in another module, call its endpoint as a Cloud Code module endpoint, and pass the scope ID of the target scope instance. The target scope instance must be different from the one that's running your code. Refer to Call the current scope instance.

Call the current scope instance

A scope instance runs one call at a time. While your endpoint runs, any other call into the same scope instance waits for your endpoint to finish. How a call into the current scope instance fails depends on how you make the call:
  • If you call through the generated interface with
    ForScope
    , the call fails immediately. If you pass
    Context.ScopeId
    , the build fails with
    CCSG138
    . If you pass another value that matches the current scope ID, the call throws an
    InvalidOperationException
    at runtime.
  • If you call a Cloud Code module endpoint, including a CloudBehaviour in another module, nothing stops the call. The call waits for your endpoint, and your endpoint waits for the call, until the request fails with a
    Gateway Timeout
    error.
To run code on the same CloudBehaviour in the current scope instance, call the method directly, the same as any other C# method. You can't call a different CloudBehaviour in the current scope instance and wait for its result.
To share logic between modules in the same scope, refer to Avoid calls into your own scope.

Requirements

To call another CloudBehaviour through its generated interface, you must do the following:
  • Put both CloudBehaviours in the same module. If the target is in another module, refer to Call a CloudBehaviour in another module.
  • Mark the injected parameter with
    [CloudCodeService]
    . Otherwise, the build fails with
    CCSG136
    . Refer to Inject services into an RPC.
  • Register the target's interface in your module's
    ICloudCodeSetup
    . In this example, call
    config.AddPlayerRecordClient()
    . Refer to Register the generated interface.
  • Pass only the endpoint's own arguments. The Editor removes every
    [CloudCodeService]
    parameter from
    I{Behaviour}
    , so the call has no argument for the injected parameter.
  • Give each CloudBehaviour class in the module a unique name, even across namespaces.
Note
A timer always runs on the CloudBehaviour that scheduled it, so calling
ScheduleAsync
on another CloudBehaviour's
I{Behaviour}
fails the build with
CCSG132
. To defer work on another CloudBehaviour, schedule your own endpoint and call
ForScope
from inside it. Refer to Timers in CloudBehaviours.

Additional resources