一、能力介绍
功能介绍
如小游戏没有服务器,那么可以通过开放平台提供的云存档功能对游戏进度、资产数据进行存档,解决无服务器游戏换手机后无存档的问题,保障用户体验。由于原先的云存档能力不支持高性能容器,目前淘宝平台对云存档能力进行了升级,新能力支持普通容器与高性能容器。
有服务器的游戏,优先建议自己实现存档能力,将存档数据保存在自己的服务器上。请确保支持玩家换手机后仍然有存档,且不允许将用户的存档数据存放在本地。
注意:
1)接入旧版云存档能力的游戏无需强制升级,旧版能力在普通容器仍然能够使用。
2)本文档为已接入过旧版云存档能力、希望升级为新版云存档能力的存量游戏提供接入指引,涉及能力升级、数据迁移。如为全新游戏接入,请参考云存档(新游戏)
云存档大小限制
每个openid所标识的微信用户在每个游戏上托管的数据不能超过128个key-value对。
上报的key-value列表当中每一项的key+value长度都不能超过1K(1024)字节。
上报的key-value列表当中每一个key长度都不能超过128字节。
二、技术接入
接入前准备
接入云存档前,请在控制台-小游戏开发-小游戏开发权限中,申请小游戏云存档能力(新)权限包。
确认获得权限包后,可以在小游戏开发-小游戏云存档中,查看存储下来的数据(接入旧版云存档服务的游戏,数据在云服务-云服务套件-小程序云存储查看)。
类型定义
TBaseParams
基础回调参数类型,用于定义接口请求的回调函数。
export type TBaseParams
complete (): void; // 请求完成后的回调,无论成功或失败
success(data: T): void; // 请求成功时的回调
fail (err: any): void; // 请求失败时的回调 {errorCode: string;errorMessage: string;extra?: any;}
};
KVData
表示键值对数据的类型。
export type KVData = {
key: string; // 键名
value: string | number ; // 对应的值
};
TKVDataList
KVData 类型的数组。
export type TKVDataList = KVData[];
TKeyList
键名的字符串数组。
export type TKeyList = string[];
OldVersionCloud
旧版本云存档对象
export type OldVersionCloud = { cloudObject?: any };
OldVersionSinglePath
需要设置单路径参数
export type OldVersionSinglePath = { path?: string } & OldVersionCloud;
说明:
如果要设置默认路径,可以不设置任何值。
OldVersionMultiPath
需要设置多路径的参数
export type OldVersionMultiPath = { getDefault?: boolean; pathList?: TKeyList } & OldVersionCloud;
说明:
如果需要设置默认路径,getDefault 设置为true,禁止在pathList里面添加default路径。
迁移数据前置条件
迁移数据需要使用最新的老版本云服务的库(@tbmp/mp-cloud-sdk)
{
"@tbmp/mp-cloud-sdk": "1.5.11-alpha.0",
}
使用建议(必看!!)
1)在开发阶段,你可以联系运营来获取你目前游戏目前线上用户存储数据的最大数据,用这些数据来测试是否超过了新版本云存档的限制。
2)如果你之前没有使用多路径,并且线上最大的用户存储数据量级没有超过限制,你可以通过使用setUserCloudStorage接口触发自动迁移。调用其他接口均不会触发自动迁移。
3)如果你使用了多路径,或者线上最大的用户存储数据量级超过了限制,那么必须使用手动迁移相关接口来进行迁移,否则可能出现存档迁移失败的问题。建议是在进入游戏loading阶段进行手动迁移,再进行云存档的后续操作(比如读取数据或者写入数据)。
接口
初始化
async init(params: TInitParams)
参数
export type TInitParams = { env: 'online' | 'test'; };
env:online代表正式环境,test代表测试环境。
获取用户云存储中所有的键名。
async getUserCloudStorageKeys(params: TGetStorageKeys): Promise
参数:
export type TGetStorageKeys = OldVersionSinglePath & TBaseParams<{ keys: TKeyList, isMigrated: boolean, isMigrateApp: boolean }>;
说明:
1)如果你之前使用过旧版本云存档小游戏,且处于没有迁移的状态。那么通过此接口你获得到的keys是旧版本的数据,如果你已经迁移过了,那么你获得的keys是新版本的数据。可以通过success回调函数的结果isMigrated代表是否已经迁移过,isMigrateApp代表此小游戏是否需要迁移。
2)通过此接口只能设置旧版本的一个路径,如果要获得旧版本全路径的数据,请使用getExistAllStorage接口。
错误码:
错误码
说明
SDK_PARAMS_ERROR
params未设置
获取用户云存储中的键值对列表
async getUserCloudStorage(params: TGetStorageParams): Promise
参数:
export type TGetStorageParams = OldVersionSinglePath & { keyList: TKeyList } & TBaseParams<{ KVDataList: TKVDataList, isMigrated: boolean, isMigrateApp: boolean}>;
说明:
1)如果你之前使用过旧版本云存档小游戏,且处于没有迁移的状态。那么通过此接口你获得到的数据是旧版本的数据,如果你已经迁移过了,那么你获得的数据是新版本的数据。可以通过success回调函数的结果isMigrated代表是否已经迁移过,isMigrateApp代表此小游戏是否需要迁移。
2)通过此接口只能设置旧版本的一个路径,如果要获得旧版本全路径的数据,请使用getExistAllStorage接口。
错误码:
错误码
说明
SDK_PARAMS_ERROR
params未设置
设置用户云存储中的键值对列表。
async setUserCloudStorage(params: TSetStorageParams): Promise
参数:
export type TSetStorageParams = OldVersionSinglePath & { KVDataList: TKVDataList } & TBaseParams;
说明:
1)如果你之前使用过旧版本云存档小游戏,且处于没有迁移的状态。那么通过调用接口设置数据,会自动将配置的路径的数的全部数据读取出来,然后跟KVDataList的数据进行合并,最后进行保存。如果KVDataList存在跟老版本数据相同key的情况,会将新数据覆盖老数据。
2)通过此接口只能设置旧版本的一个路径,如果要设置旧版本全路径的数据,请使用transferUserCloudStorage街口进行迁移。
3)同一个用户同一个游戏中,同时进行set操作会抛出抢锁失败异常,一般重试即可解决。对于抢锁失败异常,游戏必须进行异常处理,避免出现丢档情况。
建议:
a. 当同一个用户对同一个游戏发起多个set接口请求时,在上一次接口返回之后再发起新请求
b. 为保证系统性能、优化用户体验并节约服务器资源,所有客户端状态上报(set请求)请遵循“关键状态驱动”原则。不建议基于固定时间间隔(Polling)或无差别事件触发的冗余上报。
错误码:
错误码
说明
SDK_PARAMS_ERROR
params未设置
需要cloudObject未设置,游戏未迁移,需要设置cloudObject才能获取数据
从用户云存储中移除指定的键
async removeUserCloudStorage(params: TRemoveStorageParams): Promise
参数:
请求参数包括一个键列表和基础回调参数。
export type TRemoveStorageParams = { keyList: TKeyList } & TBaseParams;
错误码:
错误码
说明
SDK_PARAMS_ERROR
params未设置
keyList为空或者长度为0
SDK_NEED_MIGRATE_ERROR
当前处于未迁移状态,禁止删除数据
说明:
如果你之前使用过旧版本云存档小游戏,且处于没有迁移的状态,那么调用此接口会提示SDK_NEED_MIGRATE_ERROR错误。建议在删除数据之前通过设置数据或者手动迁移相关的接口进行数据迁移。
手动迁移相关(可选):获取所有当前环境下所有路径的数据
async getExistAllStorage(params: TGetExistAllStorageParams): Promise
参数:
export type TGetExistAllStorageParams = OldVersionMultiPath & TBaseParams<{ KVDataList: TKVDataList }>;
错误码
说明
SDK_PARAMS_ERROR
params未设置
pathList不能包含default,请使用isDefault进行设置
说明:
此接口的入参可以设置多个参数。
手动迁移相关(可选):迁移数据
async migrateUserCloudStorage(params: TMigrateStorageParams): Promise
参数:
export type TMigrateStorageParams = { KVDataList: TKVDataList, setMigrated?: boolean } & TBaseParams ;
说明:
1)如果你的数据量很大,比如超过512kb,接口可能会报错。建议大数据迁移使用分批多次上传。
2)如果使用分批多次上传,那么就需要手动设置setMigrated参数。非最后一轮调用设置设置false,代表未迁移完成。最后一轮设置为true,代表迁移完成。默认值是true,请谨慎设置,如果迁移完成,就不能再次迁移,获取数据的接口也不会去读取老数据。
错误码
说明
SDK_PARAMS_ERROR
params未设置
SDK_MIGRATED_ERROR
云存已迁移或不需要迁移
通用错误码
错误码
说明
SDK_REQUEST_MIGRATED_ERROR
cloud.storage.migrated 返回数据错误
cloud.storage.migrated 返回数据错误,isMigrateApp 或 isMigrated 不是布尔值
SDK_ENV_NOT_SET
请调用init进行环境变量设置。
SDK_READ_STORAGE_ERROR
无法获得老版本云存档所有keys,path:xxx。
无法获得老版本云存档数据,xxx,keys:xxx。
SDK_REQUEST_RESULT_FAILED_ERROR
xxx请求失败。
SDK_PARAMS_ERROR
需要cloudObject未设置,游戏未迁移,需要设置cloudObject才能获取数据
无法获得cloudObject.init,cloudObject可能不是有效的云存档对象
无法获得cloudObject.userCloudStore,cloudObject可能不是有效的云存档对象
示例代码
import cloud from '@tbmp/mp-cloud-sdk'
const sdk = my.tb.getInteractiveSDK();
const newCloud = sdk.getCloudStorageController();
newCloud.init({ env: 'online' });
let userCloudStore;
try {
cloud.init({
env: 'online',
});
userCloudStore = cloud.userCloudStore;
} catch (error) {
console.error(error);
}
if (newCloud) {
newCloud.getExistAllStorage({
cloudObject: cloud,
getDefault: true,
pathList: ['login'],
success: (res) => {
console.log('获取全量数据成功', res);
},
fail: (err) => {
console.log('获取全量数据失败', err);
},
complete: (res) => {
console.log('获取全量数据完成', res);
}
});
// 手动迁移数据
newCloud.getExistAllStorage({
cloudObject: cloud,
getDefault: false,
pathList: ['login'],
success: (res) => {
console.log('获取全量数据成功', res);
const KVDataList = res.KVDataList;
const liteList: Array
for (let i = 0; i < KVDataList.length; i++) {
const item = KVDataList[i];
// players 数据太大了,进行过滤
item.key != 'players' && liteList.push({
key: item.key,
value: item.value
});
}
newCloud.migrateUserCloudStorage({
KVDataList: liteList,
success: (res) => {
console.log('迁移数据成功', res);
},
fail: (err) => {
console.log('迁移数据失败', err);
},
complete: (res) => {
console.log('迁移数据完成', res);
}
});
},
fail: (err) => {
console.log('获取全量数据失败', err);
},
complete: (res) => {
console.log('获取全量数据完成', res);
}
});
newCloud.getUserCloudStorage({
cloudObject: cloud,
path: 'login',
keyList: ['name', 'age', 'desc', 'cnTime'],
success: (res) => {
console.log('云存档 获取数据成功', res);
},
fail: (err) => {
console.log('云存档 获取数据失败', err);
},
complete: (res) => {
console.log('云存档 获取数据完成', res);
}
})
newCloud.setUserCloudStorage({
cloudObject: cloud,
path: 'login',
KVDataList: [
{
key: 'cnTime',
value: 'test',
}
],
success: (res) => {
console.log('云存档 设置数据成功', res);
},
fail: (err) => {
console.log('云存档 设置数据失败', err);
},
complete: (res) => {
console.log('云存档 设置数据完成', res);
}
});
newCloud.getUserCloudStorageKeys({
cloudObject: cloud,
path: 'login',
success: (res) => {
console.log('云存档 获取全部key成功', res);
},
fail: (err) => {
console.log('云存档 获取全部key失败', err);
},
complete: (res) => {
console.log('云存档 获取全部key完成', res);
}
});
newCloud.removeUserCloudStorage({
keyList: ['cnTime'],
success: (res) => {
console.log('云存档 删除数据成功', res);
},
fail: (err) => {
console.log('云存档 删除数据失败', err);
},
complete: (res) => {
console.log('云存档 删除数据完成', res);
}
});
// setUserCloudStorage 接口重试参考代码
const MAX_RETRIES = 3; // 最大重试次数
const INITIAL_DELAY = 1000; // 初始重试延迟(毫秒)
const setCloudStorageWithRetry = (retryCount = 0) => {
newCloud.setUserCloudStorage({
KVDataList: [{ key: 'cnTime', value: 'test' }],
success: (res) => {
console.log('云存档 设置数据成功', res);
},
fail: (err) => {
if (retryCount < MAX_RETRIES) {
const delay = INITIAL_DELAY * Math.pow(2, retryCount);
console.warn(`云存档 设置失败,${delay}ms后第${retryCount + 1}次重试...`, err);
setTimeout(() => {
setCloudStorageWithRetry(retryCount + 1);
}, delay);
} else {
console.error('云存档 设置数据失败,已达最大重试次数', err);
}
},
complete: (res) => {
console.log('云存档 设置数据完成', res);
}
});
};
setCloudStorageWithRetry();
}