Files
CC-Framework.BriskGameServer/Brisk Unity SDK 设计草案.md

15 KiB
Raw Blame History

Brisk Unity SDK 设计草案

本文档用于沉淀当前 Brisk Unity SDK 的首版设计方向,目标是:

  • 对开发者极简易用
  • 初始化一次后,各模块可直接调用
  • 保持内部结构清晰,便于后续扩展
  • 优先围绕当前服务端已实现接口设计,不预埋过多未来能力

参考资料:

  • 服务参考_临时/Brisk Unity SDK接入文档.md
  • 服务参考_临时/Brisk API接口与示例文档.md
  • 服务参考_临时/Brisk 错误码文档(内部实施版).md
  • 服务参考_临时/openapi.yaml
  • TapTap 官方 Unity 集成与排行榜文档

1. 设计目标

首版 SDK 对外遵循以下原则:

  • 对外统一使用 Brisk.xxx
  • 模块能力统一使用 Brisk.模块.xxx
  • 不暴露底层 HTTP、Envelope、Token 注入等细节
  • 初始化参数只保留项目级与设备级信息
  • 登录模型统一围绕 login_provider / login_user_id / code
  • 初始化完成后,模块可直接调用
  • 错误码统一在 SDK 内部识别和分发

示例目标用法:

await Brisk.InitializeAsync(new BriskOptions
{
    BaseUrl = "https://brisk.lightyears.ltd",
    GameKey = "demo-game",
    ClientVersion = Application.version,
    DeviceId = SystemInfo.deviceUniqueIdentifier
});

await Brisk.Auth.LoginWithUserIdAsync("tap", "tap_user_10001");

var me = await Brisk.Player.GetMeAsync();
var top = await Brisk.Leaderboard.GetTopAsync("season-score", 20);
await Brisk.Leaderboard.SubmitScoreAsync("season-score", 128);

2. 对外 API 结构

首版建议公开如下 API 树:

Brisk.InitializeAsync(BriskOptions options)
Brisk.Shutdown()

Brisk.IsInitialized
Brisk.IsLoggedIn
Brisk.AccessToken
Brisk.PlayerId
Brisk.Identity
Brisk.Options
Brisk.Bootstrap

Brisk.Auth.LoginWithUserIdAsync(string loginProvider, string loginUserId, BriskProfile profile = null)
Brisk.Auth.LoginWithCodeAsync(string loginProvider, string code, BriskProfile profile = null)
Brisk.Auth.LogoutAsync()

Brisk.Player.GetMeAsync()

Brisk.Config.GetCurrentAsync()
Brisk.Config.RefreshAsync()

Brisk.Announcements.GetListAsync()
Brisk.Announcements.MarkReadAsync(long id)

Brisk.Leaderboard.GetTopAsync(string rankKey, int limit = 20)
Brisk.Leaderboard.GetMeAsync(string rankKey)
Brisk.Leaderboard.GetAroundMeAsync(string rankKey, int range = 10)
Brisk.Leaderboard.SubmitScoreAsync(string rankKey, long score)
Brisk.Leaderboard.GetCurrentSeasonAsync(string rankKey)
Brisk.Leaderboard.GetSeasonHistoryAsync(string rankKey, int limit = 20)
Brisk.Leaderboard.GetSeasonHistoryDetailAsync(string rankKey, string seasonId, int limit = 20)

Brisk.Archive.GetSlotsAsync()
Brisk.Archive.GetMetaAsync(int slotNo)
Brisk.Archive.UploadAsync(int slotNo, byte[] bytes, int? baseVersion = null, string checksum = null)
Brisk.Archive.DownloadAsync(int slotNo)

Brisk.Space.GetByPlayerIdAsync(string playerId)
Brisk.Space.GetByLoginIdentityAsync(string loginProvider, string loginUserId)
Brisk.Space.GetStatsByPlayerIdAsync(string playerId)
Brisk.Space.GetStatsByLoginIdentityAsync(string loginProvider, string loginUserId)
Brisk.Space.LikeByPlayerIdAsync(string playerId)
Brisk.Space.UnlikeByPlayerIdAsync(string playerId)
Brisk.Space.LikeByLoginIdentityAsync(string loginProvider, string loginUserId)
Brisk.Space.UnlikeByLoginIdentityAsync(string loginProvider, string loginUserId)
Brisk.Space.UpdateMyAsync(object payload)
Brisk.Space.GetMyVisitsAsync()

