Integrate with other Unity services

To use the full potential of Cloud Code, you can connect it with other Unity Gaming Services through the C# Service SDKs or REST APIs.

The C# SDKs are more simple and provide a more consistent experience. However, if the UGS service you want to use doesn't have a Cloud Code C# SDK yet, you can connect with it through its REST API.

Use the UGS SDKs

To access the Cloud Code C# services SDK from NuGet, search for Com.Unity.Services.CloudCode.Apis. Once you install the package, you can use it within your C# modules to simplify the way you connect with other Unity services.

Note: The documentation for Cloud Code C# SDKs is incomplete. For now, you can refer to the JavaScript documentation as it has a similar structure. Please also make use of your IDE's auto complete and intellisense features.

For a full list of Cloud Code C# SDKs, refer to the Available Libraries page.

The GameApiClient class

The GameApiClient class is a wrapper around the Cloud Code C# SDKs for each service. It provides a simplified interface when you call the UGS services from Cloud Code modules.

To use the GameAPIClient interface, register the GameApiClient as a singleton in your ICloudCodeSetup configurator:

C#

public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
        }
    }

Integrate with UGS

You can use the GameApiClient class in any function that you attribute with CloudCodeFunction. To use the GameApiClient class, pass in the IGameApiClient interface as a parameter to the function.

The following example demonstrates how to save and read data from Cloud Save:

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Shared;
using Unity.Services.CloudSave.Model;

namespace ExampleModule;

public class CloudSaveSdkSample
{
    private static ILogger<CloudSaveSdkSample> _logger;

    public CloudSaveSdkSample(ILogger<CloudSaveSdkSample> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("SaveData")]
    public async Task SaveData(IExecutionContext context, IGameApiClient gameApiClient, string key, object value)
    {
        try
        {
            await gameApiClient.CloudSaveData.SetItemAsync(context, context.AccessToken, context.ProjectId,
                context.PlayerId, new SetItemBody(key, value));
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to save data. Error: {Error}", ex.Message);
            throw new Exception($"Failed to save data for playerId {context.PlayerId}. Error: {ex.Message}");
        }
    }

    [CloudCodeFunction("GetData")]
    public async Task<object> GetData(IExecutionContext context, IGameApiClient gameApiClient, string key)
    {
        try
        {
            var result = await gameApiClient.CloudSaveData.GetItemsAsync(context, context.AccessToken,
                context.ProjectId, context.PlayerId, new List<string> { key });
            return result.Data.Results.First().Value;
        }
        catch (ApiException ex)
        {
            _logger.LogError("Failed to get data. Error: {Error}", ex.Message);
            throw new Exception($"Failed to get data for playerId {context.PlayerId}. Error: {ex.Message}");
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
        }
    }
}

Connect with UGS through REST APIs

If you want to use a service that doesn't have a C# SDK yet, you can also connect with the services directly through their REST API.

Authentication

Depending on your use case, you can use either the AccessToken or the ServiceToken to authenticate the API call. If you use the ServiceToken to authenticate UGS Client APIs, ensure the service you want to call supports service tokens. For a list of services and use cases that support service tokens, refer to the Service and access token support documentation.

Note: If the service you want to call provides a Cloud Code C# SDK, it is recommended to use the SDK instead of calling the service API directly. For more information, refer to the list of Available libraries.

Calling the API

Dependency Injection allows you to create API interfaces as singletons and inject them into your modules.

In addition to UGS APIs, you can also implement external APIs as singletons. For an example of how to implement an external API, refer to the documentation on how to integrate with external services.

For example, you can define an interface for the User Generated Content API to create and retrieve content:

Note: You need to enable the User Generated Content service in your project before you can use it.

C#

using System.Net;
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Authenticators;
using Unity.Services.CloudCode.Core;

namespace GeneratedContentSample;
public interface IGeneratedContentApi
{
    public Task<GeneratedContentGetApiResponse?> GetGeneratedContentForPlayer(IExecutionContext context);
    public Task<GeneratedContentPostApiResponse?> CreateContent(IExecutionContext context);
}

public class GeneratedContentPostApiResponse
{
    public string? uploadThumbnailUrl { get; set; }
    public string? uploadContentUrl { get; set; }
    public string? version { get; set; }
    public Content content { get; set; }
}

public class GeneratedContentGetApiResponse
{
    public int? offset { get; set; }
    public int? limit { get; set; }
    public int? total { get; set; }
    public Content[] results { get; set; }
}

