棋牌鬥地主遊戲排行榜功能快速開發教程

一般來說,一款比較流行的鬥地主遊戲主要功能如下:

實現了最經典的鬥地主玩法,叫地主搶地主等功能。
支持隨機匹配模式,主動創建房間邀請微信好友模式兩種。

這款《鬥地主》遊戲分爲客戶端和服務端兩個部分,client 是使用 Egret 實現的,server 是使用 matchvs gameServer nodejs版實現。項目目錄結構如下:

┌-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 目錄下可看到如下兩個文件:

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 中,這個列出部分代碼:

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?"; // 刪除排行

創建排行榜

在 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);
        });
    });
}

client 獲取排行榜列表

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 中上報分數是相對更安全一些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章