# RPCs in CloudBehaviours

> Declare an RPC, call it from your game, and inject Cloud Code services into it.

> **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.

You write the procedure: a public method on your CloudBehaviour that runs in the cloud. Cloud Code generates the remote procedure call (RPC) for you: a matching method in the client bindings that your game calls to run it. Because the method body never runs on a player's device, it's the place to put validation, secret logic, and anything that awards or spends resources.

> **Note:**
>
> Every public method in the cloud script is a network endpoint that any client can call, including a malicious one. For guidance on locking an RPC down, refer to [Multiplayer game security in CloudBehaviours](/cloud-code/cloudbehaviours/secure-multiplayer-game-logic.md).

## Write a method your game can call

Cloud Code generates an RPC for each public, non-static, non-generic method on the cloud class, except methods marked `[SyncView]`, which define [per-player state views](/cloud-code/cloudbehaviours/per-player-state-views.md) instead. The method can be synchronous or asynchronous (returning `Task` or `Task<T>`), and it can take parameters and return a value:

```csharp title="Methods your game can call"
[StateScope(Scope.MultiplayerSession)]
public class MatchState : CloudBehaviour
{
    public void EndTurn() { /* ... */ }

    public int SubmitScore(int score) { /* ... */ return score; }

    public async Task<string> PlayTurn(string move) { /* ... */ return "ok"; }

    // Private methods stay in the cloud
    private void ResetRound() { /* ... */ }
}
```

Parameters and return values serialize as JSON, so use serializable types, such as primitives, strings, and plain C# classes. Parameters marked `[CloudCodeService]` are the exception: the runtime injects them, so they're never serialized and don't appear on the client. Refer to [Inject Cloud Code services](#inject-cloud-code-services).

Concrete classes, structs, and enums that you define in the cloud assembly are regenerated in the client bindings, with only their public fields and properties. Refer to [Model types crossing the client boundary](/cloud-code/cloudbehaviours/synchronized-state.md#model-types-crossing-the-client-boundary). The following types cause a compile-time error instead:

* Interfaces and abstract classes, because the runtime can't tell which concrete type to deserialize a value into.
* Types that derive from a .NET framework type, such as a collection, or from a type injected with `[CloudCodeService]`.
* Custom collection types. Use `List<T>` or an array instead.
* Types with a public property that can be read but not assigned, such as a computed property or a property without a public setter.
* Types declared in another assembly that references the Unity engine or Editor, because that assembly can't run in the cloud.

## Call an RPC from your game

The generated client bindings contain an awaitable method for each RPC. Every client-side call is asynchronous, even when the cloud method is synchronous, because each call travels over the network:

```csharp title="Calling RPCs through the bindings"
public async Task PlayAsync()
{
    var result = await matchStateClient.PlayTurn("rock");
    Debug.Log(result);
}
```

Each RPC response also carries the caller's synchronized state as it stands after the method runs, and the bindings apply it before the call returns. A client that calls an RPC doesn't need to hydrate separately. In a `MultiplayerSession` scope, the other players get the update as a push instead, unless you [control synchronization manually](/cloud-code/cloudbehaviours/synchronized-state.md#control-synchronization-manually).

## Inject Cloud Code services

To use a Cloud Code service inside an RPC, such as the generated interface for scheduling a timer or calling another CloudBehaviour, add it as a parameter marked with `[CloudCodeService]`. The runtime injects it, and it's stripped from the generated client method:

```csharp title="Injecting a service into an RPC"
[StateScope(Scope.MultiplayerSession)]
public class MatchState : CloudBehaviour
{
    public async Task StartTurn([CloudCodeService] IMatchState self)
    {
        await self.OnTurnTimeout().ScheduleAsync(TimeSpan.FromSeconds(30));
    }

    [CloudCodeAccess(Access.Service)]
    public void OnTurnTimeout() { /* ... */ }
}
```

On the client, `StartTurn` generates with no `self` parameter. The client never sees it.

### Register the generated interface

A generated `I{Behaviour}` interface only resolves if your module registers it. In the module's `ICloudCodeSetup`, call the generated `Add{Behaviour}Client()` extension for each behaviour you inject:

```csharp title="Registering the generated interface"
using Unity.Services.CloudCode.Core;

public class ModuleSetup : ICloudCodeSetup
{
    public void Setup(ICloudCodeConfig config)
    {
        config.AddMatchStateClient();
    }
}
```

If you inject an `I{Behaviour}` interface without registering it, the build fails with a generator error that names the missing `Add{Behaviour}Client()` call.

> **Note:**
>
> Mark every injected service parameter with `[CloudCodeService]`. If a Cloud Code service or a generated `I{Behaviour}` interface parameter is missing the attribute, the build fails with `CCSG136`.

For the full timer scheduling pattern, refer to [Timers in CloudBehaviours](/cloud-code/cloudbehaviours/timers.md). For calling another behaviour's endpoint, refer to [Cross-behaviour calls in CloudBehaviours](/cloud-code/cloudbehaviours/calling-another-behaviour.md).

## Additional resources

* [Get started with CloudBehaviours](/cloud-code/cloudbehaviours/get-started.md)
* [Turn-based game workflow](/cloud-code/cloudbehaviours/turn-based-game-workflow.md)
* [Access control in CloudBehaviours](/cloud-code/cloudbehaviours/access-control.md)