public class Content
{
    public string id { get; set; }
    public string? name { get; set; }
    public string? customId { get; set; }
    public string? description { get; set; }
    public string visibility { get; set; }
    public string moderationStatus { get; set; }
    public string? version { get; set; }
    public string createdAt { get; set; }
    public string updatedAt { get; set; }
    public string deletedAt { get; set; }
    public string projectId { get; set; }
    public string environmentId { get; set; }
    public string creatorAccountId { get; set; }
    public string? thumbnailUrl { get; set; }
    public string? downloadUrl { get; set; }
    public string? portalUrl { get; set; }
    public string? metadata { get; set; }
    public float? averageRating { get; set; }
    public bool isUserSubscribed { get; set; }
    public string assetUploadStatus { get; set; }
    public string thumbnailUploadStatus { get; set; }
    public string? webhookEventName { get; set; }
}


public class GeneratedContentApi : IGeneratedContentApi
{
    private readonly RestClient _httpClient;

    public GeneratedContentApi()
    {
        _httpClient = new RestClient("https://ugc.services.api.unity.com/v1/");
    }

    public async Task<GeneratedContentGetApiResponse?> GetGeneratedContentForPlayer(IExecutionContext context)
    {
        var request = new RestRequest($"content/search")
        {
            Authenticator = new JwtAuthenticator(context.AccessToken)
        };

        // Pass through the analytics user id and Unity installation id to the service.
        if (context.AnalyticsUserId != null)
            request = request.AddHeader("analytics-user-id", context.AnalyticsUserId);
        if (context.UnityInstallationId != null)
            request = request.AddHeader("unity-installation-id", context.UnityInstallationId);


        RestResponse response = await _httpClient.ExecuteAsync(request);
        if (response.StatusCode != HttpStatusCode.OK)
            throw new Exception("Failed to get User Generated Content: " + response.Content);

        return JsonConvert.DeserializeObject<GeneratedContentGetApiResponse?>(response.Content!);
    }

    public async Task<GeneratedContentPostApiResponse?> CreateContent(IExecutionContext context)
    {
        var request = new RestRequest($"projects/{context.ProjectId}/environments/{context.EnvironmentId}/content", Method.Post)
        {
            Authenticator = new JwtAuthenticator(context.AccessToken)
        }.AddJsonBody(new
        {
            name = "a festive treat",
            description = "banasco is a traditional british toast",
        });

        // Pass through the analytics user id and Unity installation id to the service.
        if (context.AnalyticsUserId != null)
            request = request.AddHeader("analytics-user-id", context.AnalyticsUserId);
        if (context.UnityInstallationId != null)
            request = request.AddHeader("unity-installation-id", context.UnityInstallationId);


        RestResponse response = await _httpClient.ExecuteAsync(request);
        if (response.StatusCode != HttpStatusCode.OK)
            throw new Exception("Failed to create user generated content: " + response.Content);

        return JsonConvert.DeserializeObject<GeneratedContentPostApiResponse>(response.Content!);

    }

}

Then you can add the GeneratedContentApi as a singleton:

C#

public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton<IGeneratedContentApi>(new GeneratedContentApi());
        }
    }

You can then pass in IGeneratedContentApi interface as a function parameter to your module functions, and access the UGC API.

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Core;

namespace GeneratedContentSample;

public class GeneratedContent
{
    private readonly ILogger<GeneratedContent> _logger;
    public GeneratedContent(ILogger<GeneratedContent> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("AddContent")]
    public async Task<GeneratedContentPostApiResponse?> AddUserGeneratedContent(IExecutionContext ctx, IGeneratedContentApi contentClient)
    {
        try
        {
            return await contentClient.CreateContent(ctx);
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to add content. Error: {Error}", e.Message);
            throw new Exception($"Failed to add content for player {ctx.PlayerId}. Error: {e.Message}");
        }
    }

    [CloudCodeFunction("GetContent")]
    public async Task<GeneratedContentGetApiResponse?> GetContent(IExecutionContext ctx, IGeneratedContentApi contentClient)
    {
        try
        {
            return await contentClient.GetGeneratedContentForPlayer(ctx);
        }
        catch (Exception e)
        {
            _logger.LogError("Failed to get content. Error: {Error}",  e.Message);
            throw new Exception($"Failed to get content. Error: {e.Message}");
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton<IGeneratedContentApi>(new GeneratedContentApi());
        }
    }
}