// Learn cc.Class: // - [Chinese] http://docs.cocos.com/creator/manual/zh/scripting/class.html // - [English] http://www.cocos2d-x.org/docs/creator/en/scripting/class.html // Learn Attribute: // - [Chinese] http://docs.cocos.com/creator/manual/zh/scripting/reference/attributes.html // - [English] http://www.cocos2d-x.org/docs/creator/en/scripting/reference/attributes.html // Learn life-cycle callbacks: // - [Chinese] http://docs.cocos.com/creator/manual/zh/scripting/life-cycle-callbacks.html // - [English] http://www.cocos2d-x.org/docs/creator/en/scripting/life-cycle-callbacks.html //var httpclient = require("httpclient"); //var urlbuilder = require("urlbuilder"); var httpclient = require('httpclient'); var urlbuilder = require('urlbuilder'); const BaseNet = require('../../BaseNet'); const Config = require('../../Config'); const { isTesting } = require('../../Config'); const { OperationType } = require('../../Operation/Operation'); const { default: ServerNotOpenTip } = require('../../tips/ServerNotOpenTip'); const { WhitelistTip } = require('../../tips/WhitelistTip'); const { uimanger } = require('../../UI/UIManger'); const jcgamelog = require('./jcgamelog'); module.exports = { // LIFE-CYCLE CALLBACKS: // onLoad () {}, //start () {}, // update (dt) {}, __login( channelid, gameid, tokencode, isoffical, successcb, failcb, openid ) { if (window.canAutoLogin == true) { this.autoLogin( localStorage.getItem('loginaccount'), localStorage.getItem('loginsession'), (res) => { console.log('auto login res===' + res); var obj = httpclient.JSON_parse(res); // console.log("[__login]success!" + JSON.stringify(obj)); successcb && successcb(obj); cc.Notifier.emit('autologinsuccess'); } ); return; } console.log(BaseNet.getNormalApiUrl('login')); this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'auth') .addKV('gameid', gameid); if (cc.appPlatformID) { channelid = cc.appPlatformID; console.log('PlatformID==', channelid); } this.urlbd.addKV('channel', channelid); if (cc.sdkdata) { this.urlbd.addKV('openid', cc.sdkdata.openid); // this.urlbd.addKV('token', cc.sdkdata.token); this.urlbd.addKV('poly_sdk_channel', 8001); this.urlbd.addKV( 'poly_sdk_subchannel', cc.sdkdata.poly_sdk_subchannel ); } else if (cc.sdkData2) { console.log('SDKData3==', JSON.stringify(cc.sdkData2)); this.urlbd.addKV('openid', cc.sdkData2.openid); // this.urlbd.addKV('token', cc.sdkData2.token); } else { if (openid && openid != '') { this.urlbd.addKV('openid', openid); } else { this.urlbd.addKV('openid', tokencode); } this.urlbd.addKV('token', tokencode); } if (this.env) { this.urlbd.addKV('env', this.env); } if (channelid == '6516') { this.urlbd.addKV('account', window.account); this.urlbd.addKV('net_id', '321'); // getNounce this.urlbd.addKV('nonce', window.nonce); this.urlbd.addKV('signature', window.appsign); this.urlbd.addKV('tips', 'sign request'); // reset token var tmpToken = ''; if (cc.sdkdata) { tmpToken = cc.sdkdata.token; } else if (cc.sdkData2) { tmpToken = cc.sdkData2.token; } var token = {}; token = { token: tmpToken, account: window.account, tips: 'sign request', nonce: window.nonce, signature: window.appsign, net_id: 321, }; this.urlbd.addKV('type', 0); // if (cc.sys.os == cc.sys.OS_ANDROID) { // token = { // act: 'login', // account: window.account, // chainid: 321, // token: window.appsign, // nonce: window.nonce, // }; // this.urlbd.addKV('type', 1); // } else if (cc.sys.os == cc.sys.OS_IOS) { // token = { // token: tmpToken, // account: window.account, // tips: 'sign request', // nonce: window.nonce, // signature: window.appsign, // net_id: 321, // }; // this.urlbd.addKV('type', 0); // } console.log(`TOKEN --- ${JSON.stringify(token)}`); this.urlbd.addKV('token', JSON.stringify(token)); } else { if (cc.sdkdata) { this.urlbd.addKV('token', cc.sdkdata.token); } else if (cc.sdkData2) { this.urlbd.addKV('token', cc.sdkData2.token); } } httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); console.log(`login res --${restext}`); console.log('errcode----' + obj.errcode); if (obj.errcode == 66) { jcgamelog.addOperation( OperationType.LOGIN, 'blacklist', window.account ); uimanger.showUI(WhitelistTip.prefabPath); if (localStorage.getItem('walletaccount')) { localStorage.removeItem('walletaccount'); } window.hasWallet = false; return; } if (obj.errcode == 103) { uimanger.showUI(ServerNotOpenTip.prefabPath); if (localStorage.getItem('walletaccount')) { localStorage.removeItem('walletaccount'); } window.hasWallet = false; return; } if (obj.errcode == 1001) { cc.Notifier.emit("AutoLoginFailed"); return; } if (obj.errcode == 0) { console.log('[__login]success!' + JSON.stringify(obj)); cc.subchannel = obj.channel; successcb && successcb(obj); } else { console.log( '[__login]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[__login]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, __activeWX( gameid, accountid, sessionid, raw, sign, encyptdata, iv, successcb, failcb ) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'activateWeiXinUser') .addKV('gameid', gameid) .addKV('account_id', accountid) .addKV('session_id', sessionid) .addKV('rawdata', raw) .addKV('signature', sign) .addKV('encrypted_data', encyptdata) .addKV('iv', iv); httpclient.httpGet( this.urlbd.baseurl, function (restext) { if (restext == '') { failcb && failcb(0, -1, 'restext is nullstring!'); return; } var obj = httpclient.JSON_parse(restext); if (obj && obj.errcode == 0) { console.log('[__activeWX]success!' + JSON.stringify(obj)); successcb && successcb(obj); } else { console.log( '[__activeWX]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[__activeWX]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, __updateInfo(accountid, sessionid, usrinfo, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'updateUserInfo') .addKV('account_id', accountid) .addKV('session_id', sessionid) .addKV('nickname', usrinfo.nickname) .addKV('city', usrinfo.city) .addKV('province', usrinfo.province) .addKV('avatar_url', usrinfo.avatar_url) .addKV('country', usrinfo.country) .addKV('sex', usrinfo.sex) .addKV('birthday', usrinfo.birthday) .addKV('phone', usrinfo.phone) .addKV('location', usrinfo.location); // if(!this.isoffical){ // console.log(this.urlbd.baseurl); // } httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log('[__updateInfo]success!' + JSON.stringify(obj)); successcb && successcb(obj); } else { console.log( '[__updateInfo]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[__updateInfo]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, __generalSignature(accountid, sessionid, signdata, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'signature') .addKV('account_id', accountid) .addKV('session_id', sessionid) .addKV('params', signdata); httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log( '[__generalSignature]success!' + JSON.stringify(obj) ); successcb && successcb(obj); } else { console.log( '[__generalSignature]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log( '[__generalSignature]failed!' + errcode + ':' + errmsg ); failcb && failcb(errcode, 0, errmsg); } ); }, __generalToken(accountid, sessionid, sceneid, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'getAccessToken') .addKV('account_id', accountid) .addKV('session_id', sessionid) .addKV('scene', sceneid); httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log( '[__generalToken]success!' + JSON.stringify(obj) ); successcb && successcb(obj); } else { console.log( '[__generalToken]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[__generalToken]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, __getAnnouncement(channelid, gameid, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Annc') .addKV('a', 'getAnnouncement') .addKV('gameid', gameid) .addKV('channel', channelid); httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log( '[__getAnnouncement]success!' + JSON.stringify(obj) ); successcb && successcb(obj); } else { console.log( '[__getAnnouncement]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log( '[__getAnnouncement]failed!' + errcode + ':' + errmsg ); failcb && failcb(errcode, 0, errmsg); } ); }, __getServerList(channelid, gameid, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'SrvList') .addKV('a', 'getSrvList') .addKV('gameid', gameid) .addKV('channel', channelid); if (this.env) { this.urlbd.addKV('env', this.env); } httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log( '[__getServerList]success!' + JSON.stringify(obj) ); successcb && successcb(obj.server_list); } else { console.log( '[__getServerList]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log( '[__getServerList]failed!' + errcode + ':' + errmsg ); failcb && failcb(errcode, 0, errmsg); } ); }, __loginYoume(accountid, sessionid, nickname, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'YouMe') .addKV('a', 'login') .addKV('account_id', accountid) .addKV('session_id', sessionid) .addKV('nickname', nickname); httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log('[__loginYoume]success!' + JSON.stringify(obj)); successcb && successcb(obj.token); } else { console.log( '[__loginYoume]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[__loginYoume]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, _checkUserInfo() { if (this._localinfoloaded && this._svinfoloaded) { if (this.channelid == 6001) { if (!this.actived) { this.activewx( this.userinfo, this._activesuccess, this._activefail ); } else { if (this._combineInfo(this.svuserinfo, this.userinfo)) { this.updateinfo( this.svuserinfo, this._activesuccess, this._activefail ); } else { this._activesuccess && this._activesuccess(this.userinfo); } // this._activefail && this._activefail(0, -1, 'is not weixin platform!'); } } else { if (this._combineInfo(this.svuserinfo, this.userinfo)) { this.updateinfo( this.svuserinfo, this._activesuccess, this._activefail ); } else { this._activesuccess && this._activesuccess(this.userinfo); } } } }, _checkNickName() { if (this.userinfo.nickname) { this.owner && this.owner.setNickName(this.userinfo.nickname); } }, _loadLocalInfo(info) { if (!this.userinfo) { this.userinfo = {}; } switch (this.channelid) { case 6001: { this.userinfo.nickname = info.nickName; this.userinfo.country = info.country; this.userinfo.province = info.province; this.userinfo.city = info.city; this.userinfo.avatar_url = info.avatarUrl; this.userinfo.sex = info.gender; } break; case 6002: { } break; case 6003: { this.userinfo.nickname = info.nickName; this.userinfo.avatar_url = info.avatar; this.userinfo.sex = info.sex; this.userinfo.birthday = info.birthday; this.userinfo.phone = info.phoneNum; this.userinfo.location = info.location; } break; case 6004: { this.userinfo.nickname = info.nickname; this.userinfo.avatar_url = info.avatar; this.userinfo.unionid = info.unionid; //todo: } break; } console.log('[_loadLocalInfo]finish'); this._localinfoloaded = true; }, _loadTokenInfo(tokeninfo) { if (!this.userinfo) { this.userinfo = {}; } if (tokeninfo && tokeninfo.rawData) { this.userinfo.rawData = tokeninfo.rawData; this.userinfo.signature = tokeninfo.signature; this.userinfo.encryptedData = tokeninfo.encryptedData; this.userinfo.iv = tokeninfo.iv; } this._localtokenloaded = true; }, _loadServerInfo(info) { if (!this.svuserinfo) { this.svuserinfo = {}; } this.svuserinfo.nickname = info.nickname; this.svuserinfo.country = info.country; this.svuserinfo.province = info.province; this.svuserinfo.city = info.city; this.svuserinfo.avatar_url = info.avatar_url; this.svuserinfo.sex = info.sex; this.svuserinfo.birthday = info.birthday; this.svuserinfo.phone = info.phone; this.svuserinfo.location = info.location; console.log('[_loadServerInfo]finish'); this._svinfoloaded = true; }, _combineInfo(svinfo, lcinfo) { let bupdated = false; if (lcinfo.nickname && svinfo.nickname != lcinfo.nickname) { console.log('[nickname]' + svinfo.nickName + '|' + lcinfo.nickname); svinfo.nickname = lcinfo.nickname; bupdated = true; } if (lcinfo.city && svinfo.city != lcinfo.city) { console.log('[city]' + svinfo.city + '|' + lcinfo.city); svinfo.city = lcinfo.city; bupdated = true; } if (lcinfo.province && svinfo.province != lcinfo.province) { console.log('[province]' + svinfo.province + '|' + lcinfo.province); svinfo.province = lcinfo.province; bupdated = true; } if (lcinfo.country && svinfo.country != lcinfo.country) { console.log('[country]' + svinfo.country + '|' + lcinfo.country); svinfo.country = lcinfo.country; bupdated = true; } if (lcinfo.avatar_url && svinfo.avatar_url != lcinfo.avatar_url) { console.log( '[avatar_url]' + svinfo.avatar_url + '|' + lcinfo.avatar_url ); svinfo.avatar_url = lcinfo.avatar_url; bupdated = true; } if (lcinfo.sex && svinfo.sex != lcinfo.sex) { console.log('[sex]' + svinfo.sex + '|' + lcinfo.sex); svinfo.sex = lcinfo.sex; bupdated = true; } if (lcinfo.birthday && svinfo.birthday != lcinfo.birthday) { console.log('[birthday]' + svinfo.birthday + '|' + lcinfo.birthday); svinfo.birthday = lcinfo.birthday; bupdated = true; } if (lcinfo.phone && svinfo.phone != lcinfo.phone) { console.log('[phone]' + svinfo.phone + '|' + lcinfo.phone); svinfo.phone = lcinfo.phone; bupdated = true; } if (lcinfo.location && svinfo.location != lcinfo.location) { console.log('[location]' + svinfo.location + '|' + lcinfo.location); svinfo.location = lcinfo.location; bupdated = true; } return bupdated; }, init(channelid, gameid, isoffical, owner, env) { this.owner = owner; this.channelid = channelid; this.gameid = gameid; this.sessionid = this.sessionid ? this.sessionid : ''; this.accountid = this.accountid ? this.accountid : ''; this.openid = this.openid ? this.openid : ''; this.env = env; this.isoffical = isoffical; this.logined = this.logined ? this.logined : false; this.urlbd = new urlbuilder(BaseNet.getNormalApiUrl('login')); console.log( '[jclogin]init:' + gameid + '|' + channelid + '|' + isoffical ); }, login(token, successcb, failcb, openid) { var self = this; this.__login( this.channelid, this.gameid, token, this.isoffical, function (obj) { let nowtime = new Date().getTime(); self.sessionid = obj.session_id; self.accountid = obj.account_id; self.openid = obj.openid; self.actived = obj.activated == true; self.logined = true; self.login_costtime = nowtime - self._loginstarttime; self._loadServerInfo(obj); self._checkUserInfo(); successcb && successcb(obj); }, failcb, openid ); }, loginPT(successcb, failcb, exparam) { switch (this.channelid) { case 6511: case 6513: case 6516: case 6000: { this.login(this.localid, successcb, failcb); } break; case 6001: { this.loginwx(successcb, failcb); } break; case 6002: { // let self = this; let openId = BK.getSystemInfoSync().openId; console.log('loginQQ openid:' + openId); // BK.Script.loadlib("GameRes://qqPlayCore.js") // BK.QQ.fetchOpenKey(function (errCode, cmd, data) { if (errCode == 0) { let openKey = data.openKey; console.log('loginQQ openKey:' + openKey); self.login(openKey, successcb, failcb, openId); } }); } break; case 6003: { console.log('[qg.login]BEGIN:' + exparam); if (typeof qg != 'undefined') { qg.login({ pkgName: exparam, success: (res) => { console.log('[qg.login]success'); console.log(JSON.stringify(res)); this.login( res.token, successcb, failcb, res.uid ); this.updateuserinfo(res); }, fail: (res) => { console.log('[qg.login]fail'); console.log(JSON.stringify(res)); failcb && failcb(); }, }); } } break; case 6004: { var self = this; var vivoGetUserInfo = function (vivotoken) { qg.getProfile({ token: vivotoken, success: function (data) { console.log('[vivo.login]success'); self.login( vivotoken, successcb, failcb, data.openid ); self.updateuserinfo(data); }, fail: function (data, code) { console.log('[vivo.login]fail'); failcb && failcb(); }, }); }; qg.authorize({ type: 'token', success: function (data) { console.log('vivoAuthorize success'); vivoGetUserInfo(data.accessToken); }, fail: function (data, code) { console.log('vivoAuthorize fail'); failcb && failcb(); }, }); } break; } }, updatePTInfo(userinfo, tokeninfo, successcb, failcb) { this.updateuserinfo(userinfo, tokeninfo, successcb, failcb); }, loginwx(successcb, failcb) { if (wx.login) { var self = this; this._loginstarttime = new Date().getTime(); wx.login({ success: function (res) { console.log('[wx]login success!' + res.code); self.login(res.code, successcb, failcb); }, fail: function (res) { console.log('[wx]login fail!'); failcb && failcb(0, res.errcode, res.errmsg); }, }); } else { failcb && failcb(-1, 0, 'not weixin platform!'); } }, loginYoume(successcb, failcb) { this.__loginYoume( this.accountid, this.sessionid, this.nickname, successcb, failcb ); }, updateuserinfo(userinfo, tokeninfo, successcb, failcb) { this._loadLocalInfo(userinfo); this._loadTokenInfo(tokeninfo); this._activesuccess = successcb; this._activefail = failcb; this._checkUserInfo(); this._checkNickName(); }, activewx(res, successcb, failcb) { var self = this; this.__activeWX( this.gameid, this.accountid, this.sessionid, res.rawData, res.signature, res.encryptedData, res.iv, function (obj) { self.actived = true; successcb && successcb(obj); }, failcb ); }, updateinfo(usrinfo, successcb, failcb) { this.__updateInfo( this.accountid, this.sessionid, usrinfo, successcb, failcb ); }, getToken(sceneid, successcb, failcb) { this.__generalToken( this.accountid, this.sessionid, sceneid, successcb, failcb ); }, getSignature(signdata, successcb, failcb) { this.__generalSignature( this.accountid, this.sessionid, signdata, successcb, failcb ); }, getAnnouncement(successcb, failcb) { this.__getAnnouncement(this.channelid, this.gameid, successcb, failcb); }, getServerList(successcb, failcb) { this.__getServerList(this.channelid, this.gameid, successcb, failcb); }, isActived() { return this.actived; }, AccountID(openid) { if (this.accountid && this.accountid != '') { return this.accountid; } return this.channelid + '_' + this.gameid + '_' + openid; }, setAccountID(accountid, sessionid) { this.accountid = accountid; this.sessionid = sessionid; }, setNickName(nickname) { this.nickname = nickname; }, setFromAppID(appid) { this.fromid = appid; }, setLocalUUID(uuid) { this.localid = uuid; }, setSystemInfo(info) {}, getNonce() { return new Promise((resolve, reject) => { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'getNonce') .addKV('gameid', this.gameid) .addKV('account', window.account); httpclient.httpGet( this.urlbd.baseurl, function (res) { window.nonce = httpclient.JSON_parse(res).nonce; if (cc.sys.os == cc.sys.OS_IOS) { jsb.reflection.callStaticMethod( 'AppController', 'signApp:', window.nonce ); } else if (cc.sys.os == cc.sys.OS_ANDROID) { var signData = { types: { EIP712Domain: [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, ], set: [ { name: 'tips', type: 'string' }, { name: 'nonce', type: 'string' }, ], }, primaryType: 'set', domain: { name: 'Auth', version: '1' }, message: { tips: 'sign request', nonce: window.nonce, }, }; jsb.reflection.callStaticMethod( 'org/cocos2dx/javascript/AppActivity', 'signApp', '(Ljava/lang/String;)V', JSON.stringify(signData) ); } resolve(window.nonce); }, function (errcode, errmsg) { console.log(`get nonce error--${errmsg}----${errcode}`); } ); }); }, autoLogin(accountid, sessionid, successcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'sessionAuth') .addKV('account_id', accountid) .addKV('session_id', sessionid); httpclient.httpGet( this.urlbd.baseurl, function (res) { console.log('login success'); successcb && successcb(res); }, function (errcode, errmsg) { console.log('login err' + errcode + errmsg); } ); }, getSwitch(cb) { const tmpUrlBd = new urlbuilder(BaseNet.getExaminingUrl()); tmpUrlBd .addKV('c', 'ServerSwitch') .addKV('a', 'getSwitch') .addKV('gameid', 2006); httpclient.httpGet( tmpUrlBd.baseurl, function (res) { var obj = httpclient.JSON_parse(res); if (obj.examining == '1') { Config.isTest = false; } else { Config.isTest = false; } cb(obj); }, function (errcode, errmsg) {} ); }, getVoipSign(group_id, successcb, failcb) { this.urlbd.clear(); this.urlbd .addKV('c', 'Login') .addKV('a', 'getVoipSign') .addKV('gameid', this.gameid) .addKV('account_id', this.accountid) .addKV('session_id', this.sessionid) .addKV('group_id', group_id); httpclient.httpGet( this.urlbd.baseurl, function (restext) { var obj = httpclient.JSON_parse(restext); if (obj.errcode == 0) { console.log('[getVoipSign]success!' + JSON.stringify(obj)); successcb && successcb(obj); } else { console.log( '[getVoipSign]failed!' + obj.errcode + ':' + obj.errmsg ); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function (errcode, errmsg) { console.log('[getVoipSign]failed!' + errcode + ':' + errmsg); failcb && failcb(errcode, 0, errmsg); } ); }, };