通常來講,一款比較流行的鬥地主遊戲主要功能以下:node
實現了最經典的鬥地主玩法,叫地主搶地主等功能。
支持隨機匹配模式,主動建立房間邀請微信好友模式兩種。ios
這款《鬥地主》遊戲分爲客戶端和服務端兩個部分,client 是使用 Egret 實現的,server 是使用 matchvs gameServer nodejs版實現。項目目錄結構以下:axios
┌-client Egret實現的客戶端代碼
├-gs-server Matchvs gameServer nodejs 實現的服務端代碼
├-matchvs Matchvs客戶端SDK
├-wxshare 封裝的微信小遊戲接口
└-README安全
Poker 排行榜實現方法以下:微信
client 上報分數到 gameServer,gameServer 再將分數上報給 Matchvs Rank System,Rank System 是經過gameID 和 userID 來計算用戶排行數據,並不關心用戶的 nickname 、avatar 等信息。 因此須要將 user information 另外的使用Matchvs Store System 存儲系統保存。框架
接入排行榜以前,能夠先看看 Matchvs Rank System API Doc 描述了全部可以使用的排行接口。gs-server 中主要使用到了 建立排行榜、上報分數、查詢指定用戶排行信息接口。函數
gs-server 使用的 axios 框架作 http 請求。在代碼中有作好了排行榜接口的封裝工做。在 gs-server/src 目錄下可看到以下兩個文件:post
gs-server/src/HttpRequest.js:對 axios 接口再次封裝,使用 get、post、put、delete 等請求函數明確了Restful 接口請求方式。由於 Matchvs Rank API 嚴格按照 Restful 模式設置的。
gs-server/src/ReportDataNew.js:排行榜數據上報接口,是對Matchvs Rank API 請求接口的封裝, 把http 請求和 sign 簽名都實現好了,只須要傳入相應的參數便可。
排行榜接口 host 和 path
接口地址和對應請求 path 定義在 ReportDataNew.js 中,這個列出部分代碼:this
const rank_host = (GameData.Conf.DATA_STORAGE_ENV == 1 ? GameData.HttpApi.RELEASE_HOST : GameData.HttpApi.ALPHA_HOST); // 排行榜接口地址
const rank_config = "/rank/ranking_list_configs?"; // 排行榜配置
const rank_score = "/rank/scores?"; // 上傳排行榜分數
const rank_snapshot = "/rank/snapshot?"; // 建立排行榜快照
const rank_grades = "/rank/grades?"; // 查詢用戶排行
const rank_list = "/rank/ranking_list?"; // 排版列表
const rank_delete = "/rank/ranking_list_configs?"; // 刪除排行url
在 ReportDataNew.js 能夠看到 CreatorRankConfig 函數,這個函數是請求 Matchvs Rank System 建立一個排行榜。該排行榜參數由用戶設置,設置參數可參考 this.rankconfig 變量值:
class ReportDataNew{
...... constructor(){ this.rankconfig = { gameID: this.gameID, rankinglistName: "totlal_rank", rankGist: "score", sortOrder: 0, updatePeriodType: 3, customStartTime: 0, customPeriod: 0, rankNum: 100, historyPeriodNum: 0, updateRuleType: 2, sign: "xxx", userID: 0, }; } /** * 建立排行榜 * @param {Function} callback 回調函數 */ CreatorRankConfig(callback){ this.rankconfig.sign = this.SignParse(this.rankconfig); http.post(httpReq.url_Join(rank_host, rank_config), this.rankconfig, callback); } ......
}
在 gs-server 啓動的時候調用 CreatorRankConfig 函數建立一個排行榜,若是排行榜已被建立,接口會返回錯誤提示(排行榜已存在),咱們不用關心這個返回值, 以下代碼:
// main.js
let report = new ReportDataNew();
report.CreatorRankConfig();
client 打完一局,再離開房間以前須要 調用 MatchvsSDK sendEventEx 接口上報分數和用戶信息到 gs-server 中。而後gs-server 把用戶信息和分數分別存儲到 Matchvs Store System 和 Matchvs Rank System。在 ReportDataNew.js 文件能夠看到如下幾個函數:
/** * 上傳分數,把玩家分數上報到 Matchvs Rank System * @param {object} args 請求參數 {userID:1,value:0} * @param {Function} callback 回調函數 */ UpdateScores(args, callback){ let data = { userID:args.userID, gameID:this.gameID, sign:"", items:[ {fieldName:this.rankconfig.rankGist, value:args.value} ] }; data.sign = this.SignParse(data); let userid = args.userID; console.log("上報數據參數:", JSON.stringify(data)); http.put(httpReq.url_Join(rank_host, rank_score) , data, callback); } /** * 從 Matchvs Rank System 獲取用戶當前的排行數據 * @param {object} args {userID:,} * @param {Function} callback */ GetUserRank(args, callback){ let grades = { userID: args.userID, gameID: this.gameID, type: 0, // 類型,取值0或者1,0排行榜,1快照 rankName: this.rankconfig.rankinglistName,//排行榜名稱 snapshotName: "", //快照名稱 rank: 0, //範圍 period: 0, //週期,取值0或1,0當前週期,1上一週期 sing: "", //簽名 } grades.sign = this.SignParse(grades); let param = this.paramsParse(grades); http.get(httpReq.url_Join(rank_host, rank_grades) + param, callback); } /** * 保存用戶信息 * @param {number} userID * @param {Array<object>} userInfo [{userID:123, name:'', avatar:''}] * @param {Function} callback */ RecordUserListInfo(userID , InfoList , callback){ let listInfo =[]; InfoList.forEach(user=>{ listInfo.push({ key: user.userID, value: this.base64Encode(JSON.stringify({ name: user.name, avatar: user.avatar })), }); }); let data = { gameID : this.gameID, userID : userID, dataList : listInfo, sign : "" } data.sign = this.SignParse(data); let param = this.paramsParse(data); http.get(httpReq.url_Join(rank_host, GameData.HttpApi.SET_GAMEDATA) + param, callback); }
在 Room.js 函數 roomEvent 收到玩家上報分數指令,而後調用 Player.js 中的 reportGameScoreNew ,分別數處理相關的數據。
//Room.js
/** * 收到上報分數的消息調用上報分數模塊接口 * @param {number} userID 上報的玩家ID * @param {number} dt 上報的數據 */ reportPlayerScore(userID, dt){ //房間上報數據狀態 this.roomState |= ROOMSTATE.GAME_REPORT; let player = this.players.get(userID); let event = { action: GameData.RSP_EVENT.REPROT_RESULT, data:{ userID:userID, status:1, rank:0, totleScore:0, } }; let self = this; if(player){ log.debug("userID:"+userID+" data:",dt); // 這裏調用 Player.js 分別處理上報數據 player.reportGameScoreNew(dt, function(res, err){ if(res !== null){ log.info("上報成功:", res); event.data.rank = res.data.rank; event.data.totleScore = res.data.value; event.data.status = 0; self.reInitRoom(); self.sendEvent(event); }else{ log.error("report data error ", JSON.stringify(err)); self.sendEvent(event); } }); }else{ log.error("This userID is invaild"); self.sendEvent(event); } }
// Player.js
/** * 上報分數新接口,不用在 gameServer 本身排行,藉助獨立的排行榜系統排序。 * @param {*} data 分數 {times:1,model:1,value:19} * @param {*} _callback 結果回調函數 (res, err)=>{} */ reportGameScoreNew(data, _callback){ let score = data.value; if ("avator" in data) { this.avator = data.avator; } if ("name" in data) { this.nickName = data.name + ""; } let report_new = new ReportDataNew(); //先 上報數據到 Matchvs Rank System report_new.UpdateScores({userID:this.userID, value: score}, (res, err)=>{ if (err) { _callback(null, err); return; } // 上報分數成功後,上傳用戶暱稱和頭像信息 report_new.RecordUserListInfo(this.userID, [ { userID: this.userID, name: this.nickName || "", avatar: this.avator || "" }, ], (res, err) =>{ if (err){ log.error("用戶信息上傳失敗:", err); } }); //獲取用戶當前排行數據,返回給我客戶端 let grades = { userID: this.userID } report_new.GetUserRank(grades, (res, err) => { if(err){ _callback(null, err); return; } _callback(res, null); }); }); }
gs-server 上報的數據,會根據建立排行榜設置的信息對數據進行排行,能夠在客戶端獲取排行榜數據。由於在排行榜系統中只能獲取到 userID 列表,須要在客戶端展現用戶的頭像,暱稱等信息須要到存儲系統中獲取,存儲系統中的用戶信息是在 gs-server 上報的。
獲取排行榜列表數據咱們能夠看 client/src/matchvs/MvsHttpApi.ts 中的 GetRankListData 接口
public GetRankListData(callback){
let params = { userID: GlobalData.myUser.userID || 0, gameID: MatchvsData.gameID, rankName:"totlal_rank", period:0, top: 50, pageIndex:1, pageMax:10, self:0, sign:"", } params.sign = MvsHttpApi.SignParse(params); let param = MvsHttpApi.paramsParse(params);
this.http_get( MvsHttpApi.url_Join(MvsHttpApi.open_host,MvsHttpApi.rank_list ) + param,call back);
}
在 RankList.js 中調用 GetRankListData 接口獲取排行榜數據,而後取出獲取到的 userID list 去Matchvs Store System 獲取對應的 nickname 和 avatar 。如 scr/scene/RankList.js 中的 RankListRsp 和 client/src/matchvs/MvsHttpApi.ts 中的 GetUserInfoList
// scr/scene/RankList.js
/** * 獲取排行榜列表回調 */ public RankListRsp(res, err){ console.log("請求的數據爲:",res); if(res && res.statusCode == 200){ let data:Array<any> = res.data; let userList:Array<any> = []; for(var i= 0; i < data.length; i++){ let obj = { ranking: data[i].rank + "", name: data[i].userID, score: data[i].value , head:"http://alphazwimg.matchvs.com/egret/Three-Poker/img/images2.jpg" }; this.dsListHeros.push(obj); userList.push(data[i].userID); } this.http.GetUserInfoList(userList,this.getUserInfoListRsp.bind(this)); }else{ console.log("請求錯誤:", err); } }
// client/src/matchvs/MvsHttpApi.ts
/** * 獲取保存在全局 http 接口列表的用戶信息 */ public GetUserInfoList(list:Array<any>,callback:Function){ let keyList = []; list.forEach(k=>{ keyList.push({key:k}); }); let data = { gameID : MatchvsData.gameID, userID : GlobalData.myUser.userID || 0, keyList : keyList, sign : "", } data.sign = MvsHttpApi.SignParse(data); let param = MvsHttpApi.paramsParse(data); this.http_get(MvsHttpApi.url_Join(MvsHttpApi.open_host, MvsHttpApi.get_game_data)+param, callback); }
玩家登錄遊戲進入到遊戲首頁,在右上角顯示用戶打完一局後剩下的分數,這個分數須要在 排行榜系統中獲取。在 Main.js 中的 getUserPointValueNew 函數調用 MvsHttpApi.js 的 GetUserRank 函數獲取用戶分數,
// MvsHttpApi.js
public GetUserRank(userID, callback){
let grades = { userID: userID, gameID: MatchvsData.gameID, type: 0, // 類型,取值0或者1,0排行榜,1快照 rankName: "totlal_rank",//排行榜名稱 snapshotName: "", //快照名稱 rank: 0, //範圍 period: 0, //週期,取值0或1,0當前週期,1上一週期 sign: "", //簽名 } grades.sign = MvsHttpApi.SignParse(grades); let param = MvsHttpApi.paramsParse(grades); this.http_get(MvsHttpApi.url_Join(MvsHttpApi.open_host,MvsHttpApi.rank_user) + param, callback); }
在接入排行榜的過程當中,主要就是對 http 接口的調用,開發者只須要關心遊戲數據的上報和 http 接口的請求,不要關心排行是怎麼計算的。整個過程就是對接口的操做,組數據,解析數據等等。咱們這裏例子是在 gs-server 中上報分數到 Matchvs Rank System 中的,固然也能夠在客戶端本身上報分數。可是在 gameServer 中上報分數是相對更安全一些。