egret-docs-master/extension/matchvs/guide/storage/README.md

537 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 数据存储
Matchvs 给开发者提供了三种存储接口:用户数据存储、全局数据存储、哈希存储。
三种数据存储的特点及对比如下:
- 用户数据存储,存储用户数据,只有用户自己有增、删、改、查自己数据的权限
- 全局数据存储,推荐在 gameServer 里使用,存储游戏全局数据。客户端也可以使用。
- 哈希存储数据操作会校验userID但用户之间可以修改和查看数据。
**注意使用存储接口前须先调用初始化、注册接口获取userIDtoken 。**
## 存储限制
每个游戏通过各种存储接口所存的数据总容量不可以超过5G如果超过服务端会返回对应错误。
## 域名
Matchvs 环境分为测试环境alpha和 正式环境release所以在使用http接口时需要通过域名进行区分。使用正式环境需要先在[官网控制台](http://www.matchvs.com/manage/gameContentList)将您的游戏发布上线。
**alpha环境域名alphavsopen.matchvs.com**
**release环境域名vsopen.matchvs.com**
## 存用户数据
存储接口 **wc5/setUserData.do**
开发者可以通过调用该接口将用户自定义的数据存储至服务器。
```
http://alphavsopen.matchvs.com/wc5/setUserData.do?gameID=200660&userID=21023&dataList=[
{"key":"Johnuser", "value":"Smith"}]&sign=f6c15ebd1957a7616781b20fc150f4aa
```
**注意:** 每个value的长度上限为1M如果长度超过1M会返回“长度超过限制”的错误。存储上限为每个游戏5G如果超过5G会返回对应错误。
可以调用setUserData实现增量存储。为避免特殊字符影响存储前建议开发者最好将字符串解码成二进制再用UrlEndcode编码后存储。
| 参数名 | 说明 |
| -------- | -------------------------- |
| gameID | 游戏ID |
| userID | 用户ID |
| dataList | 自定义存储json数组包括字段的key和value |
| sign | 见下方sign值获取方法-用户 |
返回数据示例如下:
```
{
"data": "success",
"status": 0
}
```
## 取用户数据
获取接口 **wc5/getUserData.do**
开发者可以通过调用该接口获取用户自定义存储的数据。
```
http://alphavsopen.matchvs.com/wc5/getUserData.do?gameID=200660&userID=21023&keyList=[
{"key":"Johnuser"}]&sign=f6c15ebd1957a7616781b20fc150f4aa
```
**注意:** 存储前如果将字符串解码成二进制再用UrlEndcode编码后存储对应的取出时应用UrlDecode进行解码后显示
| 参数名 | 说明 |
| ------- | ------------ |
| gameID | 游戏ID |
| userID | 用户ID |
| keyList | 需要取的数据对应的键列表 |
| sign | 见下方sign值获取方法-用户 |
返回数据示例如下:
```
{
"data": {
"dataList": [
{
"key": "Johnuser",
"value": "Smith"
}
]
},
"status": 0
}
```
## 删用户数据
删除接口 **wc5/delUserData.do**
开发者可以通过调用该接口删除用户自定义存储的数据。
```
http://alphavsopen.matchvs.com/wc5/delUserData.do?gameID=200660&userID=21023&keyList=[
{"key":"Johnuser"}]&sign=f6c15ebd1957a7616781b20fc150f4aa
```
**注意:** 支持一次删除多条数据
| 参数名 | 说明 |
| ------- | ------------- |
| gameID | 游戏ID |
| userID | 用户ID |
| keyList | 需要删除的数据对应的键列表 |
| sign | 见下方sign值获取方法-用户 |
返回数据示例如下:
```
{
"data": "success",
"status": 0
}
```
## 存全局数据
存储接口 **wc5/setGameData.do**
开发者可以通过调用该接口将全局自定义的数据存储至服务器。
```
http://alphavsopen.matchvs.com/wc5/setGameData.do?gameID=200660&userID=21023&dataList=[
{"key":"Johnuser", "value":"Smith"}]&sign=0c2c2df5949f498afd307e8783bb1f3c
```
**注意:** 每个value的长度上限为1M如果长度超过1M会返回“长度超过限制”的错误。存储上限为每个游戏5G如果超过5G会返回对应错误。
可以调用setGameData实现增量存储。为避免特殊字符影响存储前建议开发者最好将字符串解码成二进制再用UrlEndcode编码后存储。
| 参数名 | 说明 |
| -------- | -------------------------- |
| gameID | 游戏ID |
| userID | 用户ID |
| dataList | 自定义存储json数组包括字段的key和value |
| sign | 见下方sign值获取方法-全局 |
返回数据示例如下:
```
{
"data": "success",
"status": 0
}
```
## 取全局数据
获取接口 **wc5/getGameData.do**
开发者可以通过调用该接口获取用户自定义存储的数据。
```
http://alphavsopen.matchvs.com/wc5/getGameData.do?gameID=200660&userID=21023&keyList=[
{"key":"Johnuser"}]&sign=0c2c2df5949f498afd307e8783bb1f3c
```
**注意:** 存储前如果将字符串解码成二进制再用UrlEndcode编码后存储对应的取出时应用UrlDecode进行解码后显示
| 参数名 | 说明 |
| ------- | ------------ |
| gameID | 游戏ID |
| userID | 用户ID |
| keyList | 需要取的数据对应的键列表 |
| sign | 见下方sign值获取方法-全局 |
返回数据示例如下:
```
{
"data": {
"dataList": [
{
"key": "Johnuser",
"value": "Smith"
}
]
},
"status": 0
}
```
## 删全局数据
删除接口 **wc5/delGameData.do**
开发者可以通过调用该接口删除全局自定义存储的数据。
```
http://alphavsopen.matchvs.com/wc5/delGameData.do?gameID=200660&userID=21023&keyList=[
{"key":"Johnuser"}]&sign=0c2c2df5949f498afd307e8783bb1f3c
```
**注意:** 支持一次删除多条数据
| 参数名 | 说明 |
| ------- | ------------- |
| gameID | 游戏ID |
| userID | 用户ID |
| keyList | 需要删除的数据对应的键列表 |
| sign | 见下方sign值获取方法-全局 |
返回数据示例如下:
```
{
"data": "success",
"status": 0
}
```
## 存哈希
存储接口 **wc5/hashSet.do**
开发者可以通过调用该接口将自定义的数据存储至服务器。
```
http://alphavsopen.matchvs.com/wc5/hashSet.do?gameID=102003&userID=21023&key=1&value=a&sign=68c592733f19f6c5ae7e8b7ae8e5002f
```
**注意:** 每个value的长度上限为1M如果长度超过1M会返回“长度超过限制”的错误。存储上限为每个玩家1000条如果超过1000条会返回对应错误。
可以调用hashSet实现增量存储。为避免特殊字符影响存储前建议开发者最好将字符串解码成二进制再用UrlEndcode编码后存储。
| 参数名 | 说明 |
| ------ | ------------ |
| gameID | 游戏ID |
| userID | 用户ID |
| key | 自定义存储字段编号 |
| value | 自定义存储字段的值 |
| sign | 见下方sign值获取方法-哈希 |
返回数据示例如下:
```
{
"code": 0,
"data": "success",
"status": 0
}
```
## 取哈希
取接口:**wc5/hashGet.do**
开发者可以通过调用该接口获取存储在服务器的自定义数据。
```
http://vsopen.matchvs.com/wc5/hashGet.do?gameID=102003&userID=21023&key=1&sign=b0244f7ed1d433975512a8f6c2ba4517
```
**注意** 存储前如果将字符串解码成二进制再用UrlEndcode编码后存储对应的取出时应用UrlDecode进行解码后显示
| 参数名 | 说明 |
| ------ | ------------ |
| gameID | 游戏ID |
| userID | 用户ID |
| key | 自定义存储字段键值 |
| sign | 见下方sign值获取方法-哈希 |
返回数据示例如下:
```
{
"code": 0,
"data": "this is my data",
"status": 0
}
```
## sign值获取方法-用户
1. 按照如下格式拼接出字符串:
```
appKey&gameID=xxx&userID=xxx&token
```
- `appKey`为您在官网配置游戏所得
- `token`通过用户注册请求获取
2. 计算第一步拼接好的字符串的`MD5`值,即为`sign`的值。
## sign值获取方法-全局
1. 按照如下格式拼接出字符串:
```
appkey&gameID=xxx&userID=xxx&appSecret
```
- `appKey和appSecret`为您在官网配置游戏所得
2. 计算第一步拼接好的字符串的`MD5`值,即为`sign`的值。
## sign值获取方法-哈希
1. 按照如下格式拼接出字符串:
```
appKey&param1=value1&param2=value2&param3=value3&token
```
- `appKey`为您在官网配置游戏所得
- `param1、param2、param3`等所有参数,按照数字`0-9`、英文字母`a~z`的顺序排列
有三个参数`gameID`、`userID`、`key`,则按照`appkey&gameID=xxx&key=xxx&userID=xxx&token` 的顺序拼出字符串。
- `token`通过用户注册请求获取
2. 计算第一步拼接好的字符串的`MD5`值,即为`sign`的值。
## 错误码
| 错误码 | 含义 |
| ------ | ---------------- |
| 413 | 存储长度超过限制 |
## 接口代码示例
这里以 [demo](https://github.com/matchvs/demo-Egret/blob/master/MatchvsDemo_Egret/src/matchvs/MvsHttpApi.ts) 的代码为例 ,下面这些是公共用代码:
```typescript
class MvsHttpApi {
//这里定义接口要使用的连接
public open_host:string = MatchvsData.pPlatform == "release"? "https://vsopen.matchvs.com":"https://alphavsopen.matchvs.com";
public get_game_data:string = "/wc5/getGameData.do?";
public set_game_data:string = "/wc5/setGameData.do?";
......
private counter:number = Math.floor(Math.random()*1000);
public token = GlobalData.myUser.token;
public gameID = MatchvsData.gameID;
public userID = GlobalData.myUser.userID;
public appkey = MatchvsData.appKey;
public secret = MatchvsData.secret;
public constructor() {
}
public getCounter(){
return ++this.counter;
}
public getTimeStamp():number{
return Math.floor(Date.now()/1000);
}
/**
* 把参数中的 key, value 转为 key=value&key1=value2&key3=value3 形式
* @param {any} args {key:value[, ...]} 形式
*/
public static paramsParse(args:any){
let str = "";
for(let k in args){
let val = "";
if ( 'object' == (typeof args[k]) ) {
val = JSON.stringify(args[k]);
}else{
val = args[k];
}
if(str == ""){
str = k + "=" + val;
}else{
str = str + "&" + k + "=" + val;
}
}
return str;
}
/**
* 组合 url 防止出现 host + path 出现两个 // 符号
* @param {string} host
* @param {...string} params
*/
public static url_Join(host, ...params) {
let p = "";
params.forEach(a => {
if (typeof a == "object") {
throw 'the parameter can only be string ';
}
if (a.substring(0,1) == '/'){
p = p + a;
}else{
p = p + '/' + a;
}
});
if (host.substring(host.length - 1, host.length) == '/') {
p = host.substring(0, host.length - 1) + p;
} else {
p = host + p;
}
return p;
}
/**
* 指定签名参数签名
*/
public SignPoint(args:any, points:Array<string>){
let tempobj = {}
points.sort();
points.forEach((val)=>{
tempobj[val] = args[val];
});
if(args["seq"]){
tempobj["seq"] = args["seq"];
}
if(args["ts"]){
tempobj["ts"] = args["ts"];
}
let headKey:string = MatchvsData.appKey;
let endKey:string = args.mode == 2? MatchvsData.secret: GlobalData.myUser.token;
let paramStr = MvsHttpApi.paramsParse(tempobj);
let md5Encode = new MD5()
let sign = md5Encode.hex_md5(headKey+"&"+paramStr+"&"+endKey);
return sign;
}
private dohttp(url:string, method:string, params:any, callback:Function){
let headtype = (method == "GET" ? "text/plain" : "application/json") ;
var request = new XMLHttpRequest()
request.open(method, url)
request.setRequestHeader("Content-Type",headtype);
if (method == "GET"){
request.send();
}else{
request.send(JSON.stringify(params));
}
request.onerror = (e)=>{
callback(JSON.parse(request.response), null);
}
request.onreadystatechange = ()=>{
if(request.readyState == 4){
if( request.status == 200 ){
callback(JSON.parse(request.responseText), null);
}else{
callback(null, " http request error "+request.responseText);
}
}
}
}
public http_get(url, callback){
this.dohttp(url, "GET", {}, callback);
}
public http_post(url, params ,callback){
this.dohttp(url, "POST", params, callback);
}
}
```
下面是通过上面的代码实现的接口示例:
```typescript
/**
* 获取全局接口数据
*/
public getGameData(list:Array<any>,callback:Function){
let keyList = [];
list.forEach(k=>{
keyList.push({key:k});
});
let data = {
gameID : "123456",
userID : GlobalData.myUser.userID || 0,
keyList : keyList,
sign : "",
mode : 2,
seq: this.getCounter(),
ts:this.getTimeStamp(),
}
data.sign = this.SignPoint(data, ["gameID", "userID"]);
let param = MvsHttpApi.paramsParse(data);
this.http_get(MvsHttpApi.url_Join(this.open_host, this.get_game_data)+param, callback);
}
/**
* 保存全局数据
*/
public setGameData(userID:number, list:Array<any>, callback:Function){
let listInfo = [];
list.forEach(user=>{
listInfo.push({
key: user.userID,
value: ArrayTools.Base64Encode(JSON.stringify({ name: user.name, avatar: user.avatar })),
});
});
let params = {
gameID : this.gameID,
userID : userID,
dataList: listInfo,
sign : "",
mode:2,
seq: this.getCounter(),
ts:this.getTimeStamp(),
}
params.sign = this.SignPoint(params, ["gameID","userID"]);
this.http_post(MvsHttpApi.url_Join(this.open_host, this.set_game_data), params, callback);
}
```
> 示例代码是Egret斗地主案例完整代码 [可以看这里](https://github.com/matchvs/demo-Egret/blob/master/MatchvsDemo_Egret/src/matchvs/MvsHttpApi.ts)
>
> 注意:** 以上为示例代码为演示接口的调用方法,可能不能直接运行,开发者根据自己需求适当的修改。