说明:

  • bootstrap 不单独暴露成模块入口,直接并入 Brisk.InitializeAsync
  • Brisk 根节点只负责全局状态、生命周期、事件和模块入口
  • 具体业务能力放在 Brisk.AuthBrisk.Leaderboard 等模块下

3. BriskOptions 设计

BriskOptions 只保留当前服务端确实需要的参数,并区分必填与推荐字段。

3.1 必填字段

根据当前服务端文档,以下字段应为初始化必填:

public sealed class BriskOptions
{
    public string BaseUrl;
    public string GameKey;
    public string ClientVersion;
}

原因:

  • BaseUrl
    • SDK 所有接口的基础地址
    • 推荐直接传 API Base例如 https://host/api
  • GameKey
    • bootstrapconfig/currentauth/login/exchange 都需要
  • ClientVersion
    • 当前服务端接口中为可选,但强烈建议传入
    • 会影响最低版本校验与动态配置命中

3.2 推荐字段

这些字段不是所有场景必填,但推荐在初始化时一次传入,后续由 SDK 自动复用:

public sealed class BriskOptions
{
    public string BaseUrl;
    public string GameKey;
    public string ClientVersion;
    public string DeviceId;
    public bool EnableLog = false;
    public bool ValidateSessionOnInitialize = true;
    public IBriskTokenStore TokenStore;
    public IBriskErrorPresenter ErrorPresenter;
    public Action ExitHandler;
}

说明:

  • ClientVersion
    • bootstrapconfig/currentlogin 推荐传入
    • 后续可用于最低版本校验
  • DeviceId
    • bootstrapconfig/currentlogin 推荐传入
    • 便于服务端做环境识别与问题排查
  • EnableLog
    • 是否输出 SDK 调试日志
  • ValidateSessionOnInitialize
    • 初始化时如果本地有 token是否自动调用 player/me 校验有效性
  • TokenStore
    • 登录态持久化存储接口
  • ErrorPresenter
    • 默认错误弹窗展示接口
  • ExitHandler
    • 遇到需要默认退出的阻断错误时,由宿主游戏接管实际退出逻辑

3.3 首版初始化建议

为了保持易用,首版建议如下:

  • 调用方最少只需要传 BaseUrlGameKey
  • ClientVersion 强烈建议传
  • DeviceId 建议有就传
  • 如果不传 TokenStoreSDK 使用默认本地实现
  • 如果不传 ErrorPresenterSDK 使用默认实现或仅触发事件
  • 如果不传 ExitHandlerSDK 不直接强制退出,而是交给项目层自行处理

4. 初始化生命周期

Brisk.InitializeAsync 的建议流程如下:

4.1 标准流程

1. 校验 BriskOptions
2. 规范化 BaseUrl
3. 初始化 Core 运行时对象
4. 调用 GET /client/bootstrap
5. 缓存 bootstrap 结果
6. 根据 bootstrap 检查维护状态与最低版本
7. 初始化各模块入口
8. 从 TokenStore 恢复本地 token
9. 如果配置了 ValidateSessionOnInitialize 且存在 token则调用 GET /player/me 验证会话
10. 进入可用状态

4.2 具体建议

  • BaseUrl 允许调用方传:
    • https://host/api
    • https://host
  • SDK 内部统一规范化为最终 API Base
  • 初始化阶段不依赖 login_provider / login_user_id
  • bootstrap 结果建议缓存到:
    • Brisk.Bootstrap
  • 如果初始化阶段检测到以下情况,直接触发全局阻断错误:
    • 项目维护中
    • 最低版本不满足
    • 本地恢复到的账号已失效或已封禁

4.3 不建议的行为

首版不建议做以下事情:

  • 不自动静默重新登录
    • 当前服务端没有 refresh token 机制
  • 不在初始化中自动执行第三方平台登录
    • 第三方登录由游戏侧完成Brisk 只负责兑换 Brisk 会话
  • 不把所有模块的预加载都塞进初始化
    • 公告、排行榜、空间等业务数据由调用方按需拉取

5. 登录态设计

首版登录核心模型:

login_provider + login_user_id -> POST /auth/login/exchange -> Brisk access_token
或
login_provider + code -> POST /auth/login/exchange -> Brisk access_token

身份建议分为两层:

  • 外部身份
    • login_provider
    • login_user_id
  • 内部身份
    • player_id
    • project_account_id

推荐登录行为:

