达达麻将客户端初始化流程
达达麻将版图
客户端代码结构
1: scripts文件夹下:
(1) 3rdparty: 第三方代码 socket-io;
(2) Comonents: 游戏中挂到节点上的组件;
(3)全局对象:在代码的根目录下;
loading场景
1: Loading Scene 挂载了脚本: LoadingLogic.js
2: 加载场景初始化过程:
(1): 初始化全局的管理对象,全部记录到 cc.vv这个表里面, 特点: cc.vv.userMgr = new UserMgr() 对象实例是小写开头,代码是大写开头,全局唯一只有一个; initMgr: UserMgr, ReplayMgr, HTTP, Global, Net, GameNetMgr, AnysdkMgr, VoiceMgr, AudioMgr, Utils, 解析url的参数,来决定不同的用户账号
(2)显示几秒的开机画面;
showSplash:function(callback){
(3)检查更新;
this.getServerInfo();
(4)startPreloading: 预加载资源 resources/textures;
(5)加载完成后,进入”login”场景;
注意:官方开源的最新版本,和泄露版本达达麻将(早期版本)相比是有做升级更新的。如下图:1是最新开源,2是早期版本。
在1中:打开游戏,第一个UI界面是start:绑定的脚本是AppStart.js
在2中:打开游戏,第一个UI界面是loading;绑定的JS脚本是LoadingLogic.js
//------------AppStart.js------------function urlParse(){var params = {};if(window.location == null){return params;}var name,value; var str=window.location.href; //取得整个地址栏var num=str.indexOf("?") str=str.substr(num+1); //取得所有参数 stringvar.substr(start [, length ]var arr=str.split("&"); //各个参数放到数组里for(var i=0;i < arr.length;i++){ num=arr[i].indexOf("="); if(num>0){ name=arr[i].substring(0,num);value=arr[i].substr(num+1);params[name]=value;} }return params;}function initMgr(){cc.vv = {};var UserMgr = require("UserMgr");cc.vv.userMgr = new UserMgr();var ReplayMgr = require("ReplayMgr");cc.vv.replayMgr = new ReplayMgr();cc.vv.http = require("HTTP");cc.vv.global = require("Global"); = require("Net");var GameNetMgr = require("GameNetMgr");cc.vv.gameNetMgr = new GameNetMgr();cc.vv.gameNetMgr.initHandlers();var AnysdkMgr = require("AnysdkMgr");cc.vv.anysdkMgr = new AnysdkMgr();cc.vv.anysdkMgr.init();var VoiceMgr = require("VoiceMgr");cc.vv.voiceMgr = new VoiceMgr();cc.vv.voiceMgr.init();var AudioMgr = require("AudioMgr");cc.vv.audioMgr = new AudioMgr();cc.vv.audioMgr.init();var Utils = require("Utils");cc.vv.utils = new Utils();//var MJUtil = require("MJUtil");//cc.vv.mjutil = new MJUtil();cc.args = urlParse();}cc.Class({extends: ponent,properties: {// foo: {// default: null,// The default value will be used only when the component attaching// to a node for the first time// 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// },// ...label: {default: null,type:cc.Label},loadingProgess:cc.Label,},// use this for initializationonLoad: function () {initMgr();cc.vv.utils.setFitSreenMode();console.log('haha'); this._mainScene = 'loading';this.showSplash(function(){this.getServerInfo();}.bind(this));},onBtnDownloadClicked:function(){cc.sys.openURL(cc.vv.SI.appweb);},showSplash:function(callback){var self = this;var SHOW_TIME = 3000;var FADE_TIME = 500;this._splash = cc.find("Canvas/splash");if(true || cc.sys.os != cc.sys.OS_IOS || !cc.sys.isNative){this._splash.active = true;if(this._splash.getComponent(cc.Sprite).spriteFrame == null){callback();return;}var t = Date.now();var fn = function(){var dt = Date.now() - t;if(dt < SHOW_TIME){setTimeout(fn,33);}else {var op = (1 - ((dt - SHOW_TIME) / FADE_TIME)) * 255;if(op < 0){self._splash.opacity = 0;callback(); }else{self._splash.opacity = op;setTimeout(fn,33); }}};setTimeout(fn,33);}else{this._splash.active = false;callback();}},getServerInfo:function(){var self = this;var onGetVersion = function(ret){cc.vv.SI = ret;if(cc.sys.isNative){var url = cc.url.raw('resources/ver/cv.txt');cc.loader.load(url,function(err,data){cc.VERSION = data;if(ret.version == null){console.log("error.");}else{if(cc.vv.SI.version != cc.VERSION){cc.find("Canvas/alert").active = true;}else{cc.director.loadScene(self._mainScene);}}}.bind(this));}else{cc.director.loadScene(self._mainScene);}};var xhr = null;var complete = false;var fnRequest = function(){self.loadingProgess.string = "正在连接服务器";xhr = cc.vv.http.sendRequest("/get_serverinfo",null,function(ret){xhr = null;complete = true;onGetVersion(ret);});setTimeout(fn,5000); }var fn = function(){if(!complete){if(xhr){xhr.abort();self.loadingProgess.string = "连接失败,即将重试";setTimeout(function(){fnRequest();},5000);}else{fnRequest();}}};fn();},log:function(content){this.label.string += content + '\n';},});//--------------------------LoadingLogic------------------------cc.Class({extends: ponent,properties: {tipLabel:cc.Label,_stateStr:'',_progress:0.0,_splash:null,_isLoading:false,},// use this for initializationonLoad: function () {cc.vv.utils.setFitSreenMode();this.tipLabel.string = this._stateStr;this.startPreloading();},startPreloading:function(){this._stateStr = "正在加载资源,请稍候"this._isLoading = true;var self = this;var onProgress = function ( completedCount, totalCount, item ){//console.log("completedCount:" + completedCount + ",totalCount:" + totalCount );if(self._isLoading){self._progress = completedCount/totalCount;}};//cc.loader.loadResDir("textures",cc.Texture2D, onProgress,function (err, assets) {// self.onLoadComplete();//});self.onLoadComplete();},onLoadComplete:function(){this._isLoading = false;this._stateStr = "准备登陆";cc.director.loadScene("login");},// called every frame, uncomment this function to activate update callbackupdate: function (dt) {if(this._stateStr.length == 0){return;}this.tipLabel.string = this._stateStr + ' ';if(this._isLoading){this.tipLabel.string += Math.floor(this._progress * 100) + "%"; }else{var t = Math.floor(Date.now() / 1000) % 4;for(var i = 0; i < t; ++ i){this.tipLabel.string += '.';} }}});
Login场景
1: login场景挂载了login.js脚本;
(1)扩展了String对象.format()函数;
String.prototype.format = function(args) { if (arguments.length>0) { var result = this; if (arguments.length == 1 && typeof (args) == "object") { for (var key in args) { var reg=new RegExp ("({"+key+"})","g"); result = result.replace(reg, args[key]); } } else { for (var i = 0; i < arguments.length; i++) { if(arguments[i]==undefined) { return ""; } else { var reg=new RegExp ("({["+i+"]})","g"); result = result.replace(reg, arguments[i]); } } } return result; } else { return this; } };
(2)监听:push_need_create_role, 进入创建角色创景;
(3)如果不是网页,那么隐藏游客登陆按钮;
(4)如果保存了微信账号在本地直接自动登陆;
(5)微信账号登陆响应;
(6)游客账号登陆;
无account则从本地获取account;本地没有获取到则通过时间来随机一个;
然后通过http将account发给服务器,消息回调处理函数是onAuth。
cc.vv.http.sendRequest("/guest",{account:account},this.onAuth);
var URL = "http://127.0.0.1:9000";exports.master_url = null;exports.url = null;exports.token = null;init();function init() {exports.master_url = URL;exports.url = URL;}function setURL(url) {URL = url;init();};function sendRequest(path, data, handler, extraUrl) {var xhr = cc.loader.getXMLHttpRequest();xhr.timeout = 5000;if (data == null) {data = {};}if (exports.token) {data.token = exports.token;}if (extraUrl == null) {extraUrl = exports.url;}//解析请求路由以及格式化请求参数var sendpath = path;var sendtext = '?';for (var k in data) {if (sendtext != "?") {sendtext += "&";}sendtext += (k + "=" + data[k]);}//组装完整的URLvar requestURL = extraUrl + sendpath + encodeURI(sendtext);//发送请求console.log("RequestURL:" + requestURL);xhr.open("GET", requestURL, true);if (cc.sys.isNative) {xhr.setRequestHeader("Accept-Encoding", "gzip,deflate", "text/html;charset=UTF-8");}var timer = setTimeout(function() {xhr.hasRetried = true;xhr.abort();console.log('http timeout');retryFunc();}, 5000);var retryFunc = function() {sendRequest(path, data, handler, extraUrl);};xhr.onreadystatechange = function () {console.log("onreadystatechange");clearTimeout(timer);if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)) {// console.log("http res(" + xhr.responseText.length + "):" + xhr.responseText);cc.log("request from [" + xhr.responseURL + "] data [", ret, "]");var respText = xhr.responseText;var ret = null;try {ret = JSON.parse(respText);} catch (e) {console.log("err:" + e);ret = {errcode: -10001,errmsg: e};}if (handler) {handler(ret);}handler = null;}else if (xhr.readyState === 4) {if(xhr.hasRetried){return;}console.log('other readystate == 4' + ', status:' + xhr.status);setTimeout(function() {retryFunc();}, 5000);}else {console.log('other readystate:' + xhr.readyState + ', status:' + xhr.status);}};try {xhr.send();}catch (e) {//setTimeout(retryFunc, 200);retryFunc();}return xhr;}exports.sendRequest = sendRequest;exports.setURL = setURL;
服务器中9000是账号服务器:
游客的登陆逻辑
1:获取url里用户的参数,如果有,就用用户传的参数;
//游客登录onBtnQuickStartClicked:function(){cc.vv.userMgr.guestAuth();},
2: 获取本地的存储的用户,如果没有,就根据时间随机生成一个;
//游客验证登录guestAuth:function(){var account = cc.args["account"];if(account == null){account = cc.sys.localStorage.getItem("account");}if(account == null){account = Date.now();cc.sys.localStorage.setItem("account",account);}cc.vv.http.sendRequest("/guest",{account:account},this.onAuth);},
3: 发送游客登陆请求到服务器;
cc.vv.http.sendRequest("/guest",{account:account},this.onAuth);
userMgr发到账号服务器上的响应地址: /guest
4: 账号服务器响应:返回OK和登陆的用户,大厅的ip地址;
//游客登录服务器返回消息处理onAuth:function(ret){var self = cc.vv.userMgr;if(ret.errcode !== 0){console.log(ret.errmsg);}else{self.account = ret.account;self.sign = ret.sign;cc.vv.http.url = "http://" + cc.vv.SI.hall;//大厅服务器的地址self.login();} },
5: userMgr: login函数,
login:function(){var self = this;var onLogin = function(ret){if(ret.errcode !== 0){console.log(ret.errmsg);}else{if(!ret.userid){//jump to register user .director.loadScene("createrole");}else{console.log(ret);self.account = ret.account;self.userId = ret.userid;self.userName = ret.name;self.lv = ret.lv;self.exp = ret.exp;self.coins = ret.coins;self.gems = ret.gems;self.roomData = ret.roomid;self.sex = ret.sex;self.ip = ret.ip;cc.director.loadScene("hall");}}};cc.vv.wc.show("正在登录游戏");cc.vv.http.sendRequest("/login",{account:this.account,sign:this.sign},onLogin);},
发送请求给大厅服务器 client_server.js提供: /login
服务器返回登陆信息
//配置好响应请求:登录到大厅服务器;app.get('/login',function(req,res){if(!check_account(req,res)){return;}var ip = req.ip;if(ip.indexOf("::ffff:") != -1){ip = ip.substr(7);}var account = req.query.account;db.get_user_data(account,function(data){if(data == null){http.send(res,0,"ok");return;}var ret = {account:data.account,userid:data.userid,name:data.name,lv:data.lv,exp:data.exp,coins:data.coins,gems:data.gems,ip:ip,sex:data.sex,};//判断是否在游戏,还是在房间;则直接进当前房间//一个账户,不能同时在两个房间里面游戏;db.get_room_id_of_user(data.userid,function(roomId){//如果用户处于房间中,则需要对其房间进行检查。 如果房间还在,则通知用户进入if(roomId != null){//检查房间是否存在于数据库中db.is_room_exist(roomId,function (retval){if(retval){ret.roomid = roomId;}else{//如果房间不在了,表示信息不同步,清除掉用户记录db.set_room_id_of_user(data.userid,null);}http.send(res,0,"ok",ret);});}else {http.send(res,0,"ok",ret);}});});});
保存用户信息到userMgr;
进入到大厅场景;
如果没有用户,进入到创建角色场景,创建完角色以后,又再重新登陆一次;
创建角色场景
//-------------CreateRole.js--------------------cc.Class({extends: ponent,properties: {inputName:cc.EditBox,// 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// },// ...},onRandomBtnClicked:function(){var names = ["上官","欧阳","东方","端木","独孤","司马","南宫","夏侯","诸葛","皇甫","长孙","宇文","轩辕","东郭","子车","东阳","子言",];var names2 = ["雀圣","赌侠","赌圣","稳赢","不输","好运","自摸","有钱","土豪",];var idx = Math.floor(Math.random() * (names.length - 1));var idx2 = Math.floor(Math.random() * (names2.length - 1));this.inputName.string = names[idx] + names2[idx2];},// use this for initializationonLoad: function () {cc.vv.utils.setFitSreenMode();this.onRandomBtnClicked();},onBtnConfirmClicked:function(){var name = this.inputName.string;if(name == ""){console.log("invalid name.");return;}console.log(name);cc.vv.userMgr.create(name);}// called every frame, uncomment this function to activate update callback// update: function (dt) {// },});
1:场景挂载了脚本代码代码 CreateRole;
(1)随机的生成一个名字;
(2)随机函数绑定到随机按钮;
(3)确定按钮:userMgr创建用户create函数
2:创建角色:
发送请求给 hallserver --> client_service.js --> “/create_user”;
cc.vv.userMgr.create(name);
返回信息继续走原来登陆的流程;
下一篇:达达麻将开房间