1. 程式人生 > >鬥地主遊戲排行榜功能快速開發教程

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

目錄

排行榜實現

接入描述

排行榜介面 host 和 path

建立排行榜

上報排行榜

client 獲取排行榜列表

獲取玩家當前分數

排行榜接入總結

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

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

這款《鬥地主》遊戲分為客戶端和服務端兩個部分,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 中上報分數是相對更安全一些。

遊戲體驗地址: