新的项目需要做一个微信棋牌小游戏,网上搜了一圈相关的代码。
单就完整性来说,幼麟棋牌的开源代码比较完整(cocosCreator客户端、nodeJs服务端、数据库脚本)
实现了一款房卡麻将基本功能(热更新、微信登陆分享、游戏回放),下面贴出分析笔记,希望对后面学习的人多少有些帮助。
只不过最终就整体代码框架方面的一些考虑,我这边放弃了从此套代码上来做二次开发,而是选择了Golang + mongodb的服务端,如果对相关的方面开发有兴趣的朋友也可以添加我为好友一起探讨。
account_server/account_server.js 处理版本获取、账户注册、登陆
app.get('/get_version',function(req,res));//返回版本信息和游戏大厅连接地址
app.get('/guest',function(req,res)); //处理游客登陆
app.get('/wechat_auth',function(req,res)); //处理微信登陆
app.get('/base_info',function(req,res)); //返回用户账户信息 主要用于显示用户头像
-app.get('/wechat_auth',function(req,res); //微信登陆
-create_user(account,nickname,sex,headimgurl,function();
hall_Server/client_service.js 面向客户端http请求
app.get('/login',function(req,res)); //处理登陆
app.get('/create_private_room',function(req,res)); //创建房间
app.get('/enter_private_room',function(req,res)); //进入房间
app.get('/get_history_list',function(req,res)); //得到游戏历史
app.get('/get_games_of_room',function(req,res)); //房间游戏数
app.get('/get_detail_of_game',function(req,res)); //游戏详细信息 战绩回放
app.get('/get_user_status',function(req,res)); //得到用户状态 钻石数量
app.get('/get_message',function(req,res)); //得到通知消息
app.get('/is_server_online',function(req,res)); //判断服务器是否在线
hall_Server/room_service.js
-app.get('/register_gs',function(req,res)); //game server 注册
isServerOnline(); //判断服务器是否在线
chooseServerAndEnter(); //选择负荷最小的服务器进入
createRoom(); //创建房间
-http.get(serverinfo.ip,serverinfo.httpPort,"/create_room");
enterRoom(); //进入房间
-http.get(serverinfo.ip,serverinfo.httpPort,"/enter_room");
majiang_server/app.js 读取配置信息 启动相关服务
//开启HTTP服务
http_service.start(config);
//开启外网SOCKET服务
socket_service.start(config);
majiang_server/http_service.js
app.get('/get_server_info',function(req,res)); //得到服务器中所有的房间和用户
-app.get('/create_room',function(req,res)); //处理从大厅发来的创建房间请求
app.get('/enter_room',function(req,res)); //处理从大厅发来的加入房间请求
app.get('/ping',function(req,res)); //大厅服务端判断游戏房间服务端是否在线
app.get('/is_room_runing',function(req,res)); //大厅服务端判断游戏房间服务端是否在线
//心跳 向大厅服务器注册游戏服务器
//如果已经注册则更新 当前服务器的负载信息(房间数量) 以方便客户选择最优房间
function update();
http.get(config.HALL_IP,config.HALL_PORT,"/register_gs",gameServerInfo);
majiang_server/socket_service.js 监听从客户端发来的socket 事件
socket.on('login',function(data));
socket.on('ready',function(data));
socket.on('huanpai',function(data));
socket.on('dingque',function(data));
socket.on('chupai',function(data));
socket.on('peng',function(data));
socket.on('gang',function(data));
socket.on('hu',function(data));
socket.on('guo',function(data));
socket.on('chat',function(data));
socket.on('quick_chat',function(data));
socket.on('voice_msg',function(data));
socket.on('emoji',function(data));
socket.on('exit',function(data));
socket.on('dispress',function(data));
socket.on('dissolve_request',function(data));
socket.on('dissolve_agree',function(data));
socket.on('dissolve_reject',function(data));
socket.on('disconnect',function(data));
socket.on('game_ping',function(data));
majiang_server/ roommgr.js 实现游戏对房间相关的操作
function generateRoomId(); //随机生成6位数字ID
functionconstructRoomFromDb(dbdata); //从数据库中获取房间信息数据 重建房间
exports.enterRoom =function(roomId,userId,userName,callback);
exports.createRoom =function(creator,roomConf,gems,ip,port,callback);
exports.exitRoom =function(userId);
exports.destroy =function(roomId);
exports.getTotalRooms =function();
exports.getRoom =function(roomId);
exports.isCreator =function(roomId,userId);
exports.setReady =function(userId,value);
exports.isReady =function(userId);
exports.getUserRoom =function(userId);
exports.getUserSeat =function(userId);
exports.getUserLocations =function();
majiang_server/ gamemgr_xzdd.js //麻将游戏大体流程实现
function getMJType(id));
function shuffle(game) );
functionmopai(game,seatIndex) );
//检查是否可以碰
functioncheckCanPeng(game,seatData,targetPai) );
//检查是否可以点杠
function checkCanDianGang(game,seatData,targetPai));
//检查是否可以暗杠
functioncheckCanAnGang(game,seatData));
//检查是否可以弯杠(自己摸起来的时候)
functioncheckCanWanGang(game,seatData));
functioncheckCanHu(game,seatData,targetPai) );
functionclearAllOptions(game,seatData));
//检查听牌
functioncheckCanTingPai(game,seatData));
functiongetSeatIndex(userId));
functiongetGameByUserID(userId));
functionhasOperations(seatData));
functionsendOperations(game,seatData,pai) );
functionmoveToNextUser(game,nextSeat));
function doUserMoPai(game));
functionisSameType(type,arr));
functionisQingYiSe(gameSeatData));
functionisMenQing(gameSeatData));
functionisZhongZhang(gameSeatData));
functionisJiangDui(gameSeatData));
functionisTinged(seatData));
functioncomputeFanScore(game,fan));
functionfindMaxFanTingPai(ts));
functionfindUnTingedPlayers(game));
functioncalculateResult(game,roomInfo));
functiondoGameOver(game,userId,forceEnd));
functionrecordUserAction(game,seatData,type,target));
exports.begin =function(roomId) ); //开始新的一局
exports.chuPai =function(userId,pai));
exports.peng =function(userId));
exports.isPlaying =function(userId));
functiondoGang(game,turnSeat,seatData,gangtype,numOfCnt,pai));
exports.gang =function(userId,pai));
exports.hu =function(userId));
exports.guo =function(userId));
exports.doDissolve =function(roomId));
majiang_server/ mjutils.js //麻将相关辅助函数
functioncheckTingPai(seatData,begin,end);
functionmatchSingle(seatData,selected);
functioncheckSingle(seatData);
functioncheckCanHu(seatData);
exports.getMJType =function(pai);
majiang_server/usermgr.js //绑定用户和socket、发送消息
exports.bind =function(userId,socket);
exports.del =function(userId,socket);
exports.get =function(userId);
exports.isOnline =function(userId);
exports.getOnlineCount =function();
exports.sendMsg = function(userId,event,msgdata);
-socket.emit(event,msgdata);
exports.kickAllInRoom =function(roomId);
exports.broacastInRoom =function(event,data,sender,includingSender);
-socket.emit(event,msgdata);
majiang_server/tokenmgr.js //Token相关操作 token的设计用于校验进入房间过程中是否超时
-exports.createToken =function(userId,lifeTime); //获取到游戏房间信息后创建token
exports.getToken =function(userId);
exports.getUserID =function(token);
exports.isTokenValid =function(token); //连接游戏服务器的时候校验是否有效和超时
exports.delToken =function(token);
-app.get('/enter_room');
roomMgr.enterRoom(roomId,userId,name,function(ret){
tokenMgr.createToken(userId,5000);
cc.vv.gameNetMgr.connectGameServer(ret); //客户端
var sd = {
token:data.token,
roomid:data.roomid,
time:data.time,
sign:data.sign,
};
cc.vv.net.send("login",sd);
//服务端
socket.on('login',function(data){
//检查token是否有效
if(tokenMgr.isTokenValid(token)==false){
socket.emit('login_result',{errcode:3,errmsg:"tokenout of time."});
return;
}
}
特定流程分析
注册游戏服务器
majiang_server/http_service.js
//初始化本房间服务器地址为空
serverIp =””;
gameServerInfo.load =roomMgr.getTotalRooms();
http.get(HALL_IP, HALL_PORT,"/register_gs",gameServerInfo,function(ret,data){
//注册服务器成功后 设置房间连接IP为本服务器IP
serverIp = data.ip;
}
hall_server/room_service.js
app.get('/register_gs',function(req,res){
hall_server/room_service.js
app.get('/register_gs',function(req,res){
//服务器已经注册
if(serverMap[id]){
//更新服务器负荷信息
info.load= load;
http.send(res,0,"ok",{ip:ip});
return;
}
//将服务器信息保存到服务器数组
serverMap[id]= {
ip:ip,
id:id,
clientip:clientip,
clientport:clientport,
httpPort:httpPort,
load:load
};
}
进入房间 服务器选择
function chooseServer(){
var serverinfo = null;
//在服务器列表中选取 房间数最少的一个
for(var s in serverMap){
var info = serverMap[s];
if(serverinfo == null){
serverinfo = info;
}
else{
if(serverinfo.load >info.load){
serverinfo = info;
}
}
}
return serverinfo;
}
创建房间、进入房间流程
Hall/client_service.js
创建房间
app.get('/create_private_room');
room_service.createRoom();
-room_service.enterRoom();
加入房间
app.get('/enter_private_room');
-room_service.enterRoom();
Hall/room_service.js
exports.enterRoom =function(userId,name,roomId,fnCallback){
//大厅服务端向游戏服务端提交http请求
http.get(serverinfo.ip,serverinfo.httpPort,"/enter_room");
}
majiang_server/http_service.js
app.get('/enter_room');
-roomMgr.enterRoom();