const log$i = new Logger("NetworkController"); class NetworkController extends EventEmitter { constructor(e) { super(); E(this, "socket"); E(this, "rtcp"); E(this, "stream"); E(this, "_state", "connecting"); E(this, "_networkMonitor"); E(this, "blockedActions", []); E(this, "reconnectCount", 0); E(this, "startGame", ()=>new Promise((e,t)=>{ if (!this.rtcp.connected) return t(new InternalError("Game cannot load. Please refresh")); if (!this.rtcp.inputReady) return t(new InternalError("Game is not ready yet. Please wait")); this.socket.on("gameRoomAvailable", r=>{ this.setState("connected"), e(r), this.rtcp.heartbeat.start() } ), this.socket.on("socketClosed", r=>{ t(r) } ), this.socket.startGame() } )); this.room = e, this.socket = new Socket(this), this.rtcp = new Rtcp(this), this.stream = new Stream, this._networkMonitor = new NetworkMonitor(()=>{ log$i.info("network changed, online:", this._networkMonitor.isOnline), this._state === "disconnected" && this._networkMonitor.isOnline && (log$i.info("network back to online, try to reconnect"), this.reconnect()) } ), checkNetworkQuality(this.room.currentNetworkOptions.wsServerUrl), this._networkMonitor.start(), new VisibilityChangeHandler().subscribe(r=>{ var n, o; r ? ((o = this.room.stats) == null || o.disable(), log$i.infoAndReportMeasurement({ metric: "pageHide", startTime: Date.now() })) : ((n = this.room.stats) == null || n.enable(), log$i.infoAndReportMeasurement({ metric: "pageShow", startTime: Date.now(), extra: { state: this._state } }), this._state === "disconnected" && this.reconnect()) } ) } addBlockedActions(e) { this.blockedActions.push(...e) } removeBlockedActions(e) { if (!e) { this.blockedActions = []; return } const t = this.blockedActions.indexOf(e); this.blockedActions.splice(t, 1) } setState(e) { this._state !== e && (log$i.info("Set network state to ", e), this._state = e) } async connectAndStart(e) { return this.connect(e).then(this.startGame) } async connect(e=!1) { return this.room.updateCurrentNetworkOptions({ reconnect: e }), new Promise((t,r)=>{ this.rtcp.on("rtcConnected", ()=>{ this.setState("connected"), t() } ), this.rtcp.on("rtcDisconnected", ()=>{ log$i.info("rtc disconnected"), this._state === "connecting" ? (this.setState("disconnected"), r(new InternalError("rtc connect failed"))) : (this.setState("disconnected"), log$i.info("rtc disconnected, start to reconnect"), this.reconnect()) } ), this.socket.on("socketQuit", ()=>{ log$i.info("socket quit success"), this.setState("closed") } ), this.socket.on("socketClosed", n=>{ this._state === "connecting" && (this.setState("disconnected"), r(n)), r(n) } ), this.socket.start() } ) } reconnect() { if (this.room.viewMode === "observer") return; const e = Date.now(); if (this.reconnectCount++, this.reconnectCount > MAX_RECONNECT_COUNT) { log$i.error("reconnect failed, reached max reconnect count", MAX_RECONNECT_COUNT), this.reconnectCount = 0, this.emit("stateChanged", { state: "disconnected" }); return } return log$i.info("start reconnect, count:", this.reconnectCount), this._reconnect().then(()=>{ log$i.infoAndReportMeasurement({ startTime: e, metric: "reconnect" }) } ).catch(t=>{ if (log$i.infoAndReportMeasurement({ startTime: e, metric: "reconnect", error: t }), t.code === Codes$1.RepeatLogin) { this.room.handleRepetLogin(); return } const r = 1e3; log$i.info("reconnect failed, wait " + r + " ms for next reconnect"), setTimeout(()=>{ this.reconnect() } , r) } ) } _reconnect() { return this._state === "closed" ? (log$i.warn("connection closed already"), Promise.reject()) : this._state === "connecting" ? (log$i.warn("connection is already in connecting state"), Promise.reject()) : this._state !== "disconnected" ? Promise.reject() : (this.prepareReconnect(), this._state = "connecting", this.emit("stateChanged", { state: "reconnecting", count: this.reconnectCount }), this.socket.off("gameRoomAvailable"), this.socket.off("socketClosed"), this.rtcp.off("rtcDisconnected"), this.rtcp.off("rtcConnected"), this.connectAndStart(!0).then(({session_id: e})=>{ this.room.updateCurrentNetworkOptions({ sessionId: e }), reporter.updateBody({ serverSession: e }), log$i.info("reconnect success"), this.setState("connected"), this.reconnectCount = 0, this.emit("stateChanged", { state: "reconnected" }) } )) } prepareReconnect() { this.rtcp.disconnect(), this.socket.prepareReconnect(), this.prepareReconnectOptions() } prepareReconnectOptions() { const {camera: e, player: t} = this.room.currentClickingState || {}; e && t && this.room.updateCurrentNetworkOptions({ camera: e, player: t }) } sendRtcData(e) { if (this.blockedActions.includes(e.action_type)) { log$i.info(`action: ${Actions[e.action_type]} was blocked`); return } this.rtcp.sendData(e) } sendSocketData(e) { log$i.debug("ws send ->", e), this.socket.send(e) } quit() { const e = uuid$1() , t = { action_type: Actions.Exit, trace_id: e, exit_action: {}, user_id: this.room.options.userId, packet_id: e }; this.setState("closed"), this.socket.quit(), this.sendRtcData(t) } }