1.当用户登录到socket之后,会获取房间里的其他用户信息,返回给当前登录用户作为login_result,同时会在房间内广播new_user_comes_push消息,告知其他用户自己的相关信息,同时设置用户ready为true,socket.gameMgr.setReady(userId);

最后检查是否又解散消息,有的话推给用户

在游戏管理器setReady的地方不断检查是否有足够的用户可以开始一局游戏,如果有,则调用begin方法

//开始新的一局
exports.begin = function(roomId) {var roomInfo = roomMgr.getRoom(roomId);if(roomInfo == null){return;}var seats = roomInfo.seats;var game = {conf:roomInfo.conf,roomInfo:roomInfo,gameIndex:roomInfo.numOfGames,button:roomInfo.nextButton,mahjongs:new Array(108),currentIndex:0,gameSeats:new Array(4),numOfQue:0,turn:0,chuPai:-1,state:"idle",firstHupai:-1,yipaoduoxiang:-1,fangpaoshumu:-1,actionList:[],chupaiCnt:0,};roomInfo.numOfGames++;for(var i = 0; i < 4; ++i){var data = game.gameSeats[i] = {};data.game = game;data.seatIndex = i;data.userId = seats[i].userId;//持有的牌data.holds = [];//打出的牌data.folds = [];//暗杠的牌data.angangs = [];//点杠的牌data.diangangs = [];//弯杠的牌data.wangangs = [];//碰了的牌data.pengs = [];//缺一门data.que = -1;//换三张的牌data.huanpais = null;//玩家手上的牌的数目,用于快速判定碰杠data.countMap = {};//玩家听牌,用于快速判定胡了的番数data.tingMap = {};data.pattern = "";//是否可以杠data.canGang = false;//用于记录玩家可以杠的牌data.gangPai = [];//是否可以碰data.canPeng = false;//是否可以胡data.canHu = false;//是否可以出牌data.canChuPai = false;//如果guoHuFan >=0 表示处于过胡状态,//如果过胡状态,那么只能胡大于过胡番数的牌data.guoHuFan = -1;//是否胡了data.hued = false;//data.actions = [];//是否是自摸data.iszimo = false;data.isGangHu = false;data.fan = 0;data.score = 0;data.huInfo = [];data.lastFangGangSeat = -1;//统计信息data.numZiMo = 0;data.numJiePao = 0;data.numDianPao = 0;data.numAnGang = 0;data.numMingGang = 0;data.numChaJiao = 0;gameSeatsOfUsers[data.userId] = data;}games[roomId] = game;//洗牌shuffle(game);//发牌deal(game);var numOfMJ = game.mahjongs.length - game.currentIndex;var huansanzhang = roomInfo.conf.hsz;for(var i = 0; i < seats.length; ++i){//开局时,通知前端必要的数据var s = seats[i];//通知玩家手牌userMgr.sendMsg(s.userId,'game_holds_push',game.gameSeats[i].holds);//通知还剩多少张牌userMgr.sendMsg(s.userId,'mj_count_push',numOfMJ);//通知还剩多少局userMgr.sendMsg(s.userId,'game_num_push',roomInfo.numOfGames);//通知游戏开始userMgr.sendMsg(s.userId,'game_begin_push',game.button);if(huansanzhang == true){game.state = "huanpai";//通知准备换牌userMgr.sendMsg(s.userId,'game_huanpai_push');}else{game.state = "dingque";//通知准备定缺userMgr.sendMsg(s.userId,'game_dingque_push');}}
};

在这个方法里新建了一个游戏对象,然后把游戏对象存入全局游戏数组中,最后洗牌,发牌

系统发完牌后,如果房间里有换三张的规则,显示换牌界面,如果选定了要换的牌,则向服务器发送换牌信息

cc.vv.net.send("huanpai",data);

服务器收到换牌信息后

exports.huanSanZhang = function(userId,p1,p2,p3){var seatData = gameSeatsOfUsers[userId];if(seatData == null){console.log("can't find user game data.");return;}var game = seatData.game;if(game.state != "huanpai"){console.log("can't recv huansanzhang when game.state == " + game.state);return;}if(seatData.huanpais != null){console.log("player has done this action.");return;}if(seatData.countMap[p1] == null || seatData.countMap[p1] == 0){return;}seatData.countMap[p1]--;if(seatData.countMap[p2] == null || seatData.countMap[p2] == 0){seatData.countMap[p1]++;return;}seatData.countMap[p2]--;if(seatData.countMap[p3] == null || seatData.countMap[p3] == 0){seatData.countMap[p1]++;seatData.countMap[p2]++;return;}seatData.countMap[p1]++;seatData.countMap[p2]++;seatData.huanpais = [p1,p2,p3];for(var i = 0; i < seatData.huanpais.length; ++i){var p = seatData.huanpais[i];var idx = seatData.holds.indexOf(p);seatData.holds.splice(idx,1);seatData.countMap[p] --;}userMgr.sendMsg(seatData.userId,'game_holds_push',seatData.holds);for(var i = 0; i < game.gameSeats.length; ++i){var sd = game.gameSeats[i];if(sd == seatData){var rd = {si:seatData.userId,huanpais:seatData.huanpais};userMgr.sendMsg(sd.userId,'huanpai_notify',rd);            }else{var rd = {si:seatData.userId,huanpais:[]};userMgr.sendMsg(sd.userId,'huanpai_notify',rd);}}//如果还有未换牌的玩家,则继承等待for(var i = 0; i < game.gameSeats.length; ++i){if(game.gameSeats[i].huanpais == null){return;}}//换牌函数var fn = function(s1,huanjin){for(var i = 0; i < huanjin.length; ++i){var p = huanjin[i];s1.holds.push(p);if(s1.countMap[p] == null){s1.countMap[p] = 0;    }s1.countMap[p] ++;}}//开始换牌var f = Math.random();var s = game.gameSeats;var huanpaiMethod = 0;//对家换牌if(f < 0.33){fn(s[0],s[2].huanpais);fn(s[1],s[3].huanpais);fn(s[2],s[0].huanpais);fn(s[3],s[1].huanpais);huanpaiMethod = 0;}//换下家的牌else if(f < 0.66){fn(s[0],s[1].huanpais);fn(s[1],s[2].huanpais);fn(s[2],s[3].huanpais);fn(s[3],s[0].huanpais);huanpaiMethod = 1;}//换上家的牌else{fn(s[0],s[3].huanpais);fn(s[1],s[0].huanpais);fn(s[2],s[1].huanpais);fn(s[3],s[2].huanpais);huanpaiMethod = 2;}var rd = {method:huanpaiMethod,}game.huanpaiMethod = huanpaiMethod;    game.state = "dingque";for(var i = 0; i < s.length; ++i){var userId = s[i].userId;userMgr.sendMsg(userId,'game_huanpai_over_push',rd);userMgr.sendMsg(userId,'game_holds_push',s[i].holds);//通知准备定缺userMgr.sendMsg(userId,'game_dingque_push');}
};

如果四个玩家都换完拍了,在服务端给玩家把牌交换,然后进入定缺阶段,同时通知玩家手牌变化和状态变化

玩家定缺之后,给服务器发送一个消息

exports.dingQue = function(userId,type){var seatData = gameSeatsOfUsers[userId];if(seatData == null){console.log("can't find user game data.");return;}var game = seatData.game;if(game.state != "dingque"){console.log("can't recv dingQue when game.state == " + game.state);return;}if(seatData.que < 0){game.numOfQue++;    }seatData.que = type;//检查玩家可以做的动作//如果4个人都定缺了,通知庄家出牌if(game.numOfQue == 4){construct_game_base_info(game);var arr = [1,1,1,1];for(var i = 0; i < game.gameSeats.length; ++i){arr[i] = game.gameSeats[i].que;}userMgr.broacastInRoom('game_dingque_finish_push',arr,seatData.userId,true);userMgr.broacastInRoom('game_playing_push',null,seatData.userId,true);//进行听牌检查for(var i = 0; i < game.gameSeats.length; ++i){var duoyu = -1;var gs = game.gameSeats[i];if(gs.holds.length == 14){duoyu = gs.holds.pop();gs.countMap[duoyu] -= 1;}checkCanTingPai(game,gs);if(duoyu >= 0){gs.holds.push(duoyu);gs.countMap[duoyu] ++;}}var turnSeat = game.gameSeats[game.turn];game.state = "playing";//通知玩家出牌方turnSeat.canChuPai = true;userMgr.broacastInRoom('game_chupai_push',turnSeat.userId,turnSeat.userId,true);//检查是否可以暗杠或者胡//直杠checkCanAnGang(game,turnSeat);//检查胡 用最后一张来检查checkCanHu(game,turnSeat,turnSeat.holds[turnSeat.holds.length - 1]);//通知前端sendOperations(game,turnSeat,game.chuPai);}else{userMgr.broacastInRoom('game_dingque_notify_push',seatData.userId,seatData.userId,true);}
};

如果某一个玩家定缺了,给客户端推送定缺的提示,直到所有的都定缺,则通知庄家开始出牌

定缺完会有一个game_dingque_finish的通知,用来在玩家右上角显示缺哪门牌

最后给玩家推送game_chupai_push,客户端收到通知后

        cc.vv.net.addHandler("game_chupai_push",function(data){console.log('game_chupai_push');//console.log(data);var turnUserID = data;var si = self.getSeatIndexByID(turnUserID);self.doTurnChange(si);});doTurnChange:function(si){var data = {last:this.turn,turn:si,}this.turn = si;this.dispatchEvent('game_chupai',data);},

轮到玩家出牌的时候,玩家点击两次麻将出牌,服务端进行出牌操作

exports.chuPai = function(userId,pai){pai = Number.parseInt(pai);var seatData = gameSeatsOfUsers[userId];if(seatData == null){console.log("can't find user game data.");return;}var game = seatData.game;var seatIndex = seatData.seatIndex;//如果不该他出,则忽略if(game.turn != seatData.seatIndex){console.log("not your turn.");return;}if(seatData.canChuPai == false){console.log('no need chupai.');return;}if(hasOperations(seatData)){console.log('plz guo before you chupai.');return;}//如果是胡了的人,则只能打最后一张牌if(seatData.hued){if(seatData.holds[seatData.holds.length - 1] != pai){console.log('only deal last one when hued.');return;}}//从此人牌中扣除var index = seatData.holds.indexOf(pai);if(index == -1){console.log("holds:" + seatData.holds);console.log("can't find mj." + pai);return;}seatData.canChuPai = false;game.chupaiCnt ++;seatData.guoHuFan = -1;seatData.holds.splice(index,1);seatData.countMap[pai] --;game.chuPai = pai;recordGameAction(game,seatData.seatIndex,ACTION_CHUPAI,pai);checkCanTingPai(game,seatData);userMgr.broacastInRoom('game_chupai_notify_push',{userId:seatData.userId,pai:pai},seatData.userId,true);//如果出的牌可以胡,则算过胡if(seatData.tingMap[game.chuPai]){seatData.guoHuFan = seatData.tingMap[game.chuPai].fan;}//检查是否有人要胡,要碰 要杠var hasActions = false;for(var i = 0; i < game.gameSeats.length; ++i){//玩家自己不检查if(game.turn == i){continue;}var ddd = game.gameSeats[i];//未胡牌的才检查杠和碰if(!ddd.hued){checkCanPeng(game,ddd,pai);checkCanDianGang(game,ddd,pai);            }checkCanHu(game,ddd,pai);if(seatData.lastFangGangSeat == -1){if(ddd.canHu && ddd.guoHuFan >= 0 && ddd.tingMap[pai].fan <= ddd.guoHuFan){console.log("ddd.guoHuFan:" + ddd.guoHuFan);ddd.canHu = false;userMgr.sendMsg(ddd.userId,'guohu_push');            }     }if(hasOperations(ddd)){sendOperations(game,ddd,game.chuPai);hasActions = true;    }}//如果没有人有操作,则向下一家发牌,并通知他出牌if(!hasActions){setTimeout(function(){userMgr.broacastInRoom('guo_notify_push',{userId:seatData.userId,pai:game.chuPai},seatData.userId,true);seatData.folds.push(game.chuPai);game.chuPai = -1;moveToNextUser(game);doUserMoPai(game);            },500);}
};

最后没操作的话给下一个玩家发牌,doUserMoPai就是给玩家发牌

function doUserMoPai(game){game.chuPai = -1;var turnSeat = game.gameSeats[game.turn];turnSeat.lastFangGangSeat = -1;turnSeat.guoHuFan = -1;var pai = mopai(game,game.turn);//牌摸完了,结束if(pai == -1){doGameOver(game,turnSeat.userId);return;}else{var numOfMJ = game.mahjongs.length - game.currentIndex;userMgr.broacastInRoom('mj_count_push',numOfMJ,turnSeat.userId,true);}recordGameAction(game,game.turn,ACTION_MOPAI,pai);//通知前端新摸的牌userMgr.sendMsg(turnSeat.userId,'game_mopai_push',pai);//检查是否可以暗杠或者胡//检查胡,直杠,弯杠if(!turnSeat.hued){checkCanAnGang(game,turnSeat);    }//如果未胡牌,或者摸起来的牌可以杠,才检查弯杠if(!turnSeat.hued || turnSeat.holds[turnSeat.holds.length-1] == pai){checkCanWanGang(game,turnSeat,pai);    }//检查看是否可以和checkCanHu(game,turnSeat,pai);//广播通知玩家出牌方turnSeat.canChuPai = true;userMgr.broacastInRoom('game_chupai_push',turnSeat.userId,turnSeat.userId,true);//通知玩家做对应操作sendOperations(game,turnSeat,game.chuPai);
}

//通知前端新摸的牌
userMgr.sendMsg(turnSeat.userId,'game_mopai_push',pai);

下面是客户端显示摸到牌的代码

        this.node.on('game_mopai',function(data){self.hideChupai();var pai = data.pai;var localIndex = cc.vv.gameNetMgr.getLocalIndex(data.seatIndex);if(localIndex == 0){var index = 13;var sprite = self._myMJArr[index];self.setSpriteFrameByMJID("M_",sprite,pai,index);sprite.node.mjId = pai;                }else if(cc.vv.replayMgr.isReplay()){self.initMopai(data.seatIndex,pai);}});

幼麟棋牌游戏进程分析相关推荐

  1. cocos creator微信棋牌小游戏 幼麟棋牌服务端分析笔记

    新的项目需要做一个微信棋牌小游戏,网上搜了一圈相关的代码. 单就完整性来说,幼麟棋牌的开源代码比较完整(cocosCreator客户端.nodeJs服务端.数据库脚本) 实现了一款房卡麻将基本功能(热 ...

  2. 幼麟棋牌登录流程分析

    1.默认开机进入的是startScene,加载了AppStart脚本,其中有一个非常重要的方法,初始化了游戏内用到的所有管理器,另外还请求了服务器的版本信息(次要) function initMgr( ...

  3. 幼麟棋牌创建房间简短分析

    1.用户创建房间的操作 hall_server->client_service->create_private_room     调用hall_server->room_servic ...

  4. 麒麟子再放大招,据传幼麟棋牌全部版本将开源!

    前段时间有幸被「幼麟棋牌-麒麟子」 邀请加入幼麟游戏研发群,讨论学习游戏开发技术.行业动态等相关内容. 在群中麒麟子透露,出一个重磅消息:"幼麟棋牌全部版本将开源!" 在这里为不了 ...

  5. cocos creator麻将教程系列(九)—— 幼麟棋牌代码讲解

    这篇文章是官方整理的: 技术栈 客户端 引擎:Cocos Creator 2.0.6. 语言:Javasctipt 可用开发平台:MAC,Windows 可发布平台: iOS,Android,Wind ...

  6. 幼麟棋牌技术分享系列:H5棋牌游戏加载速度优化

    转自:http://forum.cocos.com/t/h5/52893 HELLO,大家好. 由于业务繁忙,很久没在论坛发技术相关的贴子了. 今天想和大家一起讨论一下当下最重点的问题.<H5加 ...

  7. 棋牌游戏网站分析——远航游戏中心

    作为行业的分析师,该拿出点东西让市场了解一下环境了!今天先拿远航游戏中心开刀讨论一下. 棋牌游戏的成功代表作品包括联众,QQ游戏,边锋等.其中包含游戏典型有升级.梭哈.斗地主.锄大地.四国军棋等.最近 ...

  8. 开源H5棋牌 cocos creator微信棋牌小游戏 幼麟棋牌客户端分析笔记

    此文紧接上文的服务端分析来进行客户端的分析 LoadingLogic.js  (与初始化场景绑定 初始化各个组件 获取版本信息 加载资源) 1.onload:显示 splash 界面 初始化各个组件 ...

  9. 幼麟棋牌登录socket服务器分析

    登录socket服务器有两个时机,一个是创建完房间没错误后,连接,另一个是用户自行进入房间请求没错后连接 connectGameServer:function(data){this.dissoveDa ...

最新文章

  1. 相机居然能存储气味,未来智能家居会是什么样? | CCF C³
  2. 【LorMe云讲堂】蔡枫:真菌的耐盐机制
  3. MS CRM 2011 用Jscript打开新窗口的几种方法
  4. Ubuntu 11.04解决txt文档中文乱码方法
  5. 聊一聊js中的null、undefined与NaN
  6. HTTP/3 未来可期?
  7. ubuntu 18.04 ip固定
  8. OA办公自动化系统~~~SSM整合开发
  9. LeetCode题库5:最长回文子串——JavaScript解答
  10. Java定义People类
  11. 10款让你心动的 HTML5 CSS3 效果
  12. java代码post接口请求 用 hutool工具类
  13. gpu opencl 向量加_【Embedding】GloVe:大规模语料中快速训练词向量
  14. 【电子签章】HTML格式合同转化成PDF文件
  15. vue在线预览word踩坑日记
  16. 网站/APP统计分析工具及教程
  17. 如何建立自己的【渲染农场】终极指南(1)
  18. ldconfig是个什么东东
  19. 弯头lisp_(最全的)管道材料代号说明
  20. python 批量处理图片

热门文章

  1. Linux下二维码生成工具:QRencode
  2. 一个微博热搜引发的故事
  3. Kiosk模式是什么?win10如何启用Kiosk模式?
  4. C++实现进程通信(管道pipe)
  5. CuraEngine VS2017
  6. 使用音频分析工具audacity分析wave文件
  7. 腾讯地图 周边 poi 搜索及参数配置
  8. egg.js中Class constructor BaseContextClass cannot be invoked without ‘new‘解决方法
  9. Ubuntu18.04下的截图快捷方式
  10. ESP32-C3 学习测试 蓝牙 篇(五、添加 characteristic)