import CodeMgr from "../../scriptMain/CodeMgr" import { StringMap } from "../Collections/StringMap" import Gamecfg from "../common/gameCfg" import { gameMethod } from "../common/gameMethod" import { SevBack } from "../common/Xys" import Config from "../Config" import { GameEvent, NetworkEvent } from "../data/const/EventConst" import GameDataCenter from "../data/GameDataCenter" import GameController from "../GameController" import UIHelp, { DialogParams } from "../logic/ui/UIHelp" import EventMng from "../manager/EventMng" import { I18n } from "../utils/I18nUtil" import NetConfig from "./NetConfig" export interface POST_DATA { cmdId: number url: string, body: Object, cb1?: (sev: SevBack) => void, cb2?: (sev: SevBack) => void, deal: number postTime?: number } export default class HttpRequest { // 网络请求所需时间 static netMs: number = 0 // 网络请求最长时间(超时时间) static timeoutMs: number = 15000 // 请求失败计数 static errCount: number = 0 //请求失败的接口 static errPostMap: StringMap = new StringMap(); //当前请求的接口 static curPostMap: StringMap = new StringMap(); static GET(path: string, callback: Function = () => { }) { let isJiami = this.isJiamiPlatform(); let r = new XMLHttpRequest() r.open("GET", path + `?x=${isJiami ? 1 : 0}`, true) r.timeout = this.timeoutMs r.onerror = () => { console.log("发生错误,url=", path) callback({}) } r.ontimeout = () => { console.log("超时了,url=", path) callback({}) } r.onloadend = function () { let temp = {} if (r.status >= 200 && r.status <= 400 && r.readyState == 4) { let txt = gameMethod.xorEncrypt(r.response, isJiami); temp = JSON.parse(txt) } callback(temp) } r.send() } // 只发送,无需处理回调 static POST_SIMPLE(url: string, body: Object) { let r = new XMLHttpRequest() r.open("POST", url, true) r.setRequestHeader("Content-Type", "application/json") r.send(JSON.stringify(body)) } // 发送业务外,需处理回调 static POST_SIMPLE_2(url: string, body: Object, callback: Function = () => { }) { let r = new XMLHttpRequest() r.open("POST", url, true) r.setRequestHeader("Content-Type", "application/json") r.onreadystatechange = () => { if (r.status >= 200 && r.status <= 400 && r.readyState == 4) { callback(r.response) } else { console.error("POST Faile:", r.status) } } r.send(JSON.stringify(body)) } // 业务逻辑协议调用接口 static POST(path: string, data: { [key: string]: string }, body: Object, callback: Function = () => { }, isLogin: boolean = false) { body = body || {} if (CC_JSB) { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("C->S:", path, JSON.stringify(body)) } } else { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("C->S:", path, body) } } let delay = new Date().getTime() if (NetConfig.needWait(data.url, body)) { EventMng.emit(NetworkEvent.WAIT, 1) } //淘宝需要通过云服务调用服务器 if (cc.sys.platform == cc.sys.TAOBAO_MINIGAME) { this.taoBaoCloudRequest(data, body, callback, isLogin) return } let r = new XMLHttpRequest() r.open("POST", path, true) r.timeout = this.timeoutMs r.setRequestHeader("Content-Type", "application/json") let xor: number = this.isJiamiPlatform() ? 1 : 0;//原生端接收到加密数据会不全,所以就不加密了 // r.onerror = () => { // // console.warn("=======https onerror", r.status, r.readyState) // callback({ type: 0 }) // if (isLogin) { // EventMng.emit(NetworkEvent.LOGIN_FAILED) // return // } // this.onError(data.url) // } r.onerror = r.ontimeout = () => { // console.warn("=======https ontimeout", r.status, r.readyState) this.netMs = r.timeout // 超时的时候,先把waiting关掉 if (NetConfig.needWait(data.url, body)) { EventMng.emit(NetworkEvent.WAIT_CLOSE) } //请求失败时关闭强请求遮罩 EventMng.emit(NetworkEvent.NETWAIT_MASK, false) // 如果是登录过程中出错,特殊处理 if (isLogin) { EventMng.emit(NetworkEvent.LOGIN_FAILED) return } this.onTimeOut(data.url) } r.onreadystatechange = () => { // console.log("=======https onreadystatechange", r.status, r.readyState) if (r.readyState == 4) { this.netMs = new Date().getTime() - delay if (this.netMs > 1000) { console.warn("服务器回调时间超过1秒:", path, JSON.stringify(body)) } } let temp = {} if (r.status >= 200 && r.status <= 400 && r.readyState == 4) { this.errCount = 0 if (r.response == null || r.response == "") { EventMng.emit(NetworkEvent.ON_EXCEPTION) this.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), r.response == null ? "null" : r.response) return } try { // temp = JSON.parse(r.response) // gameMethod.jiemi(r.response) // let jiemiTime = new Date().getTime() let txt = gameMethod.xorEncrypt(r.response, xor == 1); temp = JSON.parse(txt) // console.log("消息解密耗时:", new Date().getTime() - jiemiTime) } catch (error) { EventMng.emit(NetworkEvent.ON_EXCEPTION) console.error("解析回调数据失败:", r.response) this.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), r.response == null ? "null" : r.response) } // try { if (temp && Object.keys(temp).length > 0) { if (CC_JSB) { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("S->C:", JSON.stringify(temp)) } } else { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("S->C:", temp) } } //请求成功移除缓存的消息请求 this.errPostMap.Remove(data.url) this.curPostMap.Remove(data.url) callback(temp) } // } catch (error) { // console.error("服务端回调数据处理时出现异常:", path, error) // } } if (r.readyState == 4 && NetConfig.needWait(data.url, body)) { EventMng.emit(NetworkEvent.WAIT, -1) } if (r.status < 200 || r.status > 400) { // 异常情况 if (isLogin) { EventMng.emit(NetworkEvent.LOGIN_FAILED) } else { this.onTimeOut(data.url) } } }; // r.send(JSON.stringify({ cs: CodeMgr.jiami(body) })) r.send(JSON.stringify({ xor: gameMethod.xorEncrypt(JSON.stringify(body)), x: xor })) // r.send(JSON.stringify(body)) } //淘宝云服请求 static taoBaoCloudRequest(data: { [key: string]: string }, body: Object, callback: Function = () => { }, isLogin: boolean = false) { let path = data.url + "?uuid=" + GameDataCenter.user.uuid + "&token=" + GameDataCenter.user.token + "&version=" + Config.appVersion + "&time=" + GameDataCenter.time.sevTime let self = this; async function taoBaoCloud() { let result try { result = await window['cloud'].application.httpRequest({ //不需要完整域名,只需要接口访问路径即可 'path': path, 'method': 'POST', 'headers': { "Content-Type": "application/json" }, 'params': {}, 'body': { xor: gameMethod.xorEncrypt(JSON.stringify(body)) }, //cloudAppId 云应用的Id 'exts': { "cloudAppId": "55088", "timeout": 4000, //空应用调用需要填写该字段,包括协议头以及端口号(可省略),支持http、https "domain": Gamecfg.packageInfo.getItem(Config.pid).wayhttp } }); } catch (error) { console.log(error) } // console.log("result:", JSON.stringify(gameMethod.xorEncrypt(result))); let temp = {} try { // temp = JSON.parse(result) // gameMethod.jiemi(r.response) temp = JSON.parse(gameMethod.xorEncrypt(result)) } catch (error) { EventMng.emit(NetworkEvent.ON_EXCEPTION) console.error("解析回调数据失败:", result) // self.showErrDialog(data.url, data.uuid, data.version, JSON.stringify(body), result == null ? "null" : result) } try { if (temp && Object.keys(temp).length > 0) { if (CC_JSB) { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("S->C:", JSON.stringify(temp)) } } else { if (!gameMethod.isEmpty(GameDataCenter.sevBack) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch) && !gameMethod.isEmpty(GameDataCenter.sevBack.switch.clientLog)) { console.warn("S->C:", temp) } } //请求成功移除缓存的消息请求 self.errPostMap.Remove(data.url); self.curPostMap.Remove(data.url); callback(temp) } } catch (error) { console.error("服务端回调数据处理时出现异常:", error) } if (NetConfig.needWait(data.url, body)) { EventMng.emit(NetworkEvent.WAIT, -1) } } this.errCount = 0 let delayTime = new Date().getTime() taoBaoCloud().then(res => { if (!gameMethod.isEmpty(GameDataCenter.sevBack?.switch?.clientLog)) { console.log("net delay time == " + (new Date().getTime() - delayTime)) } }); } // 发生服务器错误时,提示重试 private static onError(url: string) { let dialogParam: DialogParams = { content: I18n.getI18nText("http_error", url), cbConfirm: () => { this.errCount = 0 GameController.clear() cc.game.restart() }, txtConfirm: I18n.getI18nText("http_overtime_confirm_1"), onlyConfirm: true } UIHelp.ShowSystemDialog(dialogParam) } // 发生服务器错误时,提示重试 private static onTimeOut(url: string) { this.errCount++ if (this.errCount > 3) { // 超过三次重连失败,提示退出游戏 GameController.network.stopRequest = true let dialogParam: DialogParams = { content: I18n.getI18nText("http_error", url), cbConfirm: () => { this.errCount = 0 GameController.clear() cc.game.restart() }, txtConfirm: I18n.getI18nText("http_overtime_confirm_1"), onlyConfirm: true } UIHelp.ShowSystemDialog(dialogParam) } else { // 提示网络错误并引导重连 let dialogParam: DialogParams = { content: I18n.getI18nText("http_overtime"), cbConfirm: () => { GameDataCenter.user.sendPlayerReconnect(() => { GameController.network.stopRequest = false //重连成功,重新发送失败请求 this.errPostMap.Foreach((key: string, value: POST_DATA) => { console.log(`重新发送:${key}`) GameController.network.send(value.url, value.body, value.cb1, value.cb2, value.deal) }) this.errPostMap.Clear() this.curPostMap.Clear() GameController.network.RemoveTimers() }) }, txtConfirm: I18n.getI18nText("http_overtime_confirm_2"), onlyConfirm: true } UIHelp.ShowSystemDialog(dialogParam) } } private static showErrDialog(url: string, uuid: string, version: string, body: string, response: string) { UIHelp.ShowDialog({ content: "回调数据丢失,请将此界面截图给开发者\n" + `url:${url} uuid:${uuid} version:${version}\n` + `body:` + JSON.stringify(body) + "\n" + `response:${response.slice(0, 200)}` }) } static isJiamiPlatform(): boolean {//以下平台不加密,因为以下加密数据会接受不全 return !cc.sys.isNative && cc.sys.platform != cc.sys.VIVO_GAME && cc.sys.platform != cc.sys.XIAOMI_GAME && cc.sys.platform != cc.sys.OPPO_GAME; } }