批处理请求
在调用 UGS 服务时可以批量处理您的请求,以便它们能够并行执行。
您可以使用 Task.WhenAll
方法并行调用 UGS 服务。此方法返回一个当参数列表中的所有任务都完成时才完成的 Task
。
**注意:**所有失败的请求都会记录到日志中。您可以在 Unity Dashboard 的 Cloud Code 日志中找到它们。要了解有关日志记录的更多信息,请参阅日志记录。
**注意:**如果某些请求失败,不会回滚成功的请求。
调用和返回结果
您可以使用下面的示例来调用服务并返回请求结果。
下面的示例调用了 Leaderboards 和 Economy 服务。如果您的项目中没有配置排行榜或货币,这些调用将会失败。因此,您可以测试 Cloud Code 模块中的错误处理。
该方法返回一个字典,其中包含请求的结果,以发出请求的方法的签名作为键,以请求的结果作为值。如果请求失败,会说明哪个请求失败了。这些失败结果也会记录到日志中。
C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudSave.Model;
using Unity.Services.Economy.Model;
namespace BatchingRequests;
public class BatchingRequests
{
private const string ExperienceKey = "experience";
private const string ProgressXpKey = "progressXP";
private const string FailedMessage = "Failed. Check logs.";
private readonly ILogger<BatchingRequests> _logger;
public BatchingRequests(ILogger<BatchingRequests> logger)
{
_logger = logger;
}
[CloudCodeFunction("HandleMultipleRequests")]
public async Task<Dictionary<string, object>> HandleMultipleRequests(IGameApiClient gameApiClient, IExecutionContext ctx)
{
async Task<object> GetCloudSaveData()
{
var res = await gameApiClient.CloudSaveData.GetItemsAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, new List<string> { ExperienceKey, ProgressXpKey });
return res.Data;
}
async Task<object> SetCloudSaveData()
{
var res = await gameApiClient.CloudSaveData.SetItemAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, new SetItemBody(ExperienceKey, 1));
return res.Data;
}
async Task<object> GetLeaderboardScore()
{
const string leaderboardId = "leaderboard";
var res = await gameApiClient.Leaderboards.GetLeaderboardPlayerScoreAsync(ctx, ctx.AccessToken, Guid.Parse(ctx.ProjectId), leaderboardId, ctx.PlayerId);
return res.Data;
}
async Task<object> GetEconomyConfig()
{
var res = await gameApiClient.EconomyConfiguration.GetPlayerConfigurationAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId);
return res.Data;
}
async Task<object> SetEconomyConfig()
{
const string currencyId = "NOT_REAL";
var res = await gameApiClient.EconomyCurrencies.IncrementPlayerCurrencyBalanceAsync(ctx, ctx.AccessToken, ctx.ProjectId, ctx.PlayerId, currencyId, new CurrencyModifyBalanceRequest(currencyId, 1));
return res.Data;
}
// Create a list of UGS tasks that return data
var tasksWithObjects = new List<Func<Task<object>>>() { GetCloudSaveData, GetLeaderboardScore, SetCloudSaveData, GetEconomyConfig, SetEconomyConfig };
var results = await AwaitBatch(tasksFns);
foreach (var item in results)
{
if (item is Exception)
{
_logger.LogError($"AwaitBatch failed status {item}");
}
else if (item is CurrencyBalanceResponse)
{
_logger.LogInformation($"AwaitBatch CurrencyBalanceResponse status {item.GetType()} = {item}");
}
else
{
_logger.LogInformation($"AwaitBatch success status {item.GetType()} = {item}");
}
}
// Alternatively to looping through all results, you may access function results directly
// The order of function results follows the order of the function input e.g.
// - GetCloudSaveData corresponds to results[0]: GetItemsResponse || Exception
// - GetLeaderboardScore corresponds to results[1]: LeaderboardEntryWithUpdatedTime || Exception
// ... etc
// This can also help ascertain where the Exception originated from
}
private async Task<object[]> AwaitBatch(List<Func<Task<object>>> tasks)
{
var tasksToRun = tasks.Select(Run);
var results = await Task.WhenAll(tasksToRun);
return results;
}
async Task<object> Run(Func<Task<object>> fn)
{
try
{
return await fn();
}
catch (Exception e)
{
return e;
}
}
}
public class ModuleConfig : ICloudCodeSetup
{
public void Setup(ICloudCodeConfig config)
{
config.Dependencies.AddSingleton(GameApiClient.Create());
}
}