文档

支持

Cloud Code

模块结构

Understand the structure of a Cloud Code module project and how it exposes endpoints.
阅读时间10 分钟最后更新于 1 个月前

Cloud Code 模块是简单的 .NET 项目,其中公开了游戏客户端可以调用的终端。模块采用与 .NET 类库类似的范式。例如,一个标准 C# 类可能如下所示:
namespace ModulesExample;public class Example{ private static string[] GetCheatCodes() { return new string[] { "LEAVEMEALONE", "ASPIRINE", "BIGBANG" }; } public void ChangeHaircut(string haircutName) { }}
编译后,任何对
Example
类的引用都只能使用
ChangeHaircut()
方法。
using ModulesExample;class UsageExample{ public void GameLoop() { Example.ChangeHaircut("buzzcut"); // Compile error "Cannot access private method 'GetCheatCodes()' here" var cheatCodes = Example.GetCheatCodes(); }}
开发者可以通过访问修饰符选择库接口。Cloud Code 模块的工作方式相同,但使用的是
[CloudCodeFunction("ChangeHaircut")]
属性。
借助 Cloud Code 模块,您可以创建独立的服务器授权库。因此,您可以在服务器上运行游戏逻辑,并从客户端调用游戏逻辑,从而将游戏逻辑与客户端分离并提高安全性。模块支持代码重用,并提供可用于管理游戏逻辑的接口。

C# 项目

Cloud Code 模块项目是类库 C# 项目。此项目也可以是某个解决方案的一部分。最简单的项目结构如下所示:
├─ Main.csproj└─ Dependencies└─ HelloWorld.cs
如果是在一个解决方案中,可能如下所示:
Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs
这个解决方案还可以包含相互引用的多个项目。因此,您可以轻松添加测试项目,并启用代码重用。以下示例显示了一个有效的项目结构:
Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs├─ Tests.csproj └─ Dependencies └─ Tests.cs├─ Other.csproj └─ Dependencies └─ Class1.cs
但是,只有主项目可以包含 Cloud Code 函数。 请参阅编写单元测试,详细了解如何为 Cloud Code 模块编写单元测试。 请参阅 Cloud Code 限制,了解适用的存储限制。

主项目

尽管解决方案可以包含多个 C# 项目,但每个模块只能有一个主项目。主项目包含所有模块入口点(即具有
CloudCodeFunction
属性的函数)。
主项目也是您部署到 Cloud Code 服务的项目。主项目中未引用的项目和依赖项不会部署到 Cloud Code 服务。 您需要让主项目的名称与部署到 Cloud Code 服务的存档文件的名称匹配。

示例项目

最简单的模块项目可以具有以下结构:
Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs
以下示例定义的 Cloud Code 模块包含一个返回
Hello World!
字符串的
Hello
函数:
using Unity.Services.CloudCode.Core;namespace Sample;public class HelloWorld{ [CloudCodeFunction("Hello")] public string PrintHello() { return "Hello World!"; }}

Cloud Code NuGet 包

作为 .NET 项目,Cloud Code 模块项目也使用 NuGet 来共享打包的代码。 要了解有关 NuGet 的更多信息,请参阅 Microsoft 文档 Cloud Code 提供了两个可以包含在模块中的包:
  • com.Unity.Services.CloudCode.Core
    :此包中包含 Cloud Code 的核心功能,包括
    CloudCodeFunction
    属性。所有 Cloud Code 模块都需要此包。
  • com.Unity.Services.CloudCode.Apis
    :这是一个可选包,其中包含可用于与 Cloud Code 服务交互的 Unity Gaming Services(Unity 游戏服务)API。您可以使用此包调用其他 Cloud Code SDK,例如 Cloud Save。
如需了解更多信息,请参阅有关如何使用 Jetbrains RiderVisual Studio 在项目中安装和使用 NuGet 包的文档。

代码结构

为了避免代码结构问题,请遵循以下准则。

CloudCodeFunction 属性

要将方法标记为 Cloud Code 函数并使其成为可行的 Cloud Code 模块终端,请使用
CloudCodeFunction
属性。您只能在模块的主类中使用 Cloud Code 函数属性。
为了调用该函数,需要调用属性的名称。

方法签名

使用以下结构作为模块函数的方法签名。
[CloudCodeFunction("FunctionName")]public async Task<ReturnType> FunctionName(IGameApiClient gameApiClient, IExecutionContext ctx, string parameter1, string parameter2){ ...}

对模块使用单例模式

您可以使用单例模式来与 Cloud Code C# 包和外部服务调用进行集成。单例模式可提高 Cloud Code 中的性能和可扩展性,并确保您不会为同一对象创建多个实例。 您可以使用依赖项注入
IGameApiClient
IPushClient
接口注入到模块中。

传递参数

您可以将参数传递给模块函数。

原始类型和复杂类型

包括任何原始类型(例如
int
string
bool
float
)以及复杂类型(例如
List<T>
Dictionary<T>
Dictionary<string, T>
)。
例如,可以将
string
List<string>
传递给模块函数:
{ "params": { "parameter1": "a", "parameter2": ["b", "c"] }}
定义具有相同参数的模块函数:
[CloudCodeFunction("SimpleParams")]public async Task SimpleParams(IExecutionContext ctx, string parameter1, List<string> parameter2){ return parameter1 + parameter2[0] + parameter2[1];}

自定义类型

您还可以传递在模块项目中定义的任何自定义类型。 例如,可以将 JSON 对象传递给模块对象:
{ "params": { "player": { "name": "player", "health": 50, "stamina": 20, "inventory": { "count": 5, "weight": 20 } } }}
在您的模块项目中定义一个
Player
Inventory
类和一个函数:
public class Player{ public string Name { get; set; } public int Health { get; set; } public int Stamina { get; set; } public Inventory Inventory { get; set; }}public class Inventory{ public int Count { get; set; } public int Weight { get; set; }}[CloudCodeFunction("CustomParams")]public async Task<string> CustomParams(IExecutionContext ctx, Player player){ return $"{player.Name} has {player.Health} health and {player.Stamina} stamina and {player.Inventory.Count} items in their inventory with a total weight of {player.Inventory.Weight}";}

可用接口

Cloud Code 模块可以使用
IGameApiClient
IAdminApiClient
IPushClient
IExecutionContext
接口来扩展模块的功能。
ICloudCodeSetup
接口可用于管理模块的依赖项。请在依赖项注入文档中了解更多相关信息。

IGameApiClient 接口

IGameApiClient
接口提供了一种访问 Cloud Code C# Client SDK 的方法。您可以使用依赖项注入
IGameApiClient
注入到模块中。
  1. 定义一个实现
    ICloudCodeSetup
    接口的类,并添加
    GameApiClient
    为单例:
    public class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); }}
  2. IGameApiClient
    接口作为函数参数传入模块函数。

