feat:update upm
commit
27ee9f2df0
|
@ -0,0 +1,260 @@
|
||||||
|
# ChangeLog
|
||||||
|
## 3.11.1
|
||||||
|
- TapTap.Login v3.11.1
|
||||||
|
- TapTap.Common v3.11.1
|
||||||
|
- LeanCloud.Realtime v0.10.11
|
||||||
|
- LeanCloud.Storage v0.10.11
|
||||||
|
|
||||||
|
## 3.11.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.11.0
|
||||||
|
- TapTap.Common v3.11.0
|
||||||
|
- LeanCloud.Realtime v0.10.11
|
||||||
|
- LeanCloud.Storage v0.10.11
|
||||||
|
|
||||||
|
## 3.10.0
|
||||||
|
|
||||||
|
### New Feature
|
||||||
|
- 支持排行榜
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.10.0
|
||||||
|
- TapTap.Common v3.10.0
|
||||||
|
- LeanCloud.Realtime v0.10.11
|
||||||
|
- LeanCloud.Storage v0.10.11
|
||||||
|
|
||||||
|
## 3.9.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.9.0
|
||||||
|
- TapTap.Common v3.9.0
|
||||||
|
- LeanCloud.Realtime v0.10.11
|
||||||
|
- LeanCloud.Storage v0.10.11
|
||||||
|
|
||||||
|
## 3.8.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.8.0
|
||||||
|
- TapTap.Common v3.8.0
|
||||||
|
- LeanCloud.Realtime v0.10.10
|
||||||
|
- LeanCloud.Storage v0.10.10
|
||||||
|
|
||||||
|
## 3.7.1
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.7.1
|
||||||
|
- TapTap.Common v3.7.1
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.7.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.7.0
|
||||||
|
- TapTap.Common v3.7.0
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
|
||||||
|
## 3.6.3
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.6.3
|
||||||
|
- TapTap.Common v3.6.3
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.6.1
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.6.1
|
||||||
|
- TapTap.Common v3.6.1
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.6.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.6.0
|
||||||
|
- TapTap.Common v3.6.0
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.5.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.5.0
|
||||||
|
- TapTap.Common v3.5.0
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.4.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.4.0
|
||||||
|
- TapTap.Common v3.4.0
|
||||||
|
- LeanCloud.Realtime v0.10.0
|
||||||
|
- LeanCloud.Storage v0.10.0
|
||||||
|
|
||||||
|
## 3.3.0
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.3.0
|
||||||
|
- TapTap.Common v3.3.0
|
||||||
|
- LeanCloud.Realtime v0.9.11
|
||||||
|
- LeanCloud.Storage v0.9.11
|
||||||
|
|
||||||
|
## 3.2.0
|
||||||
|
|
||||||
|
### New Feature
|
||||||
|
|
||||||
|
- 支持云存档
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.2.0
|
||||||
|
- TapTap.Common v3.2.0
|
||||||
|
- LeanCloud.Storage v0.9.5
|
||||||
|
- LeanCloud.Realtime v0.9.5
|
||||||
|
|
||||||
|
## 3.1.0
|
||||||
|
|
||||||
|
### New Feature
|
||||||
|
|
||||||
|
- `TDSUser` 新增好友系统操作
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Login v3.1.0
|
||||||
|
- TapTap.Common v3.1.0
|
||||||
|
- LeanCloud.Storage v0.9.2
|
||||||
|
- LeanCloud.Realtime v0.9.2
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
TapSDK 3.0 开始,我们在单纯的 TapTap 登录之外,还提供了一个内建账户系统供游戏使用:开发者可以直接用 TapTap OAuth
|
||||||
|
授权的结果生成一个游戏内的账号(TDSUser),然后用该账号保存更多玩家数据。同时,我们也支持将更多第三方认证登录的结果绑定到这一账号上来(以及后续的解绑操作)。
|
||||||
|
|
||||||
|
### New Feature
|
||||||
|
|
||||||
|
- 新增 `TDSUser` 用于内建账户系统操作
|
||||||
|
|
||||||
|
### BreakingChange
|
||||||
|
|
||||||
|
- `TapBootstrap` 接口仅保留 `TapBootstrap.Init(tapConfig)` 接口
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- LeanCloud.Storage v0.8.2
|
||||||
|
- TapTap.Login v3.0.0
|
||||||
|
- TapTap.Common v3.0.0
|
||||||
|
|
||||||
|
## 2.1.7
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Common v2.1.7
|
||||||
|
|
||||||
|
## 2.1.6
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Common v2.1.6
|
||||||
|
|
||||||
|
## 2.1.5
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Common v2.1.5
|
||||||
|
|
||||||
|
## 2.1.4
|
||||||
|
|
||||||
|
### Optimization and fixed bugs
|
||||||
|
|
||||||
|
- 优化多语言相关
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- TapTap.Common v2.1.4
|
||||||
|
|
||||||
|
## 2.1.3
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* 新增繁中、日文、韩文、泰文和印尼语多语言配置
|
||||||
|
|
||||||
|
## 2.1.2
|
||||||
|
|
||||||
|
### BreakingChange
|
||||||
|
|
||||||
|
* 废弃 OpenUserCenter 接口
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
* TapTap.Common v2.1.2
|
||||||
|
|
||||||
|
## 2.1.1
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* 新增篝火测试资格校验
|
||||||
|
```
|
||||||
|
TapBootstrap.GetTestQualification((bool, error)=>{ }):
|
||||||
|
```
|
||||||
|
* 通过 TapConfig 进行初始化配置
|
||||||
|
* 新增 TapDBConfig 用于 TapDB 初始化配置
|
||||||
|
* 新增 ClientSecret 用于 TapSDK 初始化
|
||||||
|
```c#
|
||||||
|
//建议使用以下 TapConfig 构造方法进行初始化
|
||||||
|
var config = new TapConfig.Builder()
|
||||||
|
.ClientID("client_id")
|
||||||
|
.ClientSecret("client_secret")
|
||||||
|
.RegionType(RegionType.CN)
|
||||||
|
.TapDBConfig(true, "gameChannel", "gameVersion", true)
|
||||||
|
.ConfigBuilder();
|
||||||
|
TapBootstrap.Init(config);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
|
||||||
|
* LoginType 删除 Apple、Guest 登陆方式
|
||||||
|
* TDS-Info.plist 删除 Apple_SignIn_Enable 配置
|
||||||
|
* 废弃 Bind 接口
|
||||||
|
* TapConfig 构造方法参数修改
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
* TapTap.Common v2.1.1
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* 支持性改动用于 TapTap.Friends
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
* TapTap.Common v2.1.0
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* TapTap Bootstrap
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
* TapTap.Common v2.0.0
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7388165be5344a9682382768c5fce466
|
||||||
|
timeCreated: 1616755935
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bd03b50332353477c899419b5b455fb0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,257 @@
|
||||||
|
# 使用 TapTap.Bootstrap
|
||||||
|
|
||||||
|
## 使用前提
|
||||||
|
|
||||||
|
使用 TapTap.Bootstrap 前提是必须依赖以下库:
|
||||||
|
* [TapTap.Common](https://github.com/TapTap/TapCommon-Unity.git)
|
||||||
|
* [TapTap.Login](https://github.com/TapTap/TapLogin-Unity.git)
|
||||||
|
* [LeanCloud.Storage](https://github.com/leancloud/csharp-sdk)
|
||||||
|
* [LeanCloud.RealTime](https://github.com/leancloud/csharp-sdk)
|
||||||
|
|
||||||
|
## 命名空间
|
||||||
|
|
||||||
|
```c#
|
||||||
|
using TapTap.Bootstrap;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 接口描述
|
||||||
|
|
||||||
|
## 1.初始化
|
||||||
|
|
||||||
|
TapBootstrap 会根据 TapConfig 中的 TapDBConfig 配置来进行 TapDB 的自动初始化。
|
||||||
|
|
||||||
|
### 开启 TapDB
|
||||||
|
```c#
|
||||||
|
var config = new TapConfig.Builder()
|
||||||
|
.ClientID("client_id")
|
||||||
|
.ClientToken("client_token")
|
||||||
|
.ServerURL("https://ikggdre2.lc-cn-n1-shared.com")
|
||||||
|
.RegionType(RegionType.CN)
|
||||||
|
.TapDBConfig(true,"channel","gameVersion",true)
|
||||||
|
.Builder();
|
||||||
|
```
|
||||||
|
### 关闭 TapDB
|
||||||
|
```c#
|
||||||
|
var config = new TapConfig.Builder()
|
||||||
|
.ClientID("client_id")
|
||||||
|
.ClientToken("client_token")
|
||||||
|
.ServerURL("https://ikggdre2.lc-cn-n1-shared.com")
|
||||||
|
.RegionType(RegionType.CN)
|
||||||
|
//# .TapDBConfig(false,null,null,false)
|
||||||
|
.EnableTapDB(false)
|
||||||
|
.Builder();
|
||||||
|
```
|
||||||
|
### 初始化
|
||||||
|
```c#
|
||||||
|
TapBootstrap.Init(config);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.账户系统
|
||||||
|
|
||||||
|
> 登陆成功之后,都会得到一个 `TDSUser` 实例
|
||||||
|
|
||||||
|
### 使用 TapTap OAuth 授权结果直接登陆/注册账户系统
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginWithTapTap();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 游客登陆
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginAnonymously();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用第三方平台授权登录/注册账户
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginWithAuthData(Dictionary<string, object> authData, string platform,
|
||||||
|
LCUserAuthDataLoginOption option = null);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 绑定第三方平台授权
|
||||||
|
|
||||||
|
```c#
|
||||||
|
await TDSUser.AssociateAuthData(Dictionary<string, object> authData, string platform);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 退出登陆
|
||||||
|
|
||||||
|
```c#
|
||||||
|
TDSUser.Logout();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.好友系统
|
||||||
|
|
||||||
|
### 申请成为好友
|
||||||
|
|
||||||
|
```c#
|
||||||
|
TDSUser tom, jerry;
|
||||||
|
await tom.ApplyFriendship(jerry);
|
||||||
|
```
|
||||||
|
申请成功的回调中,我们会得到一个 LCFriendshipRequest 的实例,这个实例中包含了两个用户:
|
||||||
|
- sourceUser,指请求的发起方,上面的例子中就是 `tom`。
|
||||||
|
- friend,指请求的目的方,上面的例子中就是 `jerry`。
|
||||||
|
|
||||||
|
tom 也可以在申请好友的时候,添加更多的属性,例如 tom 希望加 jerry 为好友的时候,也设定一个名为 cat 的圈子,可以这样操作:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
Dictionary<string, object> attrs = new Dictionary<string, object> {
|
||||||
|
{ "group", "cat" }
|
||||||
|
};
|
||||||
|
await tom.ApplyFriendship(jerry, attrs);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取好友申请列表
|
||||||
|
好友申请有三种状态:
|
||||||
|
|
||||||
|
- `pending`,对方没有回应,还处于等待中。
|
||||||
|
- `accepted`,对方已经接受,现在双方成为好友。
|
||||||
|
- `declined`,对方已经拒绝。
|
||||||
|
|
||||||
|
好友请求创建之后默认是 `pending` 状态。
|
||||||
|
|
||||||
|
jerry 这里可以通过 `friendshipRequestQuery` 来查找不同状态的请求。例如 jerry 想看看新的好友请求,可以这样操作:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCFriendshipRequest> query = jerry.GetFriendshipRequestQuery(LCFriendshipRequest.STATUS_PENDING, false, true);
|
||||||
|
ReadOnlyCollection<LCFriendshipRequest> reqs = await query.Find();
|
||||||
|
foreach (LCFriendshipRequest req in reqs) {
|
||||||
|
Console.WriteLine(req);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 处理好友申请
|
||||||
|
|
||||||
|
jerry 对于新的好友请求,可以同意或者拒绝,也可以什么都不做,无视这些请求,甚至直接删除。这些操作我们都是支持的,请看下面的示例:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCFriendshipRequest tomRequest, tuffyRequest, otherRequest;
|
||||||
|
await jerry.AcceptFriendshipRequest(tomRequest);
|
||||||
|
await jerry.DeclineFriendshipRequest(tuffyRequest);
|
||||||
|
await jerry.DeleteFriendshipRequest(otherRequest);
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
* 在 jerry 拒绝了 tom 的好友请求之后,如果 tom 再次请求成为 jerry 的好友,tom 在执行 applyFriendshipInBackground 时会直接得到错误的应答,表明 jerry 不想和 ta 成为好友。
|
||||||
|
* jerry 同意了 tuffy 的好友请求之后,它们就成为了好友,之后两个人中任何一人再次调用 applyFriendshipInBackground 申请横位好友时,也会直接得到错误的应答,表明它们已经是好友无需再次申请。
|
||||||
|
* jerry 删除陌生人的好友请求后,对方还可以再次发起请求。
|
||||||
|
|
||||||
|
### 响应好友变化通知
|
||||||
|
|
||||||
|
TDS 好友模块支持客户端监听好友状态变化,在游戏中实时给玩家提示。好友状态变化的接口包括
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class FriendshipNotification {
|
||||||
|
public Action<LCFriendshipRequest> OnNewRequestComing { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestAccepted { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestDeclined { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中:
|
||||||
|
- onNewRequestComing 表示有其他人申请成为当前用户的好友,通过调用 `LCFriendshipRequest#getSourceUser()` 方法可以获得发起方用户信息。
|
||||||
|
- onRequestAccepted 表示当前用户的好友申请被对方通过,通过调用 `LCFriendshipRequest#getFriend()` 方法可以获得对方用户信息。
|
||||||
|
- onRequestDeclined 表示当前用户的好友申请被对方拒绝,通过调用 `LCFriendshipRequest#getFriend()` 方法可以获得对方用户信息。
|
||||||
|
|
||||||
|
开发者可以通过 `TDSUser#registerFriendshipNotification` 来注册通知接收器,通过调用 `TDSUser#unregisterFriendshipNotification` 来取消通知接收器。
|
||||||
|
|
||||||
|
### 获取好友列表
|
||||||
|
|
||||||
|
调用 `TDSUser#friendshipQuery()` 可以得到查询好友的 `LCQuery` 实例,之后调用 `LCQuery#findInBackground()` 方法就可以得到好友列表。示例如下:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCObject> query = jerry.GetFirendshipQuery();
|
||||||
|
```
|
||||||
|
|
||||||
|
LCFriendship 里面会包含两个用户:
|
||||||
|
|
||||||
|
- `LCFriendship#getLCUser(LCFriendship.ATTR_USER)` 得到的是 jerry 自己;
|
||||||
|
- `LCFriendship#getLCUser(LCFriendship.ATTR_FOLLOWEE)` 得到的就是另一方的用户信息。
|
||||||
|
|
||||||
|
### 删除好友
|
||||||
|
|
||||||
|
成为好友关系的两个用户,之后也可以单方面删除好友。例如 jerry 不想再和 tom 成为好友,那只需要在自己的好友列表中删除包含 tom 的那条 LCFriendship 记录即可:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
await friendship.Delete();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询好友关系
|
||||||
|
|
||||||
|
我们使用 LCQuery 可以单独查询两个用户是否为好友关系。
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCObject> query = jerry.GetFirendshipQuery();
|
||||||
|
query.whereEqualTo("followee", tom);
|
||||||
|
int count = await query.Count();
|
||||||
|
if (count > 0) {
|
||||||
|
// tom is a friend of jerry.
|
||||||
|
} else {
|
||||||
|
// tom isn't a friend of jerry.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这一查询是通过网络发送到服务端执行的,一般情况下,我们推荐开发者在游戏启动时拉取一次当前登录用户的好友列表,然后缓存在本地,以后需要检查另外玩家是否为当前用户的好友时,直接从缓存中查询即可。如果担心好友数据变化,缓存没有得到及时更新,可以调用前面「响应好友变化通知」的方法,对好友数据更新进行监听,这样在绝大部分时候数据同步都是可以保证的。
|
||||||
|
|
||||||
|
## 4.云存档
|
||||||
|
|
||||||
|
### 构建云存档元数据
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var gameSave = new TapGameSave
|
||||||
|
{
|
||||||
|
Name = "GameSave_Name",// 存档名称
|
||||||
|
Summary = "GameSave_Description", // 该字段会作为展示给用户的实际存档名
|
||||||
|
ModifiedAt = DateTime.Now.ToLocalTime(), // 原文件修改时间
|
||||||
|
PlayedTime = 1000L, // 游戏时长,单位 ms (非必填)
|
||||||
|
ProgressValue = 100, // 游戏进度 ,单位 int (非必填)
|
||||||
|
CoverFilePath = pic, // 游戏封面,可以传入一个本地文件路径,SDK 限制为 png/jpeg 格式
|
||||||
|
GameFilePath = dll // 存档源文件,可以传入一个本地文件路径
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
### 保存存档
|
||||||
|
|
||||||
|
保存存档时,会去检查当前`TDSUser`是否已经登录以及元数据。
|
||||||
|
|
||||||
|
同时 SDK 在上传时会去限制存档本身以及相关联的两个文件( Cover 以及 GameFile )的权限为当前用户本身。
|
||||||
|
```c#
|
||||||
|
await gameSave.Save();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询当前用户的所有存档
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var collection = await TapGameSave.GetCurrentUserGameSaves();
|
||||||
|
|
||||||
|
foreach(var gameSave in collection){
|
||||||
|
// 存档概览
|
||||||
|
var name = gameSave.Summary;
|
||||||
|
// 原文件修改时间
|
||||||
|
var modifiedAt = gameSave.ModifiedAt;
|
||||||
|
// 游戏时长
|
||||||
|
var playedTime = gameSave.PlayedTime;
|
||||||
|
// 游戏进度
|
||||||
|
var progressValue = gameSave.ProgressValue;
|
||||||
|
// 游戏封面
|
||||||
|
var coverFile = gameSave.CoverFile;
|
||||||
|
// 存档源文件
|
||||||
|
var gameFile = gameSave.GameFile;
|
||||||
|
// 源文件下载地址
|
||||||
|
var gameFileUrl = gameFile.Url;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询当前用户存档
|
||||||
|
|
||||||
|
我们使用 `LCQuery` 来查询当前用户的云存档。
|
||||||
|
|
||||||
|
SDK 查询封装了一个限定方法用于查询当前`TDSUser`的云存档。
|
||||||
|
```c#
|
||||||
|
TDSUser user = await TDSUser.GetCurrent();
|
||||||
|
LCQuery<TapGameSave> gameSaveQuery = TapGameSave.GetQueryWithUser(user);
|
||||||
|
// 查询 Name 为 TDSUser_GameSave_Name 的云存档
|
||||||
|
gameSaveQuery.WhereEqualTo("name","TDSUser_GameSave_Name");
|
||||||
|
var collection = await gameSaveQuery.Find();
|
||||||
|
```
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 17ed6166c017a4fcda77ff618f2703db
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92ca05cc6fb3247f79cddb7f5ef08c2a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using UnityEditor.Build.Reporting;
|
||||||
|
using TapTap.Common.Editor;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap.Editor {
|
||||||
|
public class TapBootstrapProcessBuild : SDKLinkProcessBuild {
|
||||||
|
public override int callbackOrder => 0;
|
||||||
|
|
||||||
|
public override string LinkPath => "TapTap/Bootstrap/link.xml";
|
||||||
|
|
||||||
|
public override LinkedAssembly[] LinkedAssemblies => new LinkedAssembly[] {
|
||||||
|
new LinkedAssembly { Fullname = "TapTap.Bootstrap.Runtime" },
|
||||||
|
};
|
||||||
|
|
||||||
|
public override Func<BuildReport, bool> IsTargetPlatform => (report) => {
|
||||||
|
return BuildTargetUtils.IsSupportMobile(report.summary.platform) ||
|
||||||
|
BuildTargetUtils.IsSupportStandalone(report.summary.platform);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 395365d4e25e54d7cb6a461aa19b50c0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"name": "TapTap.Bootstrap.Editor",
|
||||||
|
"references": [
|
||||||
|
"GUID:616cea76def2d4f059b94440fc8cc03d"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3cfcce84a61c740fc86ea125ab9c81e0
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,254 @@
|
||||||
|
# [使用 TapTap.Bootstrap ](./Documentation/README.md)
|
||||||
|
|
||||||
|
## 使用前提
|
||||||
|
|
||||||
|
使用 TapTap.Bootstrap 前提是必须依赖以下库:
|
||||||
|
* [TapTap.Common](https://github.com/TapTap/TapCommon-Unity.git)
|
||||||
|
* [TapTap.Login](https://github.com/TapTap/TapLogin-Unity.git)
|
||||||
|
* [LeanCloud.Storage](https://github.com/leancloud/csharp-sdk)
|
||||||
|
* [LeanCloud.RealTime](https://github.com/leancloud/csharp-sdk)
|
||||||
|
|
||||||
|
## 命名空间
|
||||||
|
|
||||||
|
```c#
|
||||||
|
using TapTap.Bootstrap;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 接口描述
|
||||||
|
|
||||||
|
## 1.初始化
|
||||||
|
|
||||||
|
TapBootstrap 会根据 TapConfig 中的 TapDBConfig 配置来进行 TapDB 的自动初始化。
|
||||||
|
|
||||||
|
### 开启 TapDB
|
||||||
|
```c#
|
||||||
|
var config = new TapConfig.Builder()
|
||||||
|
.ClientID("client_id")
|
||||||
|
.ClientToken("client_token")
|
||||||
|
.ServerURL("https://ikggdre2.lc-cn-n1-shared.com")
|
||||||
|
.RegionType(RegionType.CN)
|
||||||
|
.TapDBConfig(true,"channel","gameVersion",true)
|
||||||
|
.Builder();
|
||||||
|
```
|
||||||
|
### 关闭 TapDB
|
||||||
|
```c#
|
||||||
|
var config = new TapConfig.Builder()
|
||||||
|
.ClientID("client_id")
|
||||||
|
.ClientToken("client_token")
|
||||||
|
.ServerURL("https://ikggdre2.lc-cn-n1-shared.com")
|
||||||
|
.RegionType(RegionType.CN)
|
||||||
|
//# .TapDBConfig(false,null,null,false)
|
||||||
|
.EnableTapDB(false)
|
||||||
|
.Builder();
|
||||||
|
```
|
||||||
|
### 初始化
|
||||||
|
```c#
|
||||||
|
TapBootstrap.Init(config);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2.账户系统
|
||||||
|
|
||||||
|
> 登陆成功之后,都会得到一个 `TDSUser` 实例
|
||||||
|
|
||||||
|
### 使用 TapTap OAuth 授权结果直接登陆/注册账户系统
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginWithTapTap();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 游客登陆
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginAnonymously();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用第三方平台授权登录/注册账户
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var tdsUser = await TDSUser.LoginWithAuthData(Dictionary<string, object> authData, string platform,
|
||||||
|
LCUserAuthDataLoginOption option = null);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 绑定第三方平台授权
|
||||||
|
|
||||||
|
```c#
|
||||||
|
await TDSUser.AssociateAuthData(Dictionary<string, object> authData, string platform);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 退出登陆
|
||||||
|
|
||||||
|
```c#
|
||||||
|
TDSUser.Logout();
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3.好友系统
|
||||||
|
|
||||||
|
### 申请成为好友
|
||||||
|
|
||||||
|
```c#
|
||||||
|
TDSUser tom, jerry;
|
||||||
|
await tom.ApplyFriendship(jerry);
|
||||||
|
```
|
||||||
|
申请成功的回调中,我们会得到一个 LCFriendshipRequest 的实例,这个实例中包含了两个用户:
|
||||||
|
- sourceUser,指请求的发起方,上面的例子中就是 `tom`。
|
||||||
|
- friend,指请求的目的方,上面的例子中就是 `jerry`。
|
||||||
|
|
||||||
|
tom 也可以在申请好友的时候,添加更多的属性,例如 tom 希望加 jerry 为好友的时候,也设定一个名为 cat 的圈子,可以这样操作:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
Dictionary<string, object> attrs = new Dictionary<string, object> {
|
||||||
|
{ "group", "cat" }
|
||||||
|
};
|
||||||
|
await tom.ApplyFriendship(jerry, attrs);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 获取好友申请列表
|
||||||
|
好友申请有三种状态:
|
||||||
|
|
||||||
|
- `pending`,对方没有回应,还处于等待中。
|
||||||
|
- `accepted`,对方已经接受,现在双方成为好友。
|
||||||
|
- `declined`,对方已经拒绝。
|
||||||
|
|
||||||
|
好友请求创建之后默认是 `pending` 状态。
|
||||||
|
|
||||||
|
jerry 这里可以通过 `friendshipRequestQuery` 来查找不同状态的请求。例如 jerry 想看看新的好友请求,可以这样操作:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCFriendshipRequest> query = jerry.GetFriendshipRequestQuery(LCFriendshipRequest.STATUS_PENDING, false, true);
|
||||||
|
ReadOnlyCollection<LCFriendshipRequest> reqs = await query.Find();
|
||||||
|
foreach (LCFriendshipRequest req in reqs) {
|
||||||
|
Console.WriteLine(req);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 处理好友申请
|
||||||
|
|
||||||
|
jerry 对于新的好友请求,可以同意或者拒绝,也可以什么都不做,无视这些请求,甚至直接删除。这些操作我们都是支持的,请看下面的示例:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCFriendshipRequest tomRequest, tuffyRequest, otherRequest;
|
||||||
|
await jerry.AcceptFriendshipRequest(tomRequest);
|
||||||
|
await jerry.DeclineFriendshipRequest(tuffyRequest);
|
||||||
|
await jerry.DeleteFriendshipRequest(otherRequest);
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:
|
||||||
|
* 在 jerry 拒绝了 tom 的好友请求之后,如果 tom 再次请求成为 jerry 的好友,tom 在执行 applyFriendshipInBackground 时会直接得到错误的应答,表明 jerry 不想和 ta 成为好友。
|
||||||
|
* jerry 同意了 tuffy 的好友请求之后,它们就成为了好友,之后两个人中任何一人再次调用 applyFriendshipInBackground 申请横位好友时,也会直接得到错误的应答,表明它们已经是好友无需再次申请。
|
||||||
|
* jerry 删除陌生人的好友请求后,对方还可以再次发起请求。
|
||||||
|
|
||||||
|
### 响应好友变化通知
|
||||||
|
|
||||||
|
TDS 好友模块支持客户端监听好友状态变化,在游戏中实时给玩家提示。好友状态变化的接口包括
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class FriendshipNotification {
|
||||||
|
public Action<LCFriendshipRequest> OnNewRequestComing { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestAccepted { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestDeclined { get; set; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中:
|
||||||
|
- onNewRequestComing 表示有其他人申请成为当前用户的好友,通过调用 `LCFriendshipRequest#getSourceUser()` 方法可以获得发起方用户信息。
|
||||||
|
- onRequestAccepted 表示当前用户的好友申请被对方通过,通过调用 `LCFriendshipRequest#getFriend()` 方法可以获得对方用户信息。
|
||||||
|
- onRequestDeclined 表示当前用户的好友申请被对方拒绝,通过调用 `LCFriendshipRequest#getFriend()` 方法可以获得对方用户信息。
|
||||||
|
|
||||||
|
开发者可以通过 `TDSUser#registerFriendshipNotification` 来注册通知接收器,通过调用 `TDSUser#unregisterFriendshipNotification` 来取消通知接收器。
|
||||||
|
|
||||||
|
### 获取好友列表
|
||||||
|
|
||||||
|
调用 `TDSUser#friendshipQuery()` 可以得到查询好友的 `LCQuery` 实例,之后调用 `LCQuery#findInBackground()` 方法就可以得到好友列表。示例如下:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCObject> query = jerry.GetFirendshipQuery();
|
||||||
|
```
|
||||||
|
|
||||||
|
LCFriendship 里面会包含两个用户:
|
||||||
|
|
||||||
|
- `LCFriendship#getLCUser(LCFriendship.ATTR_USER)` 得到的是 jerry 自己;
|
||||||
|
- `LCFriendship#getLCUser(LCFriendship.ATTR_FOLLOWEE)` 得到的就是另一方的用户信息。
|
||||||
|
|
||||||
|
### 删除好友
|
||||||
|
|
||||||
|
成为好友关系的两个用户,之后也可以单方面删除好友。例如 jerry 不想再和 tom 成为好友,那只需要在自己的好友列表中删除包含 tom 的那条 LCFriendship 记录即可:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
await friendship.Delete();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询好友关系
|
||||||
|
|
||||||
|
我们使用 LCQuery 可以单独查询两个用户是否为好友关系。
|
||||||
|
|
||||||
|
```cs
|
||||||
|
LCQuery<LCObject> query = jerry.GetFirendshipQuery();
|
||||||
|
query.whereEqualTo("followee", tom);
|
||||||
|
int count = await query.Count();
|
||||||
|
if (count > 0) {
|
||||||
|
// tom is a friend of jerry.
|
||||||
|
} else {
|
||||||
|
// tom isn't a friend of jerry.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这一查询是通过网络发送到服务端执行的,一般情况下,我们推荐开发者在游戏启动时拉取一次当前登录用户的好友列表,然后缓存在本地,以后需要检查另外玩家是否为当前用户的好友时,直接从缓存中查询即可。如果担心好友数据变化,缓存没有得到及时更新,可以调用前面「响应好友变化通知」的方法,对好友数据更新进行监听,这样在绝大部分时候数据同步都是可以保证的。
|
||||||
|
|
||||||
|
## 4.云存档
|
||||||
|
|
||||||
|
### 构建云存档元数据
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var gameSave = new TapGameSave
|
||||||
|
{
|
||||||
|
Name = "GameSave_Name",// 存档名称
|
||||||
|
Summary = "GameSave_Description", // 该字段会作为展示给用户的实际存档名
|
||||||
|
ModifiedAt = DateTime.Now.ToLocalTime(), // 原文件修改时间
|
||||||
|
PlayedTime = 1000L, // 游戏时长,单位 ms (非必填)
|
||||||
|
ProgressValue = 100, // 游戏进度 ,单位 int (非必填)
|
||||||
|
CoverFilePath = pic, // 游戏封面,可以传入一个本地文件路径,SDK 限制为 png/jpeg 格式
|
||||||
|
GameFilePath = dll // 存档源文件,可以传入一个本地文件路径
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
### 保存存档
|
||||||
|
|
||||||
|
保存存档时,会去检查当前`TDSUser`是否已经登录以及元数据。
|
||||||
|
|
||||||
|
同时 SDK 在上传时会去限制存档本身以及相关联的两个文件( Cover 以及 GameFile )的权限为当前用户本身。
|
||||||
|
```c#
|
||||||
|
await gameSave.Save();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询当前用户的所有存档
|
||||||
|
|
||||||
|
```c#
|
||||||
|
var collection = await TapGameSave.GetCurrentUserGameSaves();
|
||||||
|
|
||||||
|
foreach(var gameSave in collection){
|
||||||
|
// 存档概览
|
||||||
|
var name = gameSave.Summary;
|
||||||
|
// 原文件修改时间
|
||||||
|
var modifiedAt = gameSave.ModifiedAt;
|
||||||
|
// 游戏时长
|
||||||
|
var playedTime = gameSave.PlayedTime;
|
||||||
|
// 游戏进度
|
||||||
|
var progressValue = gameSave.ProgressValue;
|
||||||
|
// 游戏封面
|
||||||
|
var coverFile = gameSave.CoverFile;
|
||||||
|
// 存档源文件
|
||||||
|
var gameFile = gameSave.GameFile;
|
||||||
|
// 源文件下载地址
|
||||||
|
var gameFileUrl = gameFile.Url;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查询云存档
|
||||||
|
云存档只能查询当前用户的所有存档,需要查询该用户的特定存档时,可以增加相应过滤,可参考 [查询条件](https://developer.taptap.com/docs/sdk/storage/guide/objc/#%E6%9F%A5%E8%AF%A2%E6%9D%A1%E4%BB%B6)
|
||||||
|
```c#
|
||||||
|
TDSUser user = await TDSUser.GetCurrent();
|
||||||
|
LCQuery<TapGameSave> gameSaveQuery = TapGameSave.GetQueryWithUser(user);
|
||||||
|
// 查询 Name 为 TDSUser_GameSave_Name 的云存档
|
||||||
|
gameSaveQuery.WhereEqualTo("name","TDSUser_GameSave_Name");
|
||||||
|
var collection = await gameSaveQuery.Find();
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c5330aab2c6a41699a1601f1af6b27d9
|
||||||
|
timeCreated: 1616755935
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ff7a465ff38894e5c806c4c68b81994b
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 11618fe14bf534d1fa85a19c9cf8d5bd
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5787ee3104e1a4ebd9f25f9bd5a82523
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
using LeanCloud;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
using TapTap.Common;
|
||||||
|
using TapTap.Common.Internal.Init;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap.Internal.Init {
|
||||||
|
public class BootstrapInitTask : IInitTask {
|
||||||
|
public int Order => 12;
|
||||||
|
|
||||||
|
public void Init(TapConfig config) {
|
||||||
|
LCApplication.Initialize(config.ClientID, config.ClientToken, config.ServerURL);
|
||||||
|
LCObject.RegisterSubclass("_User", () => new TDSUser());
|
||||||
|
LCObject.RegisterSubclass(TapGameSave.CLASS_NAME, () => new TapGameSave());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f5ad7914a84cf410e898068636f3790f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap.@internal
|
||||||
|
{
|
||||||
|
public class TDSLeaderBoardRankingService
|
||||||
|
{
|
||||||
|
public TDSLeaderBoardRankingService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ReadOnlyCollection<TDSLeaderBoardRanking>> QueryList(string name,
|
||||||
|
int from, int limit)
|
||||||
|
{
|
||||||
|
string api = "/friend/v2/taptap/leadboard";
|
||||||
|
Dictionary<string, object> queryParams = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{"type", name },
|
||||||
|
{"skip", from },
|
||||||
|
{"limit", limit }
|
||||||
|
};
|
||||||
|
Dictionary<string, object> response = await LCCore.HttpClient.Get<Dictionary<string, object>>(api,
|
||||||
|
queryParams: queryParams, withAPIVersion: false);
|
||||||
|
if (response.TryGetValue("results", out object resultObj) && resultObj is List<object> results)
|
||||||
|
{
|
||||||
|
return results.Cast<Dictionary<string, object>>()
|
||||||
|
.Select(item => {
|
||||||
|
return new TDSLeaderBoardRanking(item);
|
||||||
|
}).ToList().AsReadOnly();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 645b957e90a0842298583193473e506f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Common;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using TapTap.Common;
|
||||||
|
using TapTap.Login;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap.@internal
|
||||||
|
{
|
||||||
|
public class TDSUserEventTrigger
|
||||||
|
{
|
||||||
|
private static bool tdsUserLogin;
|
||||||
|
|
||||||
|
public static void TriggerLoginInfo(TDSUser tdsUser) {
|
||||||
|
if (!tdsUserLogin && tdsUser != null && !string.IsNullOrEmpty(tdsUser.ObjectId)) {
|
||||||
|
TriggerLoginInfo(UnityTDSUser.TDS_CHANNEL, tdsUser.ObjectId);
|
||||||
|
tdsUserLogin = true;
|
||||||
|
if (tdsUser?.AuthData != null && tdsUser.AuthData.ContainsKey("taptap")) {
|
||||||
|
var dic = tdsUser.AuthData["taptap"] as Dictionary<string, object>;
|
||||||
|
if (dic != null && dic.TryGetValue("unionid", out var unionId)) {
|
||||||
|
TriggerLoginInfo(UnityTDSUser.TAP_AUTH_CHANNEL, unionId as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TriggerLoginInfo(string platform, string userId) {
|
||||||
|
if (string.IsNullOrEmpty(platform) || string.IsNullOrEmpty(userId)) return;
|
||||||
|
if (platform.Equals("taptap".ToLower())) platform = UnityTDSUser.TDS_CHANNEL;
|
||||||
|
EventManager.TriggerEvent(EventConst.OnTapLogin, new KeyValuePair<string, string>(platform, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void TriggerLogoutInfo(string platform) {
|
||||||
|
EventManager.TriggerEvent(EventConst.OnTapLogout, platform);
|
||||||
|
tdsUserLogin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TriggerBindInfo(string platform, string userId) {
|
||||||
|
if (string.IsNullOrEmpty(platform) || string.IsNullOrEmpty(userId)) return;
|
||||||
|
if (platform.Equals("taptap".ToLower())) platform = UnityTDSUser.TDS_CHANNEL;
|
||||||
|
EventManager.TriggerEvent(EventConst.OnBind, new KeyValuePair<string, string>(platform, userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void TriggerUnbindInfo(string platform) {
|
||||||
|
if (string.IsNullOrEmpty(platform)) return;
|
||||||
|
if (platform.Equals("taptap".ToLower())) platform = UnityTDSUser.TDS_CHANNEL;
|
||||||
|
EventManager.TriggerEvent(EventConst.OnUnbind, platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9b209f0f3604d809035b0ac2ddd7a5b
|
||||||
|
timeCreated: 1692241103
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d4d24a39c0c00462f92614f8c3eac3d0
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
using TapTap.Common;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public interface ITapBootstrap
|
||||||
|
{
|
||||||
|
void Init(TapConfig config);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b7e098f3b3504e6e80966d9b6be1131
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Storage.Internal.Object;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TapTap.Bootstrap.@internal;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public class TDSLeaderBoardRanking
|
||||||
|
{
|
||||||
|
public string StatisticName
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StatisticValue
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Rank
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TDSUser User
|
||||||
|
{
|
||||||
|
get; internal set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal TDSLeaderBoardRanking(Dictionary<string,object> data)
|
||||||
|
{
|
||||||
|
|
||||||
|
StatisticName = data["statisticName"] as string;
|
||||||
|
StatisticValue = Convert.ToInt32(data["statisticValue"]);
|
||||||
|
Rank = Convert.ToInt32(data["rank"]);
|
||||||
|
TDSUser tdsUser = TDSUser.Create(TDSUser.CLASS_NAME) as TDSUser;
|
||||||
|
if (data.TryGetValue("user", out object userObj) &&
|
||||||
|
userObj is Dictionary<string, object> userDict)
|
||||||
|
{
|
||||||
|
tdsUser.Merge(LCObjectData.Decode(userDict));
|
||||||
|
}
|
||||||
|
User = tdsUser;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<ReadOnlyCollection<TDSLeaderBoardRanking>> QueryTapFriendsLeaderBoard(string name,
|
||||||
|
int from, int limit)
|
||||||
|
{
|
||||||
|
return await TDSLeaderBoardRankingService.QueryList(name, from, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 23d637800a62a4ecdbfff2cf39446485
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,408 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
using LeanCloud.LiveQuery;
|
||||||
|
using TapTap.Common;
|
||||||
|
using TapTap.Bootstrap.@internal;
|
||||||
|
using TapTap.Login;
|
||||||
|
using TapTap.Login.Internal;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public class TDSUser : LCUser {
|
||||||
|
public new string Password {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
set { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new string Email {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
set { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new string Mobile {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
set { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new bool EmailVerified {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new bool MobileVerified {
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Task<TDSUser> SignUp() {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestLoginSMSCode(string mobile) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task<TDSUser> SignUpOrLoginByMobilePhone(string mobile, string code) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task<TDSUser> Login(string username, string password) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task<TDSUser> LoginByEmail(string email, string password) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task<TDSUser> LoginByMobilePhoneNumber(string mobile, string password) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task<TDSUser> LoginBySMSCode(string mobile, string code) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestEmailVerify(string email) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestMobilePhoneVerify(string mobile) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task VerifyMobilePhone(string mobile, string code) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestPasswordReset(string email) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestPasswordResetBySmsCode(string mobile) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task ResetPasswordBySmsCode(string mobile, string code, string newPassword) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Task UpdatePassword(string oldPassword, string newPassword) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task RequestSMSCodeForUpdatingPhoneNumber(string mobile, int ttl = 360,
|
||||||
|
string captchaToken = null) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new Task VerifyCodeForUpdatingPhoneNumber(string mobile, string code) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently logged in TDSUser with a valid session, from
|
||||||
|
/// memory or disk if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new async Task<TDSUser> GetCurrent() {
|
||||||
|
LCUser user = await LCUser.GetCurrent();
|
||||||
|
var result = user as TDSUser;
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs in a user with a sessionToken.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sessionToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new async Task<TDSUser> BecomeWithSessionToken(string sessionToken) {
|
||||||
|
return (await LCUser.BecomeWithSessionToken(sessionToken)) as TDSUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs in a user with a sessionToken for XDSDK.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sessionToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<TDSUser> LoginWithSessionToken(string sessionToken) {
|
||||||
|
var result = (await LCUser.BecomeWithSessionToken(sessionToken)) as TDSUser;
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs up or signs in a user with third party authData.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="authData"></param>
|
||||||
|
/// <param name="platform"></param>
|
||||||
|
/// <param name="option"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new async Task<TDSUser> LoginWithAuthData(Dictionary<string, object> authData, string platform,
|
||||||
|
LCUserAuthDataLoginOption option = null) {
|
||||||
|
var result = (await LCUser.LoginWithAuthData(authData, platform, option)) as TDSUser;
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Task AssociateAuthData(Dictionary<string, object> authData, string platform) {
|
||||||
|
var result = base.AssociateAuthData(authData, platform);
|
||||||
|
TDSUserEventTrigger.TriggerBindInfo(platform, ObjectId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public new Task AssociateAuthDataAndUnionId(Dictionary<string, object> authData,
|
||||||
|
string platform,
|
||||||
|
string unionId,
|
||||||
|
LCUserAuthDataLoginOption option = null) {
|
||||||
|
var result = base.AssociateAuthDataAndUnionId(authData, platform, unionId, option);
|
||||||
|
TDSUserEventTrigger.TriggerBindInfo(platform, ObjectId);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new Task DisassociateWithAuthData(string platform) {
|
||||||
|
var result = base.DisassociateWithAuthData(platform);
|
||||||
|
TDSUserEventTrigger.TriggerUnbindInfo(platform);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs up or signs in a user with third party authData and unionId.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="authData"></param>
|
||||||
|
/// <param name="platform"></param>
|
||||||
|
/// <param name="unionId"></param>
|
||||||
|
/// <param name="option"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new async Task<TDSUser> LoginWithAuthDataAndUnionId(Dictionary<string, object> authData,
|
||||||
|
string platform, string unionId,
|
||||||
|
LCUserAuthDataLoginOption option = null) {
|
||||||
|
var result = (await LCUser.LoginWithAuthDataAndUnionId(authData, platform, unionId, option)) as TDSUser;
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an anonymous user.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new async Task<TDSUser> LoginAnonymously() {
|
||||||
|
var result = (await LCUser.LoginAnonymously()) as TDSUser;
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signs up or signs in a user with TapTap.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<TDSUser> LoginWithTapTap(string[] permissions = null) {
|
||||||
|
Dictionary<string, object> authData = await LoginTapTap(permissions);
|
||||||
|
LCUser user = await LoginWithAuthData(authData, "taptap");
|
||||||
|
TDSUserEventTrigger.TriggerLoginInfo(user as TDSUser);
|
||||||
|
return user as TDSUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs out the currently logged in user session.
|
||||||
|
/// </summary>
|
||||||
|
public static new async Task Logout() {
|
||||||
|
TapLogin.Logout();
|
||||||
|
TDSUserEventTrigger.TriggerLogoutInfo(UnityTDSUser.TDS_CHANNEL);
|
||||||
|
await LCUser.Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a LCQuery for TDSUser.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static new LCQuery<TDSUser> GetQuery() {
|
||||||
|
return new LCQuery<TDSUser>(CLASS_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save this user to the cloud.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fetchWhenSave"></param>
|
||||||
|
/// <param name="query"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public new async Task<TDSUser> Save(bool fetchWhenSave = false, LCQuery<LCObject> query = null) {
|
||||||
|
return (await base.Save(fetchWhenSave, query)) as TDSUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private static async Task<Dictionary<string, object>> LoginTapTap(string[] permissions) {
|
||||||
|
AccessToken token;
|
||||||
|
if (permissions == null) {
|
||||||
|
token = await TapLogin.Login();
|
||||||
|
} else {
|
||||||
|
token = await TapLogin.Login(permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
var profile = await TapLogin.GetProfile();
|
||||||
|
|
||||||
|
var result = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{"kid", token.kid},
|
||||||
|
{"access_token", token.accessToken},
|
||||||
|
{"token_type", token.tokenType},
|
||||||
|
{"mac_key", token.macKey},
|
||||||
|
{"mac_algorithm", token.macAlgorithm},
|
||||||
|
|
||||||
|
{"openid", profile.openid},
|
||||||
|
{"name", profile.name},
|
||||||
|
{"avatar", profile.avatar},
|
||||||
|
{"unionid", profile.unionid}
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Friendship
|
||||||
|
|
||||||
|
public class FriendshipNotification {
|
||||||
|
public Action<LCFriendshipRequest> OnNewRequestComing { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestAccepted { get; set; }
|
||||||
|
public Action<LCFriendshipRequest> OnRequestDeclined { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private LCLiveQuery friendshipLivequery;
|
||||||
|
|
||||||
|
public Task ApplyFriendship(TDSUser user, Dictionary<string, object> attributes = null) {
|
||||||
|
if (user == null || string.IsNullOrEmpty(user.ObjectId)) {
|
||||||
|
throw new ArgumentNullException("User or userId is null.");
|
||||||
|
}
|
||||||
|
return ApplyFriendship(user.ObjectId, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ApplyFriendship(string userId, Dictionary<string, object> attributes = null) {
|
||||||
|
if (string.IsNullOrEmpty(userId)) {
|
||||||
|
throw new ArgumentNullException(nameof(userId));
|
||||||
|
}
|
||||||
|
return LCFriendship.Request(userId, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task AcceptFriendshipRequest(LCFriendshipRequest request, Dictionary<string, object> attributes = null) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
}
|
||||||
|
return LCFriendship.AcceptRequest(request, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeclineFriendshipRequest(LCFriendshipRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
}
|
||||||
|
return LCFriendship.DeclineRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteFriendshipRequest(LCFriendshipRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new ArgumentNullException(nameof(request));
|
||||||
|
}
|
||||||
|
return request.Delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCQuery<LCFriendshipRequest> GetFriendshipRequestQuery(int status, bool includeTargetUser, bool reachToCurrentUser) {
|
||||||
|
List<string> statusList = new List<string>();
|
||||||
|
if ((status & LCFriendshipRequest.STATUS_PENDING) == LCFriendshipRequest.STATUS_PENDING) {
|
||||||
|
statusList.Add("pending");
|
||||||
|
}
|
||||||
|
if ((status & LCFriendshipRequest.STATUS_ACCEPTED) == LCFriendshipRequest.STATUS_ACCEPTED) {
|
||||||
|
statusList.Add("accepted");
|
||||||
|
}
|
||||||
|
if ((status & LCFriendshipRequest.STATUS_DECLINED) == LCFriendshipRequest.STATUS_DECLINED) {
|
||||||
|
statusList.Add("declined");
|
||||||
|
}
|
||||||
|
if (statusList.Count < 1) {
|
||||||
|
throw new ArgumentException("status is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LCQuery<LCFriendshipRequest> query = LCFriendshipRequest.GetQuery();
|
||||||
|
query.WhereContainedIn("status", statusList);
|
||||||
|
if (reachToCurrentUser) {
|
||||||
|
query.WhereEqualTo("friend", this);
|
||||||
|
if (includeTargetUser) {
|
||||||
|
query.Include("user");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query.WhereEqualTo("user", this);
|
||||||
|
if (includeTargetUser) {
|
||||||
|
query.Include("friend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.AddDescendingOrder("updatedAt");
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCQuery<LCObject> GetFriendshipQuery() {
|
||||||
|
return FriendshipQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task RegisterFriendshipNotification(FriendshipNotification notification) {
|
||||||
|
if (friendshipLivequery != null) {
|
||||||
|
// 避免重复注册
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 LiveQuery
|
||||||
|
LCQuery<LCFriendshipRequest> selfRequestQuery = new LCQuery<LCFriendshipRequest>(LCFriendshipRequest.CLASS_NAME)
|
||||||
|
.WhereEqualTo("user", this);
|
||||||
|
LCQuery<LCFriendshipRequest> otherRequestQuery = new LCQuery<LCFriendshipRequest>(LCFriendshipRequest.CLASS_NAME)
|
||||||
|
.WhereEqualTo("friend", this);
|
||||||
|
LCQuery<LCFriendshipRequest> allQuery = LCQuery<LCFriendshipRequest>.Or(new LCQuery<LCFriendshipRequest>[] {
|
||||||
|
selfRequestQuery, otherRequestQuery
|
||||||
|
});
|
||||||
|
|
||||||
|
friendshipLivequery = await allQuery.Subscribe();
|
||||||
|
friendshipLivequery.OnCreate = obj => {
|
||||||
|
if (!(obj is LCFriendshipRequest req)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LCUser friend = req.Friend;
|
||||||
|
if (friend == null || friend.ObjectId != ObjectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.OnNewRequestComing(req);
|
||||||
|
};
|
||||||
|
friendshipLivequery.OnUpdate = (obj, keys) => {
|
||||||
|
if (!(obj is LCFriendshipRequest req)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keys == null || !keys.Contains("status")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LCUser user = req.User;
|
||||||
|
if (user == null || user.ObjectId != ObjectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string status = req.Status;
|
||||||
|
if (status == "accepted") {
|
||||||
|
notification.OnRequestAccepted(req);
|
||||||
|
} else if (status == "declined") {
|
||||||
|
notification.OnRequestDeclined(req);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnregisterFriendshipNotification() {
|
||||||
|
if (friendshipLivequery == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await friendshipLivequery.Unsubscribe();
|
||||||
|
friendshipLivequery = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 62c37df89e7e848a0b8133d5a4e0bcc8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
||||||
|
using TapTap.Common;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public class TapBootstrap
|
||||||
|
{
|
||||||
|
public static void Init(TapConfig tapConfig)
|
||||||
|
{
|
||||||
|
TapBootstrapImpl.GetInstance().Init(tapConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetPreferredLanguage(TapLanguage language)
|
||||||
|
{
|
||||||
|
TapCommon.SetLanguage(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b97116f83686249a9a4a9d6eb5400170
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,87 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
using TapTap.Common;
|
||||||
|
using TapTap.Common.Internal.Init;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public class TapBootstrapImpl : ITapBootstrap
|
||||||
|
{
|
||||||
|
private static volatile TapBootstrapImpl _sInstance;
|
||||||
|
|
||||||
|
private static readonly object Locker = new object();
|
||||||
|
|
||||||
|
public static TapBootstrapImpl GetInstance()
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
if (_sInstance == null)
|
||||||
|
{
|
||||||
|
_sInstance = new TapBootstrapImpl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Init(TapConfig config)
|
||||||
|
{
|
||||||
|
// 初始化各个模块
|
||||||
|
Type interfaceType = typeof(IInitTask);
|
||||||
|
Type[] initTaskTypes = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
|
.Where(asssembly => asssembly.GetName().FullName.StartsWith("TapTap"))
|
||||||
|
.SelectMany(assembly => assembly.GetTypes())
|
||||||
|
.Where(clazz => interfaceType.IsAssignableFrom(clazz) && clazz.IsClass)
|
||||||
|
.ToArray();
|
||||||
|
if (initTaskTypes != null) {
|
||||||
|
List<IInitTask> initTasks = new List<IInitTask>();
|
||||||
|
foreach (Type initTaskType in initTaskTypes) {
|
||||||
|
initTasks.Add(Activator.CreateInstance(initTaskType) as IInitTask);
|
||||||
|
}
|
||||||
|
initTasks = initTasks.OrderBy(task => task.Order).ToList();
|
||||||
|
foreach (IInitTask task in initTasks) {
|
||||||
|
TapLogger.Debug($"Init: {task.GetType().Name}");
|
||||||
|
task.Init(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TapCommon.SetXua();
|
||||||
|
|
||||||
|
await TDSUser.GetCurrent();
|
||||||
|
|
||||||
|
TapCommon.RegisterProperties("sessionToken", new SessionTokenProperty());
|
||||||
|
|
||||||
|
TapCommon.RegisterProperties("objectId", new ObjectIdProperty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SessionTokenProperty : ITapPropertiesProxy
|
||||||
|
{
|
||||||
|
public string GetProperties()
|
||||||
|
{
|
||||||
|
Debug.Log($"sessionToken User:{GetCurrentUser()}");
|
||||||
|
var sessionToken = GetCurrentUser()?.SessionToken;
|
||||||
|
return string.IsNullOrEmpty(sessionToken) ? "" : sessionToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ObjectIdProperty : ITapPropertiesProxy
|
||||||
|
{
|
||||||
|
public string GetProperties()
|
||||||
|
{
|
||||||
|
Debug.Log($"objectId User:{GetCurrentUser()}");
|
||||||
|
var objectId = GetCurrentUser()?.ObjectId;
|
||||||
|
return string.IsNullOrEmpty(objectId) ? "" : objectId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LCUser GetCurrentUser()
|
||||||
|
{
|
||||||
|
var field = typeof(LCUser).GetField("currentUser", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
return field?.GetValue(new LCUser()) as LCUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fde65756640e24079982b9077782ed28
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,154 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using LeanCloud.Storage;
|
||||||
|
|
||||||
|
namespace TapTap.Bootstrap
|
||||||
|
{
|
||||||
|
public class TapGameSave : LCObject
|
||||||
|
{
|
||||||
|
public const string CLASS_NAME = "_GameSave";
|
||||||
|
|
||||||
|
private const string PATH_PREFIX = "gamesaves";
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => this["name"] as string;
|
||||||
|
set => this["name"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Summary
|
||||||
|
{
|
||||||
|
get => this["summary"] as string;
|
||||||
|
set => this["summary"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime ModifiedAt
|
||||||
|
{
|
||||||
|
get => this["modifiedAt"] is DateTime ? (DateTime) this["modifiedAt"] : default;
|
||||||
|
set => this["modifiedAt"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double PlayedTime
|
||||||
|
{
|
||||||
|
get => this["playedTime"] is double ? (double) this["playedTime"] : -1d;
|
||||||
|
set => this["playedTime"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ProgressValue
|
||||||
|
{
|
||||||
|
get => this["progressValue"] is int ? (int) this["progressValue"] : -1;
|
||||||
|
set => this["progressValue"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCFile Cover
|
||||||
|
{
|
||||||
|
get => this["cover"] as LCFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCFile GameFile
|
||||||
|
{
|
||||||
|
get => this["gameFile"] as LCFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LCUser User
|
||||||
|
{
|
||||||
|
set => this["user"] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CoverFilePath
|
||||||
|
{
|
||||||
|
set => this["cover"] = new LCFile(Path.GetFileName(value), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GameFilePath
|
||||||
|
{
|
||||||
|
set => this["gameFile"] = new LCFile(Path.GetFileName(value), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckArguments()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Name)) throw new ArgumentNullException(nameof(Name));
|
||||||
|
if (string.IsNullOrEmpty(Summary)) throw new ArgumentNullException(nameof(Summary));
|
||||||
|
if (Summary.Length > 1000) throw new ArgumentOutOfRangeException(nameof(Summary));
|
||||||
|
if (GameFile == null) throw new ArgumentNullException(nameof(GameFile));
|
||||||
|
if (Cover == null) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GameSaveMimeType
|
||||||
|
{
|
||||||
|
internal static readonly List<string> SupportImageMimeType = new List<string>
|
||||||
|
{
|
||||||
|
"image/png", "image/jpeg"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public TapGameSave()
|
||||||
|
: base(CLASS_NAME)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region API
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a GameSave to cloud for Current User.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<TapGameSave> Save()
|
||||||
|
{
|
||||||
|
var currentUser = await LCUser.GetCurrent();
|
||||||
|
if (currentUser == null) throw new UnauthorizedAccessException("Not Login");
|
||||||
|
CheckArguments();
|
||||||
|
var acl = new LCACL();
|
||||||
|
acl.SetUserWriteAccess(currentUser, true);
|
||||||
|
acl.SetUserReadAccess(currentUser, true);
|
||||||
|
ACL = acl;
|
||||||
|
User = currentUser;
|
||||||
|
if (Cover != null)
|
||||||
|
{
|
||||||
|
Cover.ACL = acl;
|
||||||
|
Cover.PathPrefix = PATH_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameFile.ACL = acl;
|
||||||
|
GameFile.PathPrefix = PATH_PREFIX;
|
||||||
|
return await base.Save() as TapGameSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all GameSave by Current User.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<ReadOnlyCollection<TapGameSave>> GetCurrentUserGameSaves()
|
||||||
|
{
|
||||||
|
var user = await LCUser.GetCurrent();
|
||||||
|
if (user == null) throw new UnauthorizedAccessException("Not Login");
|
||||||
|
return await GetQueryWithUser(user).Find();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor a LCQuery for GameSave.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static LCQuery<TapGameSave> GetQuery() => new LCQuery<TapGameSave>(CLASS_NAME);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor a LCQuery for GameSave with LCUser
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static LCQuery<TapGameSave> GetQueryWithUser(LCUser user)
|
||||||
|
{
|
||||||
|
var query = GetQuery();
|
||||||
|
query.Include("cover");
|
||||||
|
query.Include("gameFile");
|
||||||
|
query.WhereEqualTo("user", user);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion API
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec803f6b9bcc04c97aebbba198757ef4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "TapTap.Bootstrap.Runtime",
|
||||||
|
"references": [
|
||||||
|
"GUID:e8754b6153389406c963cd52996cc80f",
|
||||||
|
"GUID:0b3f64ec33f5b4da98a17367a35b82f2"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ab0f87386c0640798a09051c047d4ab
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5391b17003874fb2afc3e309a3f63ef
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "com.taptap.tds.bootstrap",
|
||||||
|
"displayName": "TapTap Bootstrap",
|
||||||
|
"description": "TapTap Develop Service",
|
||||||
|
"version": "3.25.0-dev.3",
|
||||||
|
"unity": "2019.4",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"com.taptap.tds.login": "3.25.0-dev.3",
|
||||||
|
"com.leancloud.realtime": "2.3.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e618b9375caf846708cffea428908521
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue