You've already forked CC-Framework.BriskGameServer
15 KiB
15 KiB
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.InitializeAsyncBrisk根节点只负责全局状态、生命周期、事件和模块入口- 具体业务能力放在
Brisk.Auth、Brisk.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
GameKeybootstrap、config/current、auth/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;
}
说明:
ClientVersionbootstrap、config/current、login推荐传入- 后续可用于最低版本校验
DeviceIdbootstrap、config/current、login推荐传入- 便于服务端做环境识别与问题排查
EnableLog- 是否输出 SDK 调试日志
ValidateSessionOnInitialize- 初始化时如果本地有 token,是否自动调用
player/me校验有效性
- 初始化时如果本地有 token,是否自动调用
TokenStore- 登录态持久化存储接口
ErrorPresenter- 默认错误弹窗展示接口
ExitHandler- 遇到需要默认退出的阻断错误时,由宿主游戏接管实际退出逻辑
3.3 首版初始化建议
为了保持易用,首版建议如下:
- 调用方最少只需要传
BaseUrl、GameKey ClientVersion强烈建议传DeviceId建议有就传- 如果不传
TokenStore,SDK 使用默认本地实现 - 如果不传
ErrorPresenter,SDK 使用默认实现或仅触发事件 - 如果不传
ExitHandler,SDK 不直接强制退出,而是交给项目层自行处理
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_providerlogin_user_id
- 内部身份
player_idproject_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缺少 token10002token 无效10023缺少会话
默认行为:
- 清空本地 token
- 更新
Brisk.IsLoggedIn = false - 触发
Brisk.OnAuthExpired - 默认弹出登录态失效提示 UI
- 抛出
BriskAuthExpiredException
8.2 GlobalBlocking
10003项目维护中10004玩家已封禁10005访问校验失败10025项目维护中10026玩家已封禁
默认行为:
- 触发
Brisk.OnBlockingError - 使用 SDK 默认 UI 弹出阻断提示
- 如果开发者自定义了
ErrorPresenter,则由开发者 UI 接管展示 - 是否退出程序由
ExitHandler决定,不在网络底层写死
进一步建议:
10003、10025->Maintenance10004、10026->AccountBanned10005->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~/
对外尽量只暴露:
BriskBriskOptions- 各模块入口类
- 结果数据结构
- 必要异常类
14. 当前阶段结论
当前已明确的设计结论:
- 对外统一使用
Brisk.xxx - 初始化完成后各模块直接可用
- 初始化参数不再包含
channel / platform - 登录模型统一为
login_provider / login_user_id / code BriskOptions最少只要求BaseUrl、GameKeyClientVersion为可选但强烈建议传入- 网络底层负责识别错误,不直接写死 UI 和退出行为
- SDK 核心负责全局错误策略、全局事件和默认表现
- “弱网模式”作为后续扩展项预留,不阻塞首版实现
15. 下一步建议
建议按以下顺序继续推进:
- 先搭 SDK 骨架与目录结构
- 先实现
Brisk.InitializeAsync - 先打通
Auth、Player、Leaderboard - 再接入
Archive的二进制上传下载 - 最后补充全局错误展示器与默认本地存储实现