Files
CC-Framework.BriskGameServer/Assets/BriskSdk/Runtime/Core/Brisk.cs

330 lines
9.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// Brisk SDK 的静态总入口。
/// 初始化完成后,开发者可以通过 <c>Brisk.Auth</c>、<c>Brisk.Leaderboard</c> 等模块直接访问能力。
/// </summary>
public static class Brisk
{
private static BriskContext s_context;
static Brisk()
{
Auth = new BriskAuthModule();
Player = new BriskPlayerModule();
Config = new BriskConfigModule();
Announcements = new BriskAnnouncementsModule();
Leaderboard = new BriskLeaderboardModule();
Archive = new BriskArchiveModule();
Space = new BriskSpaceModule();
}
/// <summary>
/// SDK 初始化完成后触发。
/// </summary>
public static event Action OnInitialized;
/// <summary>
/// 登录成功并保存会话后触发。
/// </summary>
public static event Action OnLoggedIn;
/// <summary>
/// 主动登出并清理本地会话后触发。
/// </summary>
public static event Action OnLoggedOut;
/// <summary>
/// 发生维护、封禁、强更等严重阻断错误时触发。
/// </summary>
public static event Action<BriskBlockingException> OnBlockingError;
/// <summary>
/// 登录态失效并清空本地会话时触发。
/// </summary>
public static event Action<BriskAuthExpiredException> OnAuthExpired;
/// <summary>
/// 认证模块入口。
/// </summary>
public static BriskAuthModule Auth { get; }
/// <summary>
/// 玩家模块入口。
/// </summary>
public static BriskPlayerModule Player { get; }
/// <summary>
/// 动态配置模块入口。
/// </summary>
public static BriskConfigModule Config { get; }
/// <summary>
/// 公告模块入口。
/// </summary>
public static BriskAnnouncementsModule Announcements { get; }
/// <summary>
/// 排行榜模块入口。
/// </summary>
public static BriskLeaderboardModule Leaderboard { get; }
/// <summary>
/// 云存档模块入口。
/// </summary>
public static BriskArchiveModule Archive { get; }
/// <summary>
/// 玩家空间模块入口。
/// </summary>
public static BriskSpaceModule Space { get; }
/// <summary>
/// 当前 SDK 是否已完成初始化。
/// </summary>
public static bool IsInitialized => s_context != null;
/// <summary>
/// 当前是否存在可用登录态。
/// </summary>
public static bool IsLoggedIn => s_context != null && s_context.Session.HasAccessToken;
/// <summary>
/// 当前访问令牌。
/// </summary>
public static string AccessToken => s_context != null ? s_context.Session.AccessToken : null;
/// <summary>
/// 当前玩家 ID。
/// </summary>
public static string PlayerId => s_context != null ? s_context.Session.PlayerId : null;
/// <summary>
/// 当前登录身份摘要。
/// </summary>
public static BriskIdentity Identity => s_context != null ? s_context.Session.Identity : null;
/// <summary>
/// 当前初始化选项。
/// </summary>
public static BriskOptions Options => s_context != null ? s_context.Options : null;
/// <summary>
/// 初始化阶段获取到的 bootstrap 结果。
/// </summary>
public static BriskBootstrapResult Bootstrap => s_context != null ? s_context.Bootstrap : null;
/// <summary>
/// 初始化 SDK并执行 bootstrap 与本地会话恢复。
/// </summary>
/// <param name="options">初始化选项。</param>
public static async Task InitializeAsync(BriskOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.Validate();
var context = new BriskContext(options);
s_context = context;
try
{
await BootstrapAsync(context);
await RestoreSessionAsync(context);
OnInitialized?.Invoke();
}
catch
{
if (context.Bootstrap == null)
{
s_context = null;
}
throw;
}
}
/// <summary>
/// 关闭 SDK 并清空当前上下文。
/// </summary>
public static void Shutdown()
{
s_context = null;
}
/// <summary>
/// 设置严重错误的展示器。
/// </summary>
/// <param name="presenter">自定义错误展示器;传入 null 时恢复默认展示器。</param>
public static void SetErrorPresenter(IBriskErrorPresenter presenter)
{
GetRequiredContext().ErrorPresenter = presenter ?? BriskDefaultErrorPresenter.Instance;
}
/// <summary>
/// 设置阻断错误确认后的退出回调。
/// </summary>
/// <param name="exitHandler">项目方自定义退出逻辑。</param>
public static void SetExitHandler(Action exitHandler)
{
GetRequiredContext().ExitHandler = exitHandler;
}
internal static BriskContext GetRequiredContext()
{
if (s_context == null)
{
throw new BriskNotInitializedException();
}
return s_context;
}
internal static void NotifyLoggedIn()
{
OnLoggedIn?.Invoke();
}
internal static void NotifyLoggedOut()
{
OnLoggedOut?.Invoke();
}
internal static void NotifyBlockingError(BriskBlockingException exception)
{
s_context?.ErrorPresenter?.ShowBlockingError(exception);
OnBlockingError?.Invoke(exception);
}
internal static void NotifyAuthExpired(BriskAuthExpiredException exception)
{
NotifyAuthExpired(exception, true);
}
internal static void NotifyAuthExpired(BriskAuthExpiredException exception, bool showDefaultPresenter)
{
if (showDefaultPresenter)
{
s_context?.ErrorPresenter?.ShowAuthExpired(exception);
}
OnAuthExpired?.Invoke(exception);
}
private static async Task BootstrapAsync(BriskContext context)
{
var query = new Dictionary<string, string>
{
{ "game_key", context.Options.GameKey },
{ "client_version", context.Options.ClientVersion },
{ "device_id", context.Options.DeviceId }
};
var bootstrapData = await context.HttpClient.GetDataAsync("/client/bootstrap", query, false);
var bootstrap = BriskModelMapper.ToBootstrapResult(bootstrapData);
context.Bootstrap = bootstrap;
if (bootstrap.MaintenanceMode)
{
var message = string.IsNullOrWhiteSpace(bootstrap.MaintenanceMessage)
? "Server is under maintenance."
: bootstrap.MaintenanceMessage;
var exception = new BriskMaintenanceException(message);
NotifyBlockingError(exception);
throw exception;
}
if (BriskVersionComparer.IsLessThan(context.Options.ClientVersion, bootstrap.MinClientVersion))
{
var exception = new BriskClientUpdateRequiredException("Client version is lower than the minimum supported version.");
NotifyBlockingError(exception);
throw exception;
}
}
private static async Task RestoreSessionAsync(BriskContext context)
{
var storedSession = await context.TokenStore.LoadAsync();
if (storedSession == null || string.IsNullOrWhiteSpace(storedSession.AccessToken))
{
return;
}
if (storedSession.ExpiresAt.HasValue && storedSession.ExpiresAt.Value <= DateTimeOffset.UtcNow)
{
await HandleExpiredStartupSessionAsync(
context,
storedSession,
new BriskAuthExpiredException("Stored session expired before initialization."));
return;
}
context.Session.Update(
storedSession.AccessToken,
storedSession.ExpiresAt,
storedSession.PlayerId,
storedSession.ProjectAccountId,
storedSession.LoginProvider,
storedSession.LoginUserId);
if (!context.Options.ValidateSessionOnInitialize)
{
return;
}
try
{
var meData = await context.HttpClient.GetDataAsync("/player/me", null, true);
var me = BriskModelMapper.ToPlayerMe(meData);
context.Session.Update(
storedSession.AccessToken,
storedSession.ExpiresAt,
me.PlayerId,
me.ProjectAccountId,
me.LoginProvider,
me.LoginUserId);
}
catch (BriskAuthExpiredException exception)
{
await HandleExpiredStartupSessionAsync(context, storedSession, exception);
}
catch (BriskBlockingException exception)
{
NotifyBlockingError(exception);
throw;
}
}
private static async Task HandleExpiredStartupSessionAsync(BriskContext context, BriskStoredSession storedSession, BriskAuthExpiredException exception)
{
context.Session.Clear();
await context.TokenStore.ClearAsync();
NotifyAuthExpired(exception, false);
if (!context.Options.AutoReloginOnInitialize || !HasStoredLoginIdentity(storedSession))
{
return;
}
try
{
await Auth.LoginWithUserIdAsync(storedSession.LoginProvider, storedSession.LoginUserId);
}
catch (BriskBlockingException)
{
throw;
}
catch
{
// 初始化期静默重登失败时,交给业务层后续第三方登录流程重新调用 Brisk.Auth。
}
}
private static bool HasStoredLoginIdentity(BriskStoredSession storedSession)
{
return storedSession != null
&& !string.IsNullOrWhiteSpace(storedSession.LoginProvider)
&& !string.IsNullOrWhiteSpace(storedSession.LoginUserId);
}
}