IAdminApiClient 接口

IAdminApiClient
接口提供了一种访问 Cloud Code C# Admin SDK 的方法。您可以使用依赖项注入
IAdminApiClient
注入到模块中。
  1. 定义一个实现
    ICloudCodeSetup
    接口的类,并添加
    AdminApiClient
    为单例:
    public class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(AdminApiClient.Create()); }}
  2. IAdminApiClient
    接口作为函数参数传入模块函数。

IPushClient 接口

IPushClient
接口提供了一种向玩家发送推送通知的方法。您可以使用依赖项注入
IPushClient
注入到模块中。
  1. 定义一个实现
    ICloudCodeSetup
    接口的类,并添加
    PushClient
    为单例:
    public class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(PushClient.Create()); }}
  2. PushClient
    对象作为函数参数传入模块函数。
要详细了解如何从 Cloud Code 发送推送消息,请参阅推送消息

IExecution 上下文

要在模块中调用其他 UGS 服务,可以使用
IExecutionContext
接口获取有关当前调用的额外信息。您可以使用以下值访问服务 SDK,并查看哪个玩家发出了请求。
  • ProjectId
    :调用方通过身份验证的 Project ID。
  • PlayerId
    :执行脚本的玩家 ID。注意:当服务调用脚本时,玩家 ID 不可用。
  • EnvironmentId
    :模块的环境 ID。
  • EnvironmentName
    :模块的环境名称。
  • AccessToken
    :玩家用于向 Cloud Code 进行身份验证的 JSON Web Token (JWT) 凭据。
  • UserId
    :服务帐户的用户 ID(注意:当服务调用脚本时,用户 ID 不可用)。
  • Issuer
    :签发者或服务帐户令牌(注意:当玩家调用脚本时,签发者不可用)。例如,如果 Multiplay 发出调用,则签发者为
    multiplay
  • ServiceToken
    :Cloud Code 服务帐户 JWT 凭据。
  • AnalyticsUserId
    :玩家的 Analytics 用户 ID(注意:当服务调用脚本时,Analytics 用户 ID 不可用)。
  • UnityInstallationId
    :玩家的 Unity 设备安装 ID(注意:当服务调用脚本时,Unity 安装 ID 不可用)。
