var httpclient = require('./httpclient'); var urlbuilder = require('./urlbuilder'); const JC_LOG_T = { lauch:{ key: 1, subkey: 1, }, entermain:{ key: 1, subkey: 2, }, show:{ key: 1, subkey: 3 }, hide:{ key: 1, subkey: 3 }, jumpapp:{ key: 1, subkey: 4 }, loginFailed:{ key: 1, subkey: 5 }, launchsysteminfo:{ key: 1, subkey: 6 }, logined:{ key: 11, subkey: 1 }, authed:{ key: 11, subkey: 2 }, authfail:{ key: 11, subkey: 3 }, startgame:{ key: 11, subkey: 4 }, restartgame:{ key: 11, subkey: 4 }, gameover:{ key: 11, subkey: 6 }, againgame:{ key: 11, subkey: 4 }, productitem:{ key: 11, subkey: 8 }, useitem:{ key: 11, subkey: 9 }, share:{ key: 11, subkey: 10 }, inviter:{ key: 11, subkey: 11 }, systeminfo:{ key: 11, subkey: 20 }, advinfo:{ key: 11, subkey: 21 }, vslogin:{ key: 11, subkey: 24 }, vsreconnect:{ key: 11, subkey: 25 }, vsroomcreate:{ key: 11, subkey: 26 }, vsroomjoin:{ key: 11, subkey: 27 }, vsroomleave:{ key: 11, subkey: 28 }, vsroomsetstate:{ key: 11, subkey: 29 }, business:{ key: 11, subkey: 30 }, buttonclick:{ key: 11, subkey: 31 }, msgevent:{ key: 11, subkey: 32 } }; const _SHOW_FLAG = 2; const _HIDE_FLAG = 1; const _ONLINE_KEY = 'jc_online_flags'; // 说明:带下划线的函数是内部函数,无需关心 const jcgamelog = { // LIFE-CYCLE CALLBACKS: // onLoad () {}, // start () {}, // update (dt) {}, __uuid(len, radix) { var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); var uuid = [], i; radix = radix || chars.length; if (len) { // Compact form for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; } else { // rfc4122, version 4 form var r; // rfc4122 requires these characters uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 for (i = 0; i < 36; i++) { if (!uuid[i]) { r = 0 | Math.random() * 16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } } return uuid.join(''); }, _buildReportUrl(typeobj){ this.urlbd.clear(); this.urlbd.addKV('c', 'GameLog') .addKV('a', 'reportLog') .addKV('gameid', this.gameid) .addKV('channel', this.channelid) .addKV('account_id', this.accountid) .addKV('session_id', this.sessionid) .addKV('access_token', this.token) .addKV('wid', this.wid) .addKV('sid', this.sid) .addKV('ptid', this.ptid) .addKV('logclass1', typeobj.key) .addKV('logclass2', typeobj.subkey) .addKV('localuuid', this.localid) .addKV('from_appid', this.fromid) ; //console.log("[gamelog]"); //console.log(JSON.stringify(typeobj)); return this.urlbd.baseurl; }, _buildReportUserUrl(){ this.urlbd.clear(); this.urlbd.addKV('c', 'GameLog') .addKV('a', 'reportUser') .addKV('gameid', this.gameid) .addKV('channel', this.channelid) .addKV('account_id', this.accountid) .addKV('session_id', this.sessionid) .addKV('access_token', this.token) ; return this.urlbd.baseurl; }, _buildShowHideMsg(showflag){ let data = { str1: this.localid, str2: this.fromid, str3: this.accountid, str4: this.nickName, num1: showflag, num2: -1 }; let msg = { u: this._buildReportUrl(showflag == _SHOW_FLAG? JC_LOG_T.show: JC_LOG_T.hide), v: JSON.stringify(data), ext: showflag }; return msg; }, _report(typeobj, valueobj, successcb, failcb){ let url = this._buildReportUrl(typeobj); let value = JSON.stringify(valueobj); var self = this; httpclient.httpPost(url, value, function(restext){ let obj = httpclient.JSON_parse(restext); if(obj.errcode == 0){ //console.log('[_report]success!'+JSON.stringify(obj)); successcb && successcb(); }else{ //console.log('[_report]'+url); //console.log('[_report]failed!'+obj.errcode+":"+obj.errmsg); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function(errcode, errmsg){ //console.log('[_report]'+url); //console.log('[_report]failed!'+errcode+":"+errmsg); failcb && failcb(errcode, 0, errmsg); let nflag = -1; if(typeobj.key == JC_LOG_T.show.key && typeobj.subkey == JC_LOG_T.show.subkey){ nflag = valueobj.num1; } let msg = { u: url, v: value, ext: nflag } self.cachemsg.push(msg); }); }, _reportUser(valueobj, successcb, failcb){ let url = this._buildReportUserUrl(); let value = JSON.stringify(valueobj); var self = this; httpclient.httpPost(url, value, function(restext){ let obj = httpclient.JSON_parse(restext); if(obj.errcode == 0){ //console.log('[_reportUser]success!'+JSON.stringify(obj)); successcb && successcb(); }else{ //console.log('[_reportUser]failed!'+obj.errcode+":"+obj.errmsg); failcb && failcb(0, obj.errcode, obj.errmsg); } }, function(errcode, errmsg){ //console.log('[_reportUser]failed!'+errcode+":"+errmsg); failcb && failcb(errcode, 0, errmsg); let nflag = -1; let msg = { u: url, v: value, ext: nflag } self.cachemsg.push(msg); }); }, _retry(){ if(this.cachemsg.length > 0){ let obj = this.cachemsg[0]; var self = this; httpclient.httpPost(obj.u, obj.v, function(restext){ if(obj.ext >= 0){ self.showflag = obj.ext; self._saveflag(self.showflag); } self.cachemsg.shift(); }, function(errcode, errmsg){ //console.log('[_retry_report]failed!'+errcode+":"+errmsg); }); } }, _saveflag(showflag){ }, _loadflag(){ }, _handlelaunch(res){ if(res){ if(res.query && res.query.weixinadinfo){ let wxaddr = res.query.weixinadinfo.split('.'); let ad = wxaddr[0]; if(ad && ad != ''){ this.fromadvid = ad; } } this.launchparam = res.query; if(res.referrerInfo && res.referrerInfo.appId){ this.setFromAppID(res.referrerInfo.appId); this.launchparam = res.referrerInfo.extraData; }else if(res.query && res.query.scene){ this.setFromAppID(res.query.scene); } this.scene = res.scene? res.scene: 0; } }, _handleLoginInfo(res){ if(res.account_id && res.session_id){ this.setAccountID(res.account_id, res.session_id); } if(res && res.nickname && res.nickname != ''){ this.setNickName(res.nickname); } }, _handleAuthInfo(res){ if(res && res.nickname && res.nickname != ''){ this.setNickName(res.nickname); } }, //【通用函数】生成本地匿名ID generateUUID(){ return this.__uuid(32, 62); }, // 初始化(上报前必须调用此函数)【使用jcfw.init的话此函数无需手动调用】 init(channelid, gameid, isoffical, owner, url){ this.gameid = gameid; this.channelid = channelid; this.accountid = this.accountid? this.accountid: ''; this.sessionid = this.sessionid? this.sessionid: ''; this.token = this.token? this.token: ''; this.localid = this.localid? this.localid: ''; this.nickname = this.nickname? this.nickname: ''; this.fromid = this.fromid? this.fromid: ''; this.fromadvid = this.fromadvid? this.fromadvid: ''; this.scene = this.scene? this.scene: 0; this._tempuuid = ''; this._starttime = 0; this._launchtime = 0; this.needsubmit = false; this.urlbd = new urlbuilder(url); this.showflag = this._loadflag(); this.cachemsg = []; if(this.showflag == _SHOW_FLAG){ this.cachemsg.push(this._buildShowHideMsg(this.showflag)); } this.showflag = null; setInterval(this._retry.bind(this), 5000); //console.log("[jcgamelog]init:"+gameid + "|" + channelid + "|" + isoffical); }, // 【暂未使用】 setSubmitFlag(bsubmit){ this.needsubmit = bsubmit; }, // 登陆成功后调用,传入accountid和sessionid【使用jcfw.login的话此函数无需手动调用】 setAccountID(accountid, sessionid, exobj){ if(!this.accountid || this.accountid != accountid){ this.accountid = accountid; } if(!this.sessionid || this.sessionid != sessionid){ this.sessionid = sessionid; } if(exobj){ this.wid = exobj.wid; this.sid = exobj.sid; this.ptid = exobj.ptid; } }, // 设置昵称【无需手动调用】 setNickName(nickname){ if(!this.nickname || this.nickname != nickname){ this.nickname = nickname; } }, // 设置本app从哪个appid跳转过来的【使用jcfw.init的话此函数无需手动调用】 setFromAppID(appid){ if(!this.fromid || this.fromid != appid){ this.fromid = appid; } }, // 设置本地匿名ID(本地生成匿名id后调用)【使用jcfw.init的话此函数无需手动调用】 setLocalUUID(uuid){ if(!this.localid || this.localid != uuid){ this.localid = uuid; } }, setSystemInfo(info){ }, // 【暂未使用】 setToken(token){ this.token = token; }, handlelaunch(res, cb){ this._launchtime = new Date().getTime(); var param = 0; var uid = ''; var stype = 0; var invid = ''; if(res){ this._handlelaunch(res); param = res.query.activity_param? res.query.activity_param: 0; uid = res.query.localuuid? res.query.localuuid: ''; stype = res.query.sharetype? res.query.sharetype: 0; invid = res.query.inviter_id? res.query.inviter_id: ''; } this.logLauchDefault(res); cb && cb(stype, param, uid, invid, this.fromid, this.scene, this.launchparam); }, //////////////////统计函数//////////////////// // 启动游戏--------游戏启动后上报【使用jcfw.init的话此函数无需手动调用】 logLaunch(launch_option){ this._launchtime = new Date().getTime(); this._handlelaunch(launch_option); this.logLauchDefault(launch_option); }, //启动游戏(内部调用) logLauchDefault(launch_option){ let data = { str1: this.localid, str2: this.fromid, ext: launch_option? JSON.stringify(launch_option): null, str3: this.fromadvid, num1: this.scene } this._report(JC_LOG_T.lauch, data); }, // 登陆成功--------游戏登陆成功后上报【使用jcfw.login的话此函数无需手动调用】 logLoginSuccess(res, costtime){ let nowtime = new Date().getTime(); let dttime = nowtime - this._launchtime; this._handleLoginInfo(res); let data = { str1: res.nickname, str2: res.unionid, str3: res.country, str4: res.province, str5: res.city, str6: this.localid, num1: costtime? costtime: 0, num2: dttime, str7: this.fromadvid } //console.log('[launchcost]'+dttime); this._report(JC_LOG_T.logined, data); }, // 登陆失败---------游戏登陆失败后上报【使用jcfw.login的话此函数无需手动调用】 logLoginFailed(neterr, logicerr, errmsg){ let data = { error_code_net: neterr, error_code_logic: logicerr, error_msg: errmsg }; this._report(JC_LOG_T.loginFailed, data); }, // 授权成功--------微信获取userinfo后上报【使用jcfw.login的话此函数无需手动调用】 logAuthSuccess(res){ this._handleAuthInfo(res); let data = { str1: res.nickName, num1: res.gender, str3: res.country, str4: res.province, str5: res.city, str6: this.localid, str7: res.avatarUrl? res.avatarUrl: '', str8: res.language } this._report(JC_LOG_T.authed, data); }, // 授权失败--------微信获取userinfo时,用户拒绝授权后上报【使用jcfw.login的话此函数无需手动调用】 logAuthFail(){ let data = { str1: this.nickName, str2: this.localid } this._report(JC_LOG_T.authfail, data); }, // 分享---------------用户分享时上报【使用jcfw.shareNormal/shareCapture的话此函数无需手动调用】 logShare(businessid, sharetokenid, param){ let data = { str1: this.nickname, str2: sharetokenid? sharetokenid: '', num1: businessid, str3: param? param: '' } this._report(JC_LOG_T.share, data); }, // 分享点击-------------当用户点击别人分享的卡片进入游戏时上报【使用jcfw.login的话此函数无需手动调用】 logShareInvite(inviterid, businessid, sharetokenid, param){ let data = { str1: this.nickName, str2: inviterid, num1: businessid, str3: sharetokenid? sharetokenid: sharetokenid, str4: param? param: '' } this._report(JC_LOG_T.inviter, data); }, // 系统信息-------------获取用户系统信息后上报【使用jcfw.init的话此函数无需手动调用】 logSysInfo(res, typeobj){ let data = { str1: res.brand, str2: res.model, str3: res.language, str4: res.version, str5: res.platform, str6: res.SDKVersion, str7: res.system, num1: res.pixelRatio, num2: res.screenWidth, num3: res.screenHeight, num4: res.windowWidth, num5: res.windowHeight, num6: res.benchmarkLevel // ext: JSON.stringify(res) }; this._report(typeobj, data); }, logSystemInfo(res){ if(!res){ return; } this.logSysInfo(res, JC_LOG_T.systeminfo); }, logLaunchSystemInfo(res){ if(!res){ return; } this.logSysInfo(res, JC_LOG_T.launchsysteminfo); }, // 开始游戏---------开始一局游戏时上报【使用jcfw.gameStart的话此函数无需手动调用】 logStartGame(param, startmode){ this._tempuuid = this.generateUUID(); this._starttime = new Date().getTime(); let data = { str1: this.fromid, str2: this._tempuuid, str3: param? param: '', str4: this.nickname, str5: this.localid, num1: startmode? startmode: 0 } this._report(JC_LOG_T.startgame, data); }, // 重新开始游戏---------重新开始一局游戏时上报【使用jcfw.gameStart的话此函数无需手动调用】 logRestartGame(param){ this._tempuuid = this.generateUUID(); this._starttime = new Date().getTime(); let data = { str1: this.fromid, str2: this._tempuuid, str3: param? param: '', str4: this.nickname, str5: this.localid, num1: 1 } this._report(JC_LOG_T.restartgame, data); }, // 上报游戏还活着 logGameAlive(param){ //this._tempuuid = this.generateUUID(); this._starttime = new Date().getTime(); let data = { str1: this.fromid, str2: this._tempuuid, str3: param? param: '', str4: this.nickname, str5: this.localid, num1: 3 } this._report(JC_LOG_T.restartgame, data); }, // 游戏结束---------本局游戏结束时上报【使用jcfw.gameOver的话此函数无需手动调用】 logGameover(param, score, endflag){ let nowtime = new Date().getTime(); let data = { str1: this.fromid, str2: this._tempuuid, str3: param? param: '', num1: score, str4: this.nickname, str5: this.localid, num2: nowtime - this._starttime, num3: endflag? endflag: 0 } this._report(JC_LOG_T.gameover, data); }, // 再来一次----------再来一次时上报【使用jcfw.gameStart的话此函数无需手动调用】 logTryAgain(param){ this._tempuuid = this.generateUUID(); let data = { str1: this.fromid, str2: this._tempuuid, str3: param? param: '', str4: this.nickname, str5: this.localid, num1: 2 } this._report(JC_LOG_T.againgame, data); }, // 显示-----------------------app显示时上报(eg:wx.onShow)【使用jcfw.gameShow的话此函数无需手动调用】 logShow(bIgnoreFlag){ if(!bIgnoreFlag){ let bok = !this.showflag || this.showflag == _HIDE_FLAG; if(!bok){ return; } this.showflag = _SHOW_FLAG; } let nowtime = new Date().getTime(); let data = { str1: this.localid, str2: this.fromid, str3: this.accountid, str4: this.nickName, num1: _SHOW_FLAG, num2: nowtime - this._launchtime }; this._report(JC_LOG_T.show, data, ()=>{ if(!bIgnoreFlag){ this._saveflag(this.showflag); } }, () => { }); }, // 隐藏-----------------------app隐藏时上报(eg:wx.onHide)【使用jcfw.gameHide的话此函数无需手动调用】 logHide(bIgnoreFlag){ if(!bIgnoreFlag){ let bok = this.showflag && this.showflag == _SHOW_FLAG; if(!bok){ return; } this.showflag = _HIDE_FLAG; } let nowtime = new Date().getTime(); let data = { str1: this.localid, str2: this.fromid, str3: this.accountid, str4: this.nickName, num1: _HIDE_FLAG, num2: nowtime - this._launchtime }; this._report(JC_LOG_T.hide, data, ()=>{ if(!bIgnoreFlag){ this._saveflag(this.showflag); } }, () => { }); }, // 产出道具----------道具产出时上报【使用jcfw.gameGetItem的话此函数无需手动调用】 logProductItem(itemid, itemcount, reson, resonparam){ let data = { str1: this.nickname, num1: itemid, num2: itemcount, num3: reson, num4: resonparam } this._report(JC_LOG_T.productitem, data); }, // 使用道具----------使用道具时上报【使用jcfw.gameUseItem的话此函数无需手动调用】 logUseItem(itemid, itemcount, reson, resonparam){ let data = { str1: this.nickname, num1: itemid, num2: itemcount, num3: reson, num4: resonparam } this._report(JC_LOG_T.useitem, data); }, // 运营活动上报---------------开始运营活动时上报 logBusiness(businessid, actionid){ let data = { str1: this.nickName, str2: this.localid, num1: businessid, num2: actionid }; this._report(JC_LOG_T.business, data); }, // 进入游戏主界面-------------主界面显示时上报 logEnterMainScene(dt){ let data = { str1: this.localid, str2: this.fromid, num1: dt? dt: 0 }; this._report(JC_LOG_T.entermain, data); }, // 跳转到其他app--------------跳转到其他app时上报(eg: wx.navigateMiniProgram) logJumpApp(appid, appparam, jumpres){ let data = { str1: this.localid, str2: this.fromid, str3: this.accountid, str4: this.nickName, str5: appid, str6: appparam, num1: jumpres? jumpres: 0 }; this._report(JC_LOG_T.jumpapp, data); }, // 广告相关-----------------处理广告时上报(eg: 激励广告出现时) logAdvInfo(advid, actiontype, actionparam){ let data = { str1: this.nickname, str2: advid, num1: actiontype, str3: actionparam }; this._report(JC_LOG_T.advinfo, data); }, // 【对战开房间】登陆【无需主动调用】 logVS_login(){ let data = { str1: this.nickName }; this._report(JC_LOG_T.vslogin, data); }, // 【对战开房间】重连【无需主动调用】 logVS_reconnect(roomid){ let data = { str1: this.nickName, str2: roomid }; this._report(JC_LOG_T.vsreconnect, data); }, // 【对战开房间】创建房间【无需主动调用】 logVS_createRoom(roomname, maxplayer){ let data = { str1: this.nickname, str2: roomname, num1: maxplayer }; this._report(JC_LOG_T.vsroomcreate, data); }, // 【对战开房间】加入房间【无需主动调用】 logVS_joinRoom(roomid, customdata, israndom){ let data = { str1: this.nickname, str2: roomid? roomid: '', num1: israndom? 1: 0, str3: customdata }; this._report(JC_LOG_T.vsroomjoin, data); }, // 【对战开房间】离开房间【无需主动调用】 logVS_leaveRoom(roomid, customdata){ let data = { str1: this.nickname, str2: roomid, str3: customdata }; this._report(JC_LOG_T.vsroomleave, data); }, // 【对战开房间】游戏准备【无需主动调用】 logVS_gameReady(roomid, customdata, isready){ let data = { str1: this.nickname, str2: roomid, num1: isready? 1: 0, str3: customdata }; this._report(JC_LOG_T.vsroomsetstate, data); }, // 【对战开房间】游戏开始【无需主动调用】 logVS_gameStart(roomid, customdata){ let data = { str1: this.nickname, str2: roomid, num1: 2, str3: customdata }; this._report(JC_LOG_T.vsroomsetstate, data); }, // 按钮事件----------------点击按钮时上报 logButtonClick(buttonname, param, buttonsubname){ let data = { nickname: this.nickname, button_name: buttonname, button_param: param? param: '' }; if(buttonsubname){ data.button_subname = buttonsubname; } this._report(JC_LOG_T.buttonclick, data); }, logMsg(msgid, msgname, errcode, reqcontent, rspcontent, passtime){ let data = { msg_id: msgid, msg_name: msgname, msg_error: errcode, req_content: reqcontent, rsp_content: rspcontent, msg_cosumetime: passtime }; this._report(JC_LOG_T.msgevent, data); }, // 设置角色属性 /* 调用示例:角色初始化时 run(){ let obj = { level: 1, money: 10000, ... }; jcfw.gamelog.userSet(obj); } */ userSet(kvobj){ let data = { set: kvobj }; this._reportUser(data); }, // 设置角色属性(仅允许设置一次) /* 调用示例:改名时(如果有此功能) run(){ let obj = { nickname: "abc" }; jcfw.gamelog.userSet(obj); } */ userSetOnce(kvobj){ let data = { set_once: kvobj }; this._reportUser(data); }, // 增加/减少角色数值属性 /* 调用示例:角色升级时 run(){ let obj = { level: 1 }; jcfw.gamelog.userAdd(obj); } */ userAdd(kvobj){ let data = { add: kvobj }; this._reportUser(data); }, /*可以一次调用set/setonce/add */ userReport(setobj, setonceobj, addobj){ let data = { set: setobj, set_once: setonceobj, add: addobj }; this._reportUser(data); }, /** * * @param {number} externalID 外部调用的ID * @param {object} logFunc 外部调用的方法 * @param {string} p1 参数1 * @param {string} p2 参数2 * @param {string} p3 参数3 * @param {string} p4 参数4 */ externalLog(externalID, logFunc, p1, p2, p3, p4){ let tmpid = this.gameid; this.gameid = externalID; logFunc && logFunc.call(this,p1,p2,p3,p4); this.gameid = tmpid; } }; module.exports = jcgamelog;