1. 游戏先完成第三方平台登录
2. 游戏拿到 `login_provider`
3. 再拿到 `login_user_id` 或 `code`
4. 调用 `Brisk.Auth.LoginWithUserIdAsync(...)` 或 `Brisk.Auth.LoginWithCodeAsync(...)`
5. SDK 保存 token、过期时间、player_id、project_account_id
6. 后续玩家态接口自动附带 Bearer Token

6. 错误处理总设计

首版错误处理原则改为:

  • SDK 只统一接管严重阻断错误
  • 普通接口错误不走全局监听
  • 普通接口错误由调用方在调用处自行处理
  • SDK 默认提供一套阻断错误弹窗 UI
  • 开发者可以替换默认 UI 或自行接管严重错误

内部处理链路建议如下:

BriskHttpClient
-> BriskResponseParser
-> BriskErrorClassifier
-> 严重错误进入 SDK 全局处理
-> 普通错误直接返回给当前调用方

职责划分:

  • BriskHttpClient
    • 发请求、收响应、拿到 HTTP 状态码
  • BriskResponseParser
    • 解析 code/message/data
  • BriskErrorClassifier
    • 把错误码映射成 SDK 语义错误
  • 严重错误全局处理
    • 只处理维护、封号、登录态失效等阻断错误
  • 普通错误调用点处理
    • 由调用方 try/catch
    • 或由调用方传入当前接口自己的回调处理

7. 错误分级

首版建议将错误分为以下几类:

public enum BriskErrorKind
{
    Retryable,
    Business,
    AuthExpired,
    GlobalBlocking,
    Fatal
}

说明:

  • Retryable
    • 超时、网络抖动、429、临时服务不可用
  • Business
    • 普通业务失败,例如存档冲突、参数不合法
    • 不进入 SDK 全局错误处理
  • AuthExpired
    • 登录态失效,需要清理会话
    • 属于 SDK 全局接管范围
  • GlobalBlocking
    • 封号、维护中、强制更新等,需要阻断当前玩家继续操作
    • 属于 SDK 全局接管范围
  • Fatal
    • SDK 未初始化、关键配置错误、内部不可恢复异常
    • 只用于真正不可恢复的框架级错误

8. 错误码映射建议

根据当前错误码文档,首版建议如下处理:

8.1 AuthExpired

  • 10001 缺少 token
  • 10002 token 无效
  • 10023 缺少会话

默认行为:

  • 清空本地 token
  • 更新 Brisk.IsLoggedIn = false
  • 触发 Brisk.OnAuthExpired
  • 默认弹出登录态失效提示 UI
  • 抛出 BriskAuthExpiredException

8.2 GlobalBlocking

  • 10003 项目维护中
  • 10004 玩家已封禁
  • 10005 访问校验失败
  • 10025 项目维护中
  • 10026 玩家已封禁

默认行为:

  • 触发 Brisk.OnBlockingError
  • 使用 SDK 默认 UI 弹出阻断提示
  • 如果开发者自定义了 ErrorPresenter,则由开发者 UI 接管展示
  • 是否退出程序由 ExitHandler 决定,不在网络底层写死

进一步建议:

  • 1000310025 -> Maintenance
  • 1000410026 -> AccountBanned
  • 10005 -> AccessDenied

8.3 Retryable

  • 80001 请求过于频繁
  • 80002 限流服务不可用

默认行为:

  • 短退避重试
  • 超过最大次数后抛出异常
  • 不触发全局 UI

8.4 Business

  • 60016 存档版本冲突
  • 60017 校验值不匹配
  • 60018 存档超出大小限制
  • 60021 存档不存在
  • 70011 玩家不存在
  • 70017 玩家不存在
  • 70020 玩家不存在

默认行为:

  • 不做系统级强拦截
  • 不走全局错误监听
  • 抛出对应业务异常,由调用方决定如何提示
  • 如果未来个别接口希望提供便利用法,可在该接口额外支持传入当前调用专属回调

9. 异常类命名建议

首版建议使用以下异常层次:

BriskException
BriskNetworkException
BriskHttpException
BriskApiException
BriskBusinessException
BriskAuthExpiredException
BriskBlockingException
BriskMaintenanceException
BriskAccountBannedException
BriskRateLimitException
BriskArchiveConflictException
BriskArchiveChecksumException
BriskArchiveTooLargeException
BriskNotInitializedException

