连接到游戏服务器
- 在客户端文件
GameNetMgr.js
中,connectGameServer
方法会执行与游戏服务器的连接操作,并且向服务器发送一个 “login” 指令。
var sd = {
token:data.token,
roomid:data.roomid,
time:data.time,
sign:data.sign,
};
2.服务器端的 socket_service.js
文件中,会监听并捕捉到 login
事件。
//启动服务
exports.start = function(conf,mgr){
config = conf;
var httpServer = require('http').createServer(app);
io = require('socket.io')(httpServer);
httpServer.listen(config.CLIENT_PORT);
//监听是否有客户端连接进来,如果有客户连接进来就会调用这个函数
//为这个客户创建一个socket通道
io.sockets.on('connection',function(socket){
//监听一个事件,然后data,客户端发送过来的数据
//数据使用的是json协议的编码,来进行数据交换;底层发送json文本;
socket.on('login',function(data){
//转成json格式数据
data = JSON.parse(data);
if(socket.userId != null){
//已经登陆过的就忽略
return;
}
var token = data.token;
var roomId = data.roomid;
var time = data.time;
var sign = data.sign;
console.log(roomId);
console.log(token);
console.log(time);
console.log(sign);
//检查参数合法性
if(token == null || roomId == null || sign == null || time == null){
console.log(1);
socket.emit('login_result',{errcode:1,errmsg:"invalid parameters"});
return;
}
//检查参数是否被篡改
var md5 = crypto.md5(roomId + token + time + config.ROOM_PRI_KEY);
if(md5 != sign){
console.log(2);
//emit发送一个事件
socket.emit('login_result',{errcode:2,errmsg:"login failed. invalid sign!"});
return;
}
//检查token是否有效
if(tokenMgr.isTokenValid(token)==false){
console.log(3);
socket.emit('login_result',{errcode:3,errmsg:"token out of time."});
return;
}
//检查房间合法性
var userId = tokenMgr.getUserID(token);
var roomId = roomMgr.getUserRoom(userId);
userMgr.bind(userId,socket);
socket.userId = userId;
//返回房间信息
var roomInfo = roomMgr.getRoom(roomId);
var seatIndex = roomMgr.getUserSeat(userId);
roomInfo.seats[seatIndex].ip = socket.handshake.address;
var userData = null;
var seats = [];
for(var i = 0; i < roomInfo.seats.length; ++i){
var rs = roomInfo.seats[i];
var online = false;
if(rs.userId > 0){
online = userMgr.isOnline(rs.userId);
}
seats.push({
userid:rs.userId,
ip:rs.ip,
score:rs.score,
name:rs.name,
online:online,
ready:rs.ready,
seatindex:i
});
if(userId == rs.userId){
userData = seats[i];
}
}
//通知前端
var ret = {
errcode:0,
errmsg:"ok",
data:{
roomid:roomInfo.id,
conf:roomInfo.conf,
numofgames:roomInfo.numOfGames,
seats:seats
}
};
socket.emit('login_result',ret);
//通知其它客户端
userMgr.broacastInRoom('new_user_comes_push',userData,userId);
socket.gameMgr = roomInfo.gameMgr;
//玩家上线,强制设置为TRUE
socket.gameMgr.setReady(userId);
socket.emit('login_finished');
if(roomInfo.dr != null){
var dr = roomInfo.dr;
var ramaingTime = (dr.endTime - Date.now()) / 1000;
var data = {
time:ramaingTime,
states:dr.states
}
userMgr.sendMsg(userId,'dissolve_notice_push',data);
}
});
(1)验证传入参数的有效性,并根据 token
获取 uid
,从而建立 uid
与 socket
之间的映射关系。
userMgr.bind(userId,socket);
socket.userId = userId;
2.服务器端的 socket_service.js
文件中,会监听并处理 login
事件。
var roomId = roomMgr.getUserRoom(userId);
(3)设置当前座位对应的 IP 地址配置。
var roomInfo = roomMgr.getRoom(roomId);
(4)遍历 roomInfo
中的所有座位,将每个座位的信息数据进行整合打包。
if(userId == rs.userId){
userData = seats[i];
}
(5)通过 login_result
事件将结果发送给刚连接的客户端。
//通知前端
var ret = {
errcode:0,
errmsg:"ok",
data:{
roomid:roomInfo.id,
conf:roomInfo.conf,
numofgames:roomInfo.numOfGames,
seats:seats
}
};
socket.emit('login_result',ret);
确保数据保存到全局的GameNetMgr里面
//通知其它客户端
userMgr.broacastInRoom('new_user_comes_push',userData,userId);
(7) 通知玩家当前有人上线,如果玩家人数达到 4 人,则启动新一局游戏。
socket.gameMgr = roomInfo.gameMgr;
//玩家上线,强制设置为TRUE
socket.gameMgr.setReady(userId);
麻将场景客户端
mjgame
场景上绑定了两个脚本文件,分别是MJGame.js
和MahjongMgr.js
注:新框架中新增了 Alert.js
脚本,现在共有 3 个脚本文件。
MJGame.js
的onLoad
函数中:加入了其他功能模块的一些组件。
onLoad: function () {
cc.vv.utils.setFitSreenMode();
this.addComponent("NoticeTip");
this.addComponent("GameOver");
this.addComponent("DingQue");
this.addComponent("PengGangs");
this.addComponent("MJRoom");
this.addComponent("TimePointer");
this.addComponent("GameResult");
this.addComponent("Chat");
this.addComponent("Folds");
this.addComponent("ReplayCtrl");
this.addComponent("PopupMgr");
this.addComponent("HuanSanZhang");
this.addComponent("ReConnect");
this.addComponent("Voice");
this.addComponent("UserInfoShow");
this.addComponent("Status");
this.initView();
this.initEventHandlers();
this.gameRoot.active = false;
this.prepareRoot.active = true;
this.initWanfaLabel();
this.onGameBeign();
cc.vv.audioMgr.playBGM("bgFight.mp3");
cc.vv.utils.addEscEvent(this.node);
},
3.initView
:用于初始化场景,并隐藏多余的元素。
initView:function(){
//搜索需要的子节点
var gameChild = this.node.getChildByName("game");
this._mjcount = gameChild.getChildByName('mjcount').getComponent(cc.Label);
this._mjcount.string = "剩余" + cc.vv.gameNetMgr.numOfMJ + "张";
this._gamecount = gameChild.getChildByName('gamecount').getComponent(cc.Label);
this._gamecount.string = "" + cc.vv.gameNetMgr.numOfGames + "/" + cc.vv.gameNetMgr.maxNumOfGames + "局";
var myselfChild = gameChild.getChildByName("myself");
var myholds = myselfChild.getChildByName("holds");
this._chupaidrag = gameChild.getChildByName('chupaidrag');
this._chupaidrag.active = false;
for(var i = 0; i < myholds.children.length; ++i){
var sprite = myholds.children[i].getComponent(cc.Sprite);
this._myMJArr.push(sprite);
sprite.spriteFrame = null;
this.initDragStuffs(sprite.node);
}
//var realwidth = this.node.width;
//myholds.scaleX *= realwidth/1280;
//myholds.scaleY *= realwidth/1280;
var sides = ["myself","right","up","left"];
for(var i = 0; i < sides.length; ++i){
var side = sides[i];
var sideChild = gameChild.getChildByName(side);
this._hupaiTips.push(sideChild.getChildByName("HuPai"));
this._hupaiLists.push(sideChild.getChildByName("hupailist"));
this._playEfxs.push(sideChild.getChildByName("play_efx").getComponent(cc.Animation));
this._chupaiSprite.push(sideChild.getChildByName("ChuPai").children[0].getComponent(cc.Sprite));
var opt = sideChild.getChildByName("opt");
opt.active = false;
var sprite = opt.getChildByName("pai").getComponent(cc.Sprite);
var data = {
node:opt,
sprite:sprite
};
this._opts.push(data);
}
var opts = gameChild.getChildByName("ops");
this._options = opts;
this.hideOptions();
this.hideChupai();
},
4.监听由网络传递过来的转换事件。
initEventHandlers:function(){
cc.vv.gameNetMgr.dataEventHandler = this.node;
//初始化事件监听器
var self = this;
this.node.on('game_holds',function(data){
self.initMahjongs();
self.checkQueYiMen();
});
this.node.on('game_begin',function(data){
self.onGameBeign();
//第一把开局,要提示
if(cc.vv.gameNetMgr.numOfGames == 1){
self.checkIp();
}
});
this.node.on('check_ip',function(data){
self.checkIp();
});
this.node.on('game_sync',function(data){
self.onGameBeign();
self.checkIp();
});
this.node.on('game_chupai',function(data){
self.hideChupai();
self.checkQueYiMen();
if(data.last != cc.vv.gameNetMgr.seatIndex){
self.initMopai(data.last,null);
}
if(!cc.vv.replayMgr.isReplay() && data.turn != cc.vv.gameNetMgr.seatIndex){
self.initMopai(data.turn,-1);
}
});
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);
}
});
this.node.on('game_action',function(data){
self.showAction(data);
});
this.node.on('hupai',function(data){
//如果不是玩家自己,则将玩家的牌都放倒
var seatIndex = data.seatindex;
var localIndex = cc.vv.gameNetMgr.getLocalIndex(seatIndex);
var hupai = self._hupaiTips[localIndex];
hupai.active = true;
if(localIndex == 0){
self.hideOptions();
}
var seatData = cc.vv.gameNetMgr.seats[seatIndex];
seatData.hued = true;
if(cc.vv.gameNetMgr.conf.type == "xlch"){
hupai.getChildByName("sprHu").active = true;
hupai.getChildByName("sprZimo").active = false;
self.initHupai(localIndex,data.hupai);
if(data.iszimo){
if(seatData.seatindex == cc.vv.gameNetMgr.seatIndex){
seatData.holds.pop();
self.initMahjongs();
}
else{
self.initOtherMahjongs(seatData);
}
}
}
else{
hupai.getChildByName("sprHu").active = !data.iszimo;
hupai.getChildByName("sprZimo").active = data.iszimo;
if(!(data.iszimo && localIndex==0))
{
//if(cc.vv.replayMgr.isReplay() == false && localIndex != 0){
// self.initEmptySprites(seatIndex);
//}
self.initMopai(seatIndex,data.hupai);
}
}
if(cc.vv.replayMgr.isReplay() == true && cc.vv.gameNetMgr.conf.type != "xlch"){
var opt = self._opts[localIndex];
opt.node.active = true;
opt.sprite.spriteFrame = cc.vv.mahjongmgr.getSpriteFrameByMJID("M_",data.hupai);
}
if(data.iszimo){
self.playEfx(localIndex,"play_zimo");
}
else{
self.playEfx(localIndex,"play_hu");
}
cc.vv.audioMgr.playSFX("nv/hu.mp3");
});
this.node.on('mj_count',function(data){
self._mjcount.string = "剩余" + cc.vv.gameNetMgr.numOfMJ + "张";
});
this.node.on('game_num',function(data){
self._gamecount.string = "" + cc.vv.gameNetMgr.numOfGames + "/" + cc.vv.gameNetMgr.maxNumOfGames + "局";
});
this.node.on('game_over',function(data){
self.gameRoot.active = false;
self.prepareRoot.active = true;
});
this.node.on('game_chupai_notify',function(data){
self.hideChupai();
var seatData = data.seatData;
//如果是自己,则刷新手牌
if(seatData.seatindex == cc.vv.gameNetMgr.seatIndex){
self.initMahjongs();
}
else{
self.initOtherMahjongs(seatData);
}
self.showChupai();
var audioUrl = cc.vv.mahjongmgr.getAudioURLByMJID(data.pai);
cc.vv.audioMgr.playSFX(audioUrl);
});
this.node.on('guo_notify',function(data){
self.hideChupai();
self.hideOptions();
var seatData = data;
//如果是自己,则刷新手牌
if(seatData.seatindex == cc.vv.gameNetMgr.seatIndex){
self.initMahjongs();
}
cc.vv.audioMgr.playSFX("give.mp3");
});
this.node.on('guo_result',function(data){
self.hideOptions();
});
this.node.on('game_dingque_finish',function(data){
self.initMahjongs();
});
this.node.on('peng_notify',function(data){
self.hideChupai();
var seatData = data;
if(seatData.seatindex == cc.vv.gameNetMgr.seatIndex){
self.initMahjongs();
}
else{
self.initOtherMahjongs(seatData);
}
var localIndex = self.getLocalIndex(seatData.seatindex);
self.playEfx(localIndex,"play_peng");
cc.vv.audioMgr.playSFX("nv/peng.mp3");
self.hideOptions();
});
this.node.on('gang_notify',function(data){
self.hideChupai();
var seatData = data.seatData;
var gangtype = data.gangtype;
if(seatData.seatindex == cc.vv.gameNetMgr.seatIndex){
self.initMahjongs();
}
else{
self.initOtherMahjongs(seatData);
}
var localIndex = self.getLocalIndex(seatData.seatindex);
if(gangtype == "wangang"){
self.playEfx(localIndex,"play_guafeng");
cc.vv.audioMgr.playSFX("guafeng.mp3");
}
else{
self.playEfx(localIndex,"play_xiayu");
cc.vv.audioMgr.playSFX("rain.mp3");
}
});
this.node.on("hangang_notify",function(data){
var localIndex = self.getLocalIndex(data);
self.playEfx(localIndex,"play_gang");
cc.vv.audioMgr.playSFX("nv/gang.mp3");
self.hideOptions();
});
this.node.on('login_result', function () {
self.gameRoot.active = false;
self.prepareRoot.active = true;
console.log('login_result');
});
},
5.MJRoom.js
:用于处理客户端游戏逻辑的代码组件。
cc.Class({
extends: cc.Component,
properties: {
lblRoomNo:{
default:null,
type:cc.Label
},
// foo: {
// default: null,
// url: cc.Texture2D, // optional, default is typeof default
// serializable: true, // optional, default is true
// visible: true, // optional, default is true
// displayName: 'Foo', // optional
// readonly: false, // optional, default is false
// },
// ...
_seats:[],
_seats2:[],
_timeLabel:null,
_voiceMsgQueue:[],
_lastPlayingSeat:null,
_playingSeat:null,
_lastPlayTime:null,
},
// use this for initialization
onLoad: function () {
if(cc.vv == null){
return;
}
this.initView();
this.initSeats();
this.initEventHandlers();
},
initView:function(){
var prepare = this.node.getChildByName("prepare");
var seats = prepare.getChildByName("seats");
for(var i = 0; i < seats.children.length; ++i){
this._seats.push(seats.children[i].getComponent("Seat"));
}
this.refreshBtns();
this.lblRoomNo = cc.find("Canvas/infobar/Z_room_txt/New Label").getComponent(cc.Label);
this._timeLabel = cc.find("Canvas/infobar/time").getComponent(cc.Label);
this.lblRoomNo.string = cc.vv.gameNetMgr.roomId;
var gameChild = this.node.getChildByName("game");
var sides = ["myself","right","up","left"];
for(var i = 0; i < sides.length; ++i){
var sideNode = gameChild.getChildByName(sides[i]);
var seat = sideNode.getChildByName("seat");
this._seats2.push(seat.getComponent("Seat"));
}
var btnWechat = cc.find("Canvas/prepare/btnWeichat");
if(btnWechat){
cc.vv.utils.addClickEvent(btnWechat,this.node,"MJRoom","onBtnWeichatClicked");
}
var titles = cc.find("Canvas/typeTitle");
for(var i = 0; i < titles.children.length; ++i){
titles.children[i].active = false;
}
if(cc.vv.gameNetMgr.conf){
var type = cc.vv.gameNetMgr.conf.type;
if(type == null || type == ""){
type = "xzdd";
}
titles.getChildByName(type).active = true;
}
},
refreshBtns:function(){
var prepare = this.node.getChildByName("prepare");
var btnExit = prepare.getChildByName("btnExit");
var btnDispress = prepare.getChildByName("btnDissolve");
var btnWeichat = prepare.getChildByName("btnWeichat");
var isIdle = cc.vv.gameNetMgr.numOfGames == 0;
btnExit.active = !cc.vv.gameNetMgr.isOwner() && isIdle;
btnDispress.active = cc.vv.gameNetMgr.isOwner() && isIdle;
btnWeichat.active = isIdle;
},
initEventHandlers:function(){
var self = this;
this.node.on('new_user',function(data){
self.initSingleSeat(data);
});
this.node.on('user_state_changed',function(data){
self.initSingleSeat(data);
});
this.node.on('game_begin',function(data){
self.refreshBtns();
self.initSeats();
});
this.node.on('game_num',function(data){
self.refreshBtns();
});
this.node.on('game_huanpai',function(data){
for(var i in self._seats2){
self._seats2[i].refreshXuanPaiState();
}
});
this.node.on('huanpai_notify',function(data){
var idx = data.seatindex;
var localIdx = cc.vv.gameNetMgr.getLocalIndex(idx);
self._seats2[localIdx].refreshXuanPaiState();
});
this.node.on('game_huanpai_over',function(data){
for(var i in self._seats2){
self._seats2[i].refreshXuanPaiState();
}
});
this.node.on('voice_msg',function(data){
self._voiceMsgQueue.push(data);
self.playVoice();
});
this.node.on('chat_push',function(data){
var idx = cc.vv.gameNetMgr.getSeatIndexByID(data.sender);
var localIdx = cc.vv.gameNetMgr.getLocalIndex(idx);
self._seats[localIdx].chat(data.content);
self._seats2[localIdx].chat(data.content);
});
this.node.on('quick_chat_push',function(data){
var idx = cc.vv.gameNetMgr.getSeatIndexByID(data.sender);
var localIdx = cc.vv.gameNetMgr.getLocalIndex(idx);
var index = data.content;
var info = cc.vv.chat.getQuickChatInfo(index);
self._seats[localIdx].chat(info.content);
self._seats2[localIdx].chat(info.content);
cc.vv.audioMgr.playSFX(info.sound);
});
this.node.on('emoji_push',function(data){
var idx = cc.vv.gameNetMgr.getSeatIndexByID(data.sender);
var localIdx = cc.vv.gameNetMgr.getLocalIndex(idx);
console.log(data);
self._seats[localIdx].emoji(data.content);
self._seats2[localIdx].emoji(data.content);
});
},
initSeats:function(){
var seats = cc.vv.gameNetMgr.seats;
for(var i = 0; i < seats.length; ++i){
this.initSingleSeat(seats[i]);
}
},
initSingleSeat:function(seat){
var index = cc.vv.gameNetMgr.getLocalIndex(seat.seatindex);
var isOffline = !seat.online;
var isZhuang = seat.seatindex == cc.vv.gameNetMgr.button;
console.log("isOffline:" + isOffline);
this._seats[index].setInfo(seat.name,seat.score);
this._seats[index].setReady(seat.ready);
this._seats[index].setOffline(isOffline);
this._seats[index].setID(seat.userid);
this._seats[index].voiceMsg(false);
this._seats2[index].setInfo(seat.name,seat.score);
this._seats2[index].setZhuang(isZhuang);
this._seats2[index].setOffline(isOffline);
this._seats2[index].setID(seat.userid);
this._seats2[index].voiceMsg(false);
this._seats2[index].refreshXuanPaiState();
},
onBtnSettingsClicked:function(){
cc.vv.popupMgr.showSettings();
},
onBtnBackClicked:function(){
cc.vv.alert.show("返回大厅","返回大厅房间仍会保留,快去邀请大伙来玩吧!",function(){
cc.vv.wc.show('正在返回游戏大厅');
cc.director.loadScene("hall");
},true);
},
onBtnChatClicked:function(){
},
onBtnWeichatClicked:function(){
var title = "<血战到底>";
if(cc.vv.gameNetMgr.conf.type == "xlch"){
var title = "<血流成河>";
}
cc.vv.anysdkMgr.share("天天麻将" + title,"房号:" + cc.vv.gameNetMgr.roomId + " 玩法:" + cc.vv.gameNetMgr.getWanfa());
},
onBtnDissolveClicked:function(){
cc.vv.alert.show("解散房间","解散房间不扣房卡,是否确定解散?",function(){
cc.vv.net.send("dispress");
},true);
},
onBtnExit:function(){
cc.vv.net.send("exit");
},
playVoice:function(){
if(this._playingSeat == null && this._voiceMsgQueue.length){
console.log("playVoice2");
var data = this._voiceMsgQueue.shift();
var idx = cc.vv.gameNetMgr.getSeatIndexByID(data.sender);
var localIndex = cc.vv.gameNetMgr.getLocalIndex(idx);
this._playingSeat = localIndex;
this._seats[localIndex].voiceMsg(true);
this._seats2[localIndex].voiceMsg(true);
var msgInfo = JSON.parse(data.content);
var msgfile = "voicemsg.amr";
console.log(msgInfo.msg.length);
cc.vv.voiceMgr.writeVoice(msgfile,msgInfo.msg);
cc.vv.voiceMgr.play(msgfile);
this._lastPlayTime = Date.now() + msgInfo.time;
}
},
// called every frame, uncomment this function to activate update callback
update: function (dt) {
var minutes = Math.floor(Date.now()/1000/60);
if(this._lastMinute != minutes){
this._lastMinute = minutes;
var date = new Date();
var h = date.getHours();
h = h < 10? "0"+h:h;
var m = date.getMinutes();
m = m < 10? "0"+m:m;
this._timeLabel.string = "" + h + ":" + m;
}
if(this._lastPlayTime != null){
if(Date.now() > this._lastPlayTime + 200){
this.onPlayerOver();
this._lastPlayTime = null;
}
}
else{
this.playVoice();
}
},
onPlayerOver:function(){
cc.vv.audioMgr.resumeAll();
console.log("onPlayCallback:" + this._playingSeat);
var localIndex = this._playingSeat;
this._playingSeat = null;
this._seats[localIndex].voiceMsg(false);
this._seats2[localIndex].voiceMsg(false);
},
onDestroy:function(){
cc.vv.voiceMgr.stop();
// cc.vv.voiceMgr.onPlayCallback = null;
}
});
(1) 根据服务器返回的座位数据,对玩家的座位进行初始化设置。
this.initView();
this.initSeats();
this.initEventHandlers();
(2)监听从 GameNetMgr.js
传递过来的事件。
initEventHandlers:function(){
...
this.node.on('game_begin',function(data){
self.refreshBtns();
self.initSeats();
});
this.node.on('game_num',function(data){
self.refreshBtns();
});
this.node.on('game_huanpai',function(data){
for(var i in self._seats2){
self._seats2[i].refreshXuanPaiState();
}
});
...
(3)当有新玩家加入时,监听并处理 new_user
事件。
initEventHandlers:function(){
var self = this;
this.node.on('new_user',function(data){
self.initSingleSeat(data);
});
定缺
- 麻将场景中:
Canvas
–>Game
–>dingque
节点。
2.DingQue.js
通过 this.addComponent("钉阙")
挂载到组件中;(在 MJGame.js
中添加)。
3.onQueYiMenClicked
:当按钮被点击时,将定缺的类型发送至服务器。
//---------定缺按钮点击响应时间,----------
onQueYiMenClicked:function(event){
var type = 0;
if(event.target.name == "tong"){
type = 0;
}
else if(event.target.name == "tiao"){
type = 1;
}
else if(event.target.name == "wan"){
type = 2;
}
for(var i = 0; i < this.selected.length; ++i){
this.selected[i].active = false;
}
this.selected[type].active = true;
cc.vv.gameNetMgr.dingque = type;
cc.vv.net.send("dingque",type);
//this.setInteractable(false);
},
//---------定缺消息发送服务器cc.vv.net.send("dingque",type);----------
self.dispatchEvent('game_dingque')
:触发 game_dingque
事件进行广播。
4.服务器完成定缺操作后,会广播 game_dingque_finish_push
事件。
//-------------------服务器socket_service收到定缺消息socket.on('dingque',function(data){----------------------------
socket.gameMgr.huanSanZhang(socket.userId,p1,p2,p3);
});
//定缺
socket.on('dingque',function(data){
if(socket.userId == null){
return;
}
var que = data;
socket.gameMgr.dingQue(socket.userId,que);
});
//出牌
socket.on('chupai',function(data){
if(socket.userId == null){
return;
}
var pai = data;
socket.gameMgr.chuPai(socket.userId,pai);
});
//碰
socket.on('peng',function(data){
if(socket.userId == null){
return;
}
socket.gameMgr.peng(socket.userId);
});
//杠
socket.on('gang',function(data){
//--------------------socket.gameMgr.dingQue(socket.userId,que)进行处理---------------------------
5.如果四名玩家都已完成定缺操作,则发送 game_playing_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_push
后,广播 self.dispatchEvent('game_dingque_finish', data)
;接着 DingQue.js
会处理该事件,通过 this.node.on('game_dingque_finish', function())
来响应。
客户端还会接收到服务器通过 userMgr.broadcastInRoom('game_playing_push', null, seatData.userId, true)
发送的消息;GameNetMgr.js
随即监听并处理 game_playing_push
事件,之后正式开始游戏。
cc.vv.net.addHandler("game_playing_push",function(data){
console.log('game_playing_push');
self.gamestate = "playing";
self.dispatchEvent('game_playing');
});
怎么去操作游戏
1.选择当前轮到的玩家。
2.广播出牌事件:game_chupai_push
。
3.对牌的操作进行检查,判断后续玩家是否能够进行胡牌、杠等操作。
4: 玩家出牌:
4.MJGame.js
–> onMJClicked
:通过 cc.vv.net.send('chupai', mjId)
发送出牌消息。