您可以使用执行上下文获取玩家 ID 并向不同的 Unity Gaming Services(Unity 游戏服务)进行身份验证。您可以通过类的构造函数以及具有
CloudCodeFunction
属性的任何方法获取执行上下文。
例如,您可以使用执行上下文来调用 Cloud Save 获取玩家密钥:
using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Logging;using Unity.Services.CloudCode.Apis;using Unity.Services.CloudCode.Core;using Unity.Services.CloudCode.Shared;namespace Sample;public class HelloWorld{ private static ILogger<HelloWorld> _logger; public CloudSaveSdkSample(ILogger<HelloWorld> logger) { _logger = logger; } [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 from Cloud Save. Error: {Error}", ex.Message); throw new Exception($"Failed to get data for playerId {ctx.PlayerId}. Error: {e.Message}"); } } public class ModuleConfig : ICloudCodeSetup { public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); } }}

令牌身份验证

AccessToken
ServiceToken
JWT,也是
IExecutionContext
接口的属性。
您可以使用这些令牌对 Cloud Code 的其他 Unity Gaming Services(Unity 游戏服务)调用进行身份验证。

令牌类型

来源

数据访问

用途

accessToken
Authentication 服务生成。仅限经过身份验证的玩家。
AccessToken
是用于对 Cloud Code 调用进行身份验证的 JWT。您可以将此令牌传递给其他 UGS 服务来访问经过身份验证的玩家的数据。
serviceToken
由 Cloud Code 生成。跨玩家数据访问。
ServiceToken
是用于调用其他 UGS 服务并处理跨玩家数据的令牌。
您配置的任何访问控制规则都会影响
AccessToken
以下示例显示了如何使用
AccessToken
通过 Cloud Save 访问经过身份验证的玩家的数据:
using Microsoft.Extensions.DependencyInjection;using Unity.Services.CloudCode.Apis;using Unity.Services.CloudCode.Core;using Unity.Services.CloudCode.Shared;using Unity.Services.CloudSave.Model;namespace TokenSample;public class TokenSample{ [CloudCodeFunction("GetKeysForAuthenticatedPlayer")] public Task<ApiResponse<GetKeysResponse>> GetPlayerKeys(IExecutionContext ctx, IGameApiClient gameApiClient) { try { // Using ctx.AccessToken to access the data for the player calling the function return gameApiClient.CloudSaveData.GetKeysAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId); } catch (ApiException e) { throw new Exception($"Failed to get keys for playerId {ctx.PlayerId}. Error: {e.Message}"); } } public class ModuleConfig : ICloudCodeSetup { public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); } }}
要访问所有玩家的数据,您可以使用
ServiceToken
并传入
playerId
的参数:
using Microsoft.Extensions.DependencyInjection;using Unity.Services.CloudCode.Apis;using Unity.Services.CloudCode.Core;using Unity.Services.CloudCode.Shared;using Unity.Services.CloudSave.Model;namespace TokenSample;public class TokenSample{ [CloudCodeFunction("GetKeysForAnyPlayer")] public Task<ApiResponse<GetKeysResponse>> GetPlayerKeys(IExecutionContext ctx, IGameApiClient gameApiClient, string playerId) { try { // Using ctx.ServiceToken to access the data for any player return gameApiClient.CloudSaveData.GetKeysAsync(ctx, ctx.ServiceToken, ctx.ProjectId, playerId); } catch (ApiException e) { throw new Exception($"Failed to get keys for playerId {ctx.PlayerId}. Error: {e.Message}"); } } public class ModuleConfig : ICloudCodeSetup { public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); } }}
如需了解这两个令牌之间差异的深入说明,请参阅服务和访问令牌文档。

Cloud Code 模块元数据

要获取 Cloud Code 模块及其元数据,您可以向 Cloud Code 服务发送
GET
请求。响应是具有以下结构的 JSON 对象:
{ "name": "string", "language": "CS", "tags": { "property1": "string", "property2": "string" }, "signedDownloadURL": "string", "dateCreated": "2022-04-05T09:12:13Z", "dateModified": "2022-04-05T09:12:13Z"}

标签

标签是可以附加到 Cloud Code 模块的键/值对。您可以在标签中记录所需的任何信息,例如模块的版本、作者或提交哈希(如果使用版本控制)。 模块最多可以存储 10 个标签,每个标签键和标签值最多可以使用 128 个字符。

签名下载 URL

签名下载 URL (Signed Download URL) 是一种可用于下载模块存档文件的临时 URL。