模块结构
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) { }}
ExampleChangeHaircut()开发者可以通过访问修饰符选择库接口。Cloud Code 模块的工作方式相同,但使用的是using ModulesExample;class UsageExample{ public void GameLoop() { Example.ChangeHaircut("buzzcut"); // Compile error "Cannot access private method 'GetCheatCodes()' here" var cheatCodes = Example.GetCheatCodes(); }}
[CloudCodeFunction("ChangeHaircut")]C# 项目
Cloud Code 模块项目是类库 C# 项目。此项目也可以是某个解决方案的一部分。最简单的项目结构如下所示:如果是在一个解决方案中,可能如下所示:├─ Main.csproj└─ Dependencies└─ HelloWorld.cs
这个解决方案还可以包含相互引用的多个项目。因此,您可以轻松添加测试项目,并启用代码重用。以下示例显示了一个有效的项目结构:Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs
但是,只有主项目可以包含 Cloud Code 函数。 请参阅编写单元测试,详细了解如何为 Cloud Code 模块编写单元测试。 请参阅 Cloud Code 限制,了解适用的存储限制。Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs├─ Tests.csproj └─ Dependencies └─ Tests.cs├─ Other.csproj └─ Dependencies └─ Class1.cs
主项目
尽管解决方案可以包含多个 C# 项目,但每个模块只能有一个主项目。主项目包含所有模块入口点(即具有CloudCodeFunction
主项目也是您部署到 Cloud Code 服务的项目。主项目中未引用的项目和依赖项不会部署到 Cloud Code 服务。
您需要让主项目的名称与部署到 Cloud Code 服务的存档文件的名称匹配。
示例项目
最简单的模块项目可以具有以下结构:以下示例定义的 Cloud Code 模块包含一个返回Sample.sln├─ Main.csproj └─ Dependencies └─ HelloWorld.cs
Hello World!Hellousing 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 提供了两个可以包含在模块中的包:- :此包中包含 Cloud Code 的核心功能,包括
com.Unity.Services.CloudCode.Core属性。所有 Cloud Code 模块都需要此包。CloudCodeFunction - :这是一个可选包,其中包含可用于与 Cloud Code 服务交互的 Unity Gaming Services(Unity 游戏服务)API。您可以使用此包调用其他 Cloud Code SDK,例如 Cloud Save。
com.Unity.Services.CloudCode.Apis
代码结构
为了避免代码结构问题,请遵循以下准则。CloudCodeFunction 属性
要将方法标记为 Cloud Code 函数并使其成为可行的 Cloud Code 模块终端,请使用CloudCodeFunction方法签名
使用以下结构作为模块函数的方法签名。[CloudCodeFunction("FunctionName")]public async Task<ReturnType> FunctionName(IGameApiClient gameApiClient, IExecutionContext ctx, string parameter1, string parameter2){ ...}
对模块使用单例模式
您可以使用单例模式来与 Cloud Code C# 包和外部服务调用进行集成。单例模式可提高 Cloud Code 中的性能和可扩展性,并确保您不会为同一对象创建多个实例。 您可以使用依赖项注入将IGameApiClientIPushClient传递参数
您可以将参数传递给模块函数。原始类型和复杂类型
包括任何原始类型(例如intstringboolfloatList<T>Dictionary<T>Dictionary<string, T>stringList<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 } } }}
PlayerInventorypublic 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 模块可以使用IGameApiClientIAdminApiClientIPushClientIExecutionContextICloudCodeSetupIGameApiClient 接口
IGameApiClientIGameApiClient- 定义一个实现 接口的类,并添加
ICloudCodeSetup为单例:GameApiClientpublic class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(GameApiClient.Create()); }} - 将 接口作为函数参数传入模块函数。
IGameApiClient
IAdminApiClient 接口
IAdminApiClientIAdminApiClient- 定义一个实现 接口的类,并添加
ICloudCodeSetup为单例:AdminApiClientpublic class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(AdminApiClient.Create()); }} - 将 接口作为函数参数传入模块函数。
IAdminApiClient
IPushClient 接口
IPushClientIPushClient- 定义一个实现 接口的类,并添加
ICloudCodeSetup为单例:PushClientpublic class ModuleConfig : ICloudCodeSetup{ public void Setup(ICloudCodeConfig config) { config.Dependencies.AddSingleton(PushClient.Create()); }} - 将 对象作为函数参数传入模块函数。
PushClient
IExecution 上下文
要在模块中调用其他 UGS 服务,可以使用IExecutionContext- :调用方通过身份验证的 Project ID。
ProjectId - :执行脚本的玩家 ID。注意:当服务调用脚本时,玩家 ID 不可用。
PlayerId - :模块的环境 ID。
EnvironmentId - :模块的环境名称。
EnvironmentName - :玩家用于向 Cloud Code 进行身份验证的 JSON Web Token (JWT) 凭据。
AccessToken - :服务帐户的用户 ID(注意:当服务调用脚本时,用户 ID 不可用)。
UserId - :签发者或服务帐户令牌(注意:当玩家调用脚本时,签发者不可用)。例如,如果 Multiplay 发出调用,则签发者为
Issuer。multiplay - :Cloud Code 服务帐户 JWT 凭据。
ServiceToken - :玩家的 Analytics 用户 ID(注意:当服务调用脚本时,Analytics 用户 ID 不可用)。
AnalyticsUserId - :玩家的 Unity 设备安装 ID(注意:当服务调用脚本时,Unity 安装 ID 不可用)。
UnityInstallationId
CloudCodeFunctionusing 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()); } }}
令牌身份验证
AccessTokenServiceTokenIExecutionContext令牌类型 | 来源 | 数据访问 | 用途 |
|---|---|---|---|
| 由 Authentication 服务生成。 | 仅限经过身份验证的玩家。 | |
| 由 Cloud Code 生成。 | 跨玩家数据访问。 | |
AccessTokenAccessToken要访问所有玩家的数据,您可以使用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()); } }}
ServiceTokenplayerId如需了解这两个令牌之间差异的深入说明,请参阅服务和访问令牌文档。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{ "name": "string", "language": "CS", "tags": { "property1": "string", "property2": "string" }, "signedDownloadURL": "string", "dateCreated": "2022-04-05T09:12:13Z", "dateModified": "2022-04-05T09:12:13Z"}