说明:

  • BriskException
    • SDK 所有异常的统一基类
  • BriskApiException
    • 已成功收到接口响应,但 code != 0
  • BriskBusinessException
    • 普通业务错误
  • BriskAuthExpiredException
    • 登录态失效
  • BriskBlockingException
    • 全局阻断错误的公共基类
  • BriskMaintenanceException
    • 维护中
  • BriskAccountBannedException
    • 账号封禁
  • BriskNotInitializedException
    • 未初始化即调用模块接口

10. 全局事件命名建议

首版建议 Brisk 根节点提供以下全局事件:

Brisk.OnInitialized
Brisk.OnLoggedIn
Brisk.OnLoggedOut
Brisk.OnAuthExpired
Brisk.OnBlockingError

建议语义:

  • OnInitialized
    • SDK 初始化完成
  • OnLoggedIn
    • 登录成功
  • OnLoggedOut
    • 主动登出完成
  • OnAuthExpired
    • 登录态失效
  • OnBlockingError
    • 维护、封号、访问阻断等全局错误

可选接管接口:

Brisk.SetErrorPresenter(IBriskErrorPresenter presenter)
Brisk.SetExitHandler(Action exitHandler)

11. 关于“底层直接弹窗并退出程序”的建议

这个方向本身是合理的,但不建议把“弹窗”和“退出程序”都硬编码在网络底层。

更推荐的做法是:

网络底层识别错误
-> 严重错误交给 SDK 核心处理
-> 默认错误展示器弹窗
-> 是否退出由宿主游戏或 ExitHandler 决定

这样做的好处:

  • 设计更常规
  • 便于项目方接管 UI 风格
  • 便于区分“该回登录页”还是“该退出程序”
  • 避免网络层和游戏表现层耦合过深
  • 避免普通接口错误被 SDK 过度吞掉或误处理

具体建议:

  • 登录态失效
    • 默认不退出程序
    • 默认弹窗提示后清会话并回登录流程
  • 账号封禁
    • 默认弹窗提示后由项目方决定回登录页或退出
  • 维护中
    • 默认阻断流程并提示
  • Fatal
    • 可以作为最接近“提示后退出”的类型

12. 可选扩展:标准模式与弱网模式

这项需求合理,但优先级建议放在首版正常逻辑之后。

后续可以考虑增加:

public enum BriskNetworkMode
{
    Standard,
    WeakNetwork
}

两者差异可以主要体现在:

  • 重试次数
  • 超时时间
  • 429 退避策略
  • 是否对部分只读接口自动重试

建议首版先只做:

  • 一个默认标准策略
  • 策略入口预留在 BriskOptions

例如:

public sealed class BriskOptions
{
    public BriskNetworkMode NetworkMode = BriskNetworkMode.Standard;
}

但首版实现只落 Standard 即可。

13. 首版内部结构建议

Assets/
  BriskSdk/
    Runtime/
      Core/
        Brisk.cs
        BriskOptions.cs
        BriskContext.cs
        BriskSession.cs
        BriskHttpClient.cs
        BriskModuleExecutor.cs
        BriskErrorClassifier.cs
      Auth/
      Player/
      Config/
      Announcement/
      Leaderboard/
      Archive/
      Space/
      Models/
      Storage/
      Exceptions/
    Samples/
      QuickStart/
  Scenes/
    BriskQuickStartScene.unity

PackageSource/
  com.foldcc.cc-framework.BriskGameServer/
    package.json
    README.md
    CHANGELOG.md
    Documentation~/

对外尽量只暴露:

  • Brisk
  • BriskOptions
  • 各模块入口类
  • 结果数据结构
  • 必要异常类

14. 当前阶段结论

当前已明确的设计结论:

  • 对外统一使用 Brisk.xxx
  • 初始化完成后各模块直接可用
  • 初始化参数不再包含 channel / platform
  • 登录模型统一为 login_provider / login_user_id / code
  • BriskOptions 最少只要求 BaseUrlGameKey
  • ClientVersion 为可选但强烈建议传入
  • 网络底层负责识别错误,不直接写死 UI 和退出行为
  • SDK 核心负责全局错误策略、全局事件和默认表现
  • “弱网模式”作为后续扩展项预留,不阻塞首版实现

15. 下一步建议

建议按以下顺序继续推进:

  1. 先搭 SDK 骨架与目录结构
  2. 先实现 Brisk.InitializeAsync
  3. 先打通 AuthPlayerLeaderboard
  4. 再接入 Archive 的二进制上传下载
  5. 最后补充全局错误展示器与默认本地存储实现