You've already forked CC-Framework.BriskGameServer
330 lines
9.9 KiB
C#
330 lines
9.9 KiB
C#
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);
|
||
}
|
||
}
|