update:新增比赛服断线重连机制
This commit is contained in:
+18
@@ -80,6 +80,10 @@
|
||||
// audioManager.play("射箭声音")
|
||||
}
|
||||
|
||||
function onNetworkStatusChange(status) {
|
||||
matchWebsocket.handleMatchNetworkStatusChange(status);
|
||||
}
|
||||
|
||||
function connectMatchServerFromMessage(content) {
|
||||
const messages = Array.isArray(content) ? content : [content];
|
||||
messages.forEach((message) => {
|
||||
@@ -108,6 +112,17 @@
|
||||
uni.$on("update-online", emitUpdateOnline);
|
||||
uni.$on("session-kicked-out", onSessionKickedOut);
|
||||
uni.$on("device-bind-invalid", onDeviceBindInvalid);
|
||||
if (typeof uni.offNetworkStatusChange === "function") {
|
||||
uni.offNetworkStatusChange(onNetworkStatusChange);
|
||||
}
|
||||
if (typeof uni.onNetworkStatusChange === "function") {
|
||||
uni.onNetworkStatusChange(onNetworkStatusChange);
|
||||
}
|
||||
if (typeof uni.getNetworkType === "function") {
|
||||
uni.getNetworkType({
|
||||
success: onNetworkStatusChange
|
||||
});
|
||||
}
|
||||
const token = uni.getStorageSync(
|
||||
`${uni.getAccountInfoSync().miniProgram.envVersion}_token`
|
||||
);
|
||||
@@ -122,6 +137,9 @@
|
||||
uni.$off("update-online", emitUpdateOnline);
|
||||
uni.$off("session-kicked-out", onSessionKickedOut);
|
||||
uni.$off("device-bind-invalid", onDeviceBindInvalid);
|
||||
if (typeof uni.offNetworkStatusChange === "function") {
|
||||
uni.offNetworkStatusChange(onNetworkStatusChange);
|
||||
}
|
||||
matchWebsocket.closeMatchWebSocket({
|
||||
reason: "app-hide"
|
||||
});
|
||||
|
||||
+230
-10
@@ -24,11 +24,20 @@ let isConnecting = false;
|
||||
let manualClose = false;
|
||||
let audioAckListenerReady = false;
|
||||
let lastReadyRouteKey = "";
|
||||
let reconnectTimer = null;
|
||||
let connectTimeoutTimer = null;
|
||||
let livenessTimer = null;
|
||||
let reconnectAttempt = 0;
|
||||
let networkConnected = true;
|
||||
let lastNetworkType = "";
|
||||
|
||||
// 后端要求非心跳消息必须等前端语音播报完成后再 ACK。
|
||||
const pendingAcks = [];
|
||||
const ACK_AUDIO_TIMEOUT_MS = 9000;
|
||||
const READY_ROUTE_FALLBACK_DELAY_MS = 300;
|
||||
const CONNECT_TIMEOUT_MS = 8000;
|
||||
const LIVENESS_TIMEOUT_MS = 30000;
|
||||
const RECONNECT_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];
|
||||
const ROUND_AUDIO_NAMES = ["一", "二", "三", "四", "五"];
|
||||
const MATCH_STATUS_HALF_REST = 3;
|
||||
|
||||
@@ -47,6 +56,7 @@ const BUSINESS_TYPE_BY_SERVER_TYPE = {
|
||||
|
||||
const MATCH_READY_SNAPSHOT_PREFIX = "match-ready-snapshot:";
|
||||
export const MATCH_WS_AUDIO_ACK_EVENT = "match-ws-audio-ack";
|
||||
export const MATCH_WS_STATE_EVENT = "match-ws-state";
|
||||
|
||||
function normalizeShootData(shootData) {
|
||||
if (!shootData || typeof shootData !== "object") return shootData;
|
||||
@@ -382,6 +392,166 @@ function normalizeServerUrl(serverAddr, token) {
|
||||
return appendQuery(url, "token", token);
|
||||
}
|
||||
|
||||
function emitMatchSocketState(state, extra = {}) {
|
||||
uni.$emit(MATCH_WS_STATE_EVENT, {
|
||||
state,
|
||||
matchId: currentContext?.matchId || "",
|
||||
...extra,
|
||||
});
|
||||
}
|
||||
|
||||
function clearReconnectTimer() {
|
||||
if (!reconnectTimer) return;
|
||||
clearTimeout(reconnectTimer);
|
||||
reconnectTimer = null;
|
||||
}
|
||||
|
||||
function clearConnectTimeout() {
|
||||
if (!connectTimeoutTimer) return;
|
||||
clearTimeout(connectTimeoutTimer);
|
||||
connectTimeoutTimer = null;
|
||||
}
|
||||
|
||||
function clearLivenessTimer() {
|
||||
if (!livenessTimer) return;
|
||||
clearTimeout(livenessTimer);
|
||||
livenessTimer = null;
|
||||
}
|
||||
|
||||
function clearSocketHealthTimers() {
|
||||
clearConnectTimeout();
|
||||
clearLivenessTimer();
|
||||
}
|
||||
|
||||
function closeSocketTask(socketTask, reason) {
|
||||
if (!socketTask) return;
|
||||
try {
|
||||
socketTask.close({ reason });
|
||||
} catch (err) {
|
||||
console.log("[match-ws] close socket task failed", reason, err);
|
||||
}
|
||||
}
|
||||
|
||||
function detachCurrentSocket(reason) {
|
||||
const currentSocket = socket;
|
||||
socket = null;
|
||||
isConnecting = false;
|
||||
clearSocketHealthTimers();
|
||||
closeSocketTask(currentSocket, reason);
|
||||
}
|
||||
|
||||
function scheduleReconnect(reason, { immediate = false } = {}) {
|
||||
if (
|
||||
manualClose ||
|
||||
!currentContext ||
|
||||
!networkConnected ||
|
||||
reconnectTimer ||
|
||||
socket ||
|
||||
isConnecting
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const delay = immediate
|
||||
? 0
|
||||
: RECONNECT_DELAYS_MS[
|
||||
Math.min(reconnectAttempt, RECONNECT_DELAYS_MS.length - 1)
|
||||
];
|
||||
const attempt = reconnectAttempt + 1;
|
||||
reconnectAttempt = attempt;
|
||||
emitMatchSocketState("reconnecting", { reason, delay, attempt });
|
||||
|
||||
reconnectTimer = setTimeout(() => {
|
||||
reconnectTimer = null;
|
||||
if (manualClose || !currentContext || !networkConnected) return;
|
||||
connectMatchWebSocket({
|
||||
...currentContext,
|
||||
force: true,
|
||||
reconnecting: true,
|
||||
reconnectReason: reason,
|
||||
});
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function handleUnexpectedDisconnect(
|
||||
socketTask,
|
||||
reason,
|
||||
detail,
|
||||
{ closeTask = true } = {}
|
||||
) {
|
||||
if (socket !== socketTask) return;
|
||||
|
||||
socket = null;
|
||||
isConnecting = false;
|
||||
clearSocketHealthTimers();
|
||||
if (closeTask) closeSocketTask(socketTask, reason);
|
||||
|
||||
console.log("[match-ws] unexpected disconnect", {
|
||||
reason,
|
||||
detail,
|
||||
context: currentContext,
|
||||
});
|
||||
emitMatchSocketState(networkConnected ? "disconnected" : "offline", {
|
||||
reason,
|
||||
});
|
||||
scheduleReconnect(reason);
|
||||
}
|
||||
|
||||
function startConnectTimeout(socketTask) {
|
||||
clearConnectTimeout();
|
||||
connectTimeoutTimer = setTimeout(() => {
|
||||
handleUnexpectedDisconnect(socketTask, "connect-timeout");
|
||||
}, CONNECT_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
function resetLivenessTimer(socketTask) {
|
||||
clearLivenessTimer();
|
||||
livenessTimer = setTimeout(() => {
|
||||
handleUnexpectedDisconnect(socketTask, "heartbeat-timeout");
|
||||
}, LIVENESS_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
export function forceReconnectMatchWebSocket(reason = "force-reconnect") {
|
||||
if (manualClose || !currentContext) return false;
|
||||
|
||||
clearReconnectTimer();
|
||||
detachCurrentSocket(reason);
|
||||
if (!networkConnected) {
|
||||
emitMatchSocketState("offline", { reason });
|
||||
return false;
|
||||
}
|
||||
|
||||
reconnectAttempt = 0;
|
||||
scheduleReconnect(reason, { immediate: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
export function handleMatchNetworkStatusChange(status = {}) {
|
||||
const previousConnected = networkConnected;
|
||||
const networkType = String(status.networkType || "");
|
||||
const nextConnected =
|
||||
status.isConnected !== false && networkType.toLowerCase() !== "none";
|
||||
const networkTypeChanged =
|
||||
!!lastNetworkType && !!networkType && lastNetworkType !== networkType;
|
||||
|
||||
networkConnected = nextConnected;
|
||||
if (networkType) lastNetworkType = networkType;
|
||||
if (manualClose || !currentContext) return;
|
||||
|
||||
if (!nextConnected) {
|
||||
clearReconnectTimer();
|
||||
detachCurrentSocket("network-offline");
|
||||
emitMatchSocketState("offline", { reason: "network-offline" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!previousConnected || networkTypeChanged) {
|
||||
forceReconnectMatchWebSocket(
|
||||
networkTypeChanged ? "network-type-changed" : "network-recovered"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 统一发送二进制 protobuf 消息,并在日志里保留发送类型。
|
||||
function sendBuffer(buffer, label, clientMessage) {
|
||||
if (!socket || !buffer) return;
|
||||
@@ -395,6 +565,7 @@ function sendBuffer(buffer, label, clientMessage) {
|
||||
fail: (err) => {
|
||||
if (socket !== currentSocket) return;
|
||||
console.log("比赛服 WebSocket 消息发送失败", label, clientMessage, err);
|
||||
handleUnexpectedDisconnect(currentSocket, "send-failed", err);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -582,7 +753,16 @@ function sendLeave() {
|
||||
|
||||
export function connectMatchWebSocket(options = {}) {
|
||||
// 入口参数来自原 websocket 的比赛服地址通知,不影响原有 websocket 连接。
|
||||
const { serverAddr, matchId, userId, token, mode } = options;
|
||||
const {
|
||||
serverAddr,
|
||||
matchId,
|
||||
userId,
|
||||
token,
|
||||
mode,
|
||||
force = false,
|
||||
reconnecting = false,
|
||||
reconnectReason = "",
|
||||
} = options;
|
||||
const url = normalizeServerUrl(serverAddr, token);
|
||||
const normalizedMatchId = normalizeId(matchId);
|
||||
const normalizedUserId = normalizeId(userId);
|
||||
@@ -593,11 +773,10 @@ export function connectMatchWebSocket(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
socket &&
|
||||
const isSameContext =
|
||||
currentContext?.url === url &&
|
||||
currentContext?.matchId === normalizedMatchId
|
||||
) {
|
||||
currentContext?.matchId === normalizedMatchId;
|
||||
if (!force && socket && isSameContext) {
|
||||
// 同一场比赛同一地址重复通知时不重复建连。
|
||||
console.log("[match-ws] already connected or connecting", {
|
||||
matchId: normalizedMatchId,
|
||||
@@ -606,22 +785,38 @@ export function connectMatchWebSocket(options = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force && isSameContext) {
|
||||
// 网络切换或假死重连时只销毁旧链路,保留比赛上下文且不发送 LEAVE。
|
||||
detachCurrentSocket(reconnectReason || "force-reconnect");
|
||||
} else if (!isSameContext) {
|
||||
// 切换比赛服地址时先关闭旧连接,不发送 LEAVE,避免误通知旧比赛离场。
|
||||
closeMatchWebSocket({ sendLeave: false, reason: "switch" });
|
||||
}
|
||||
|
||||
clearReconnectTimer();
|
||||
manualClose = false;
|
||||
isConnecting = true;
|
||||
if (!isSameContext) reconnectAttempt = 0;
|
||||
const connectedOnce = isSameContext && currentContext?.connectedOnce === true;
|
||||
currentContext = {
|
||||
serverAddr,
|
||||
matchId: normalizedMatchId,
|
||||
userId: normalizedUserId,
|
||||
token,
|
||||
url,
|
||||
mode: Number.isFinite(normalizedMode) ? normalizedMode : undefined,
|
||||
isMelee:
|
||||
Number.isFinite(normalizedMode) ? normalizedMode > 3 : undefined,
|
||||
meleeHalfRest: false,
|
||||
meleeHalfRest: isSameContext
|
||||
? currentContext?.meleeHalfRest === true
|
||||
: false,
|
||||
connectedOnce,
|
||||
};
|
||||
ensureAudioAckListener();
|
||||
emitMatchSocketState(reconnecting ? "reconnecting" : "connecting", {
|
||||
reason: reconnectReason,
|
||||
attempt: reconnectAttempt,
|
||||
});
|
||||
|
||||
const socketTask = uni.connectSocket({
|
||||
url,
|
||||
@@ -636,40 +831,60 @@ export function connectMatchWebSocket(options = {}) {
|
||||
socket = null;
|
||||
isConnecting = false;
|
||||
console.log("[match-ws] connect failed", err);
|
||||
clearSocketHealthTimers();
|
||||
emitMatchSocketState(networkConnected ? "disconnected" : "offline", {
|
||||
reason: "connect-failed",
|
||||
});
|
||||
scheduleReconnect("connect-failed");
|
||||
},
|
||||
});
|
||||
|
||||
socket = socketTask;
|
||||
startConnectTimeout(socketTask);
|
||||
|
||||
socketTask.onOpen(() => {
|
||||
if (socket !== socketTask) return;
|
||||
clearConnectTimeout();
|
||||
isConnecting = false;
|
||||
const wasReconnected =
|
||||
currentContext?.connectedOnce === true || reconnecting || reconnectAttempt > 0;
|
||||
if (currentContext) currentContext.connectedOnce = true;
|
||||
reconnectAttempt = 0;
|
||||
resetLivenessTimer(socketTask);
|
||||
console.log("[match-ws] connected", {
|
||||
matchId: normalizedMatchId,
|
||||
url,
|
||||
reconnected: wasReconnected,
|
||||
});
|
||||
emitMatchSocketState("open", {
|
||||
reason: reconnectReason,
|
||||
reconnected: wasReconnected,
|
||||
});
|
||||
});
|
||||
|
||||
socketTask.onMessage((res) => {
|
||||
if (socket !== socketTask) return;
|
||||
resetLivenessTimer(socketTask);
|
||||
handleMessage(res.data);
|
||||
});
|
||||
|
||||
socketTask.onError((err) => {
|
||||
if (socket !== socketTask) return;
|
||||
console.log("[match-ws] socket error", err);
|
||||
handleUnexpectedDisconnect(socketTask, "socket-error", err);
|
||||
});
|
||||
|
||||
socketTask.onClose((result) => {
|
||||
if (socket !== socketTask) return;
|
||||
socket = null;
|
||||
isConnecting = false;
|
||||
console.log("[match-ws] closed", {
|
||||
manualClose,
|
||||
result,
|
||||
context: currentContext,
|
||||
});
|
||||
if (!manualClose) removeAudioAckListener();
|
||||
if (manualClose) return;
|
||||
handleUnexpectedDisconnect(socketTask, "socket-close", result, {
|
||||
closeTask: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -690,6 +905,9 @@ export function closeMatchWebSocket(options = {}) {
|
||||
const { sendLeave: shouldSendLeave = true, reason = "manual" } = options;
|
||||
|
||||
manualClose = true;
|
||||
clearReconnectTimer();
|
||||
clearSocketHealthTimers();
|
||||
reconnectAttempt = 0;
|
||||
pendingAcks.forEach((task) => {
|
||||
if (task?.timer) clearTimeout(task.timer);
|
||||
});
|
||||
@@ -700,7 +918,7 @@ export function closeMatchWebSocket(options = {}) {
|
||||
if (shouldSendLeave) sendLeave();
|
||||
socket = null;
|
||||
try {
|
||||
currentSocket.close();
|
||||
currentSocket.close({ reason });
|
||||
} catch (err) {
|
||||
console.log("[match-ws] close failed", err);
|
||||
}
|
||||
@@ -718,4 +936,6 @@ export default {
|
||||
connectMatchWebSocket,
|
||||
connectMatchWebSocketFromNotice,
|
||||
closeMatchWebSocket,
|
||||
forceReconnectMatchWebSocket,
|
||||
handleMatchNetworkStatusChange,
|
||||
};
|
||||
|
||||
+74
-26
@@ -17,6 +17,7 @@ import {
|
||||
closeMatchWebSocket,
|
||||
connectMatchWebSocket,
|
||||
MATCH_WS_AUDIO_ACK_EVENT,
|
||||
MATCH_WS_STATE_EVENT,
|
||||
} from "@/matchWebsocket";
|
||||
import { MESSAGETYPESV2 } from "@/constants";
|
||||
import { takeMatchReturnSnapshot } from "@/utils/matchReturn";
|
||||
@@ -55,6 +56,7 @@ const showOfflineModal = ref(false);
|
||||
const xRingStreaks = ref({});
|
||||
let battleEnded = false;
|
||||
let skipNextRestoreOnShow = false;
|
||||
let restoreGeneration = 0;
|
||||
|
||||
function clearHalfRestCountdown() {
|
||||
if (halfRestTimer) {
|
||||
@@ -78,13 +80,24 @@ function getHalfRestSeconds(battleInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
const endTime = Number(battleInfo?.halfRestEndTime ?? battleInfo?.restEndTime);
|
||||
if (!Number.isFinite(endTime) || endTime <= 0) return HALF_REST_SECONDS;
|
||||
const timeoutTime = normalizeTimestamp(battleInfo?.timeoutTime);
|
||||
if (timeoutTime) {
|
||||
const elapsedSeconds = Math.max(0, (Date.now() - timeoutTime) / 1000);
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(HALF_REST_SECONDS, Math.ceil(HALF_REST_SECONDS - elapsedSeconds))
|
||||
);
|
||||
}
|
||||
|
||||
const timestamp = endTime < 1e12 ? endTime * 1000 : endTime;
|
||||
const diffSeconds = (timestamp - Date.now()) / 1000;
|
||||
if (diffSeconds > 0 && diffSeconds <= HALF_REST_SECONDS) {
|
||||
return Math.ceil(diffSeconds);
|
||||
const endTime = normalizeTimestamp(
|
||||
battleInfo?.halfRestEndTime ?? battleInfo?.restEndTime
|
||||
);
|
||||
if (endTime) {
|
||||
const diffSeconds = (endTime - Date.now()) / 1000;
|
||||
return Math.max(
|
||||
0,
|
||||
Math.min(HALF_REST_SECONDS, Math.ceil(diffSeconds))
|
||||
);
|
||||
}
|
||||
|
||||
return HALF_REST_SECONDS;
|
||||
@@ -92,7 +105,14 @@ function getHalfRestSeconds(battleInfo) {
|
||||
|
||||
function startHalfRestCountdown(seconds = HALF_REST_SECONDS) {
|
||||
clearHalfRestCountdown();
|
||||
halfRestRemain.value = Math.max(0, Math.ceil(Number(seconds) || HALF_REST_SECONDS));
|
||||
const secondsValue = Number(seconds);
|
||||
const normalizedSeconds = Number.isFinite(secondsValue)
|
||||
? secondsValue
|
||||
: HALF_REST_SECONDS;
|
||||
halfRestRemain.value = Math.max(
|
||||
0,
|
||||
Math.min(HALF_REST_SECONDS, Math.ceil(normalizedSeconds))
|
||||
);
|
||||
|
||||
if (halfRestRemain.value <= 0) return;
|
||||
|
||||
@@ -305,6 +325,47 @@ function recoverData(battleInfo, { force = false } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
async function restoreLatestBattle() {
|
||||
if (!battleId.value) return;
|
||||
|
||||
const currentRestoreId = ++restoreGeneration;
|
||||
let result = null;
|
||||
try {
|
||||
result = normalizeBattleInfo(await getBattleAPI(battleId.value));
|
||||
} catch (err) {
|
||||
console.log("restore latest melee battle failed:", err);
|
||||
return;
|
||||
}
|
||||
if (currentRestoreId !== restoreGeneration || !result) return;
|
||||
|
||||
if (result.status === 2) {
|
||||
battleEnded = true;
|
||||
uni.showToast({
|
||||
title: "比赛已结束",
|
||||
icon: "none",
|
||||
});
|
||||
uni.navigateBack({
|
||||
delta: 2,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
reconnectMatchServer(result);
|
||||
recoverData(result, { force: true });
|
||||
}
|
||||
|
||||
function onMatchSocketState(event) {
|
||||
if (event?.state !== "open" || event?.reconnected !== true) return;
|
||||
if (
|
||||
event.matchId &&
|
||||
battleId.value &&
|
||||
String(event.matchId) !== String(battleId.value)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
restoreLatestBattle();
|
||||
}
|
||||
|
||||
onLoad(async (options) => {
|
||||
const returnSnapshot = options.fromReturn ? takeMatchReturnSnapshot() : null;
|
||||
skipNextRestoreOnShow = false;
|
||||
@@ -392,7 +453,7 @@ async function onReceiveMessage(msg) {
|
||||
halfTimeTip.value = true;
|
||||
halfRest.value = true;
|
||||
tips.value = "准备下半场";
|
||||
startHalfRestCountdown();
|
||||
startHalfRestCountdown(getHalfRestSeconds(msg));
|
||||
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
||||
battleEnded = true;
|
||||
notifyMatchAudioAck(msg);
|
||||
@@ -409,14 +470,17 @@ onMounted(async () => {
|
||||
keepScreenOn: true,
|
||||
});
|
||||
uni.$on("socket-inbox", onReceiveMessage);
|
||||
uni.$on(MATCH_WS_STATE_EVENT, onMatchSocketState);
|
||||
await laserCloseAPI();
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
uni.setKeepScreenOn({
|
||||
keepScreenOn: false,
|
||||
});
|
||||
restoreGeneration += 1;
|
||||
clearHalfRestCountdown();
|
||||
uni.$off("socket-inbox", onReceiveMessage);
|
||||
uni.$off(MATCH_WS_STATE_EVENT, onMatchSocketState);
|
||||
closeBattleServer("melee-battle-unmount");
|
||||
audioManager.stopAll();
|
||||
});
|
||||
@@ -425,28 +489,12 @@ onHide(() => {
|
||||
closeBattleServer("melee-battle-hide");
|
||||
});
|
||||
|
||||
onShow(async () => {
|
||||
onShow(() => {
|
||||
if (skipNextRestoreOnShow) {
|
||||
skipNextRestoreOnShow = false;
|
||||
return;
|
||||
}
|
||||
if (battleId.value) {
|
||||
const result = normalizeBattleInfo(await getBattleAPI(battleId.value));
|
||||
if (!result) return;
|
||||
if (result.status === 2) {
|
||||
battleEnded = true;
|
||||
uni.showToast({
|
||||
title: "比赛已结束",
|
||||
icon: "none",
|
||||
});
|
||||
uni.navigateBack({
|
||||
delta: 2,
|
||||
});
|
||||
} else {
|
||||
reconnectMatchServer(result);
|
||||
recoverData(result, { force: true });
|
||||
}
|
||||
}
|
||||
restoreLatestBattle();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
closeMatchWebSocket,
|
||||
connectMatchWebSocket,
|
||||
MATCH_WS_AUDIO_ACK_EVENT,
|
||||
MATCH_WS_STATE_EVENT,
|
||||
} from "@/matchWebsocket";
|
||||
import { MESSAGETYPESV2 } from "@/constants";
|
||||
import { getDirectionText } from "@/util";
|
||||
@@ -1227,6 +1228,18 @@ function scheduleRestoreLatestBattle() {
|
||||
}, RESTORE_EMPTY_ID_RETRY_DELAY);
|
||||
}
|
||||
|
||||
function onMatchSocketState(event) {
|
||||
if (event?.state !== "open" || event?.reconnected !== true) return;
|
||||
if (
|
||||
event.matchId &&
|
||||
battleId.value &&
|
||||
String(event.matchId) !== String(battleId.value)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
scheduleRestoreLatestBattle();
|
||||
}
|
||||
|
||||
// 页面统一的 socket 回调,只负责把消息送进战况队列。
|
||||
function onReceiveMessage(message) {
|
||||
enqueueBattleMessage(message);
|
||||
@@ -1302,6 +1315,7 @@ onMounted(async () => {
|
||||
uni.$on(AUDIO_INTERRUPTION_END_EVENT, handleBattleRecovered);
|
||||
uni.$on(PROGRESS_ZERO_EVENT, onProgressZero);
|
||||
uni.$on(COUNTDOWN_READY_EVENT, hideRestoreLoading);
|
||||
uni.$on(MATCH_WS_STATE_EVENT, onMatchSocketState);
|
||||
await laserCloseAPI();
|
||||
});
|
||||
|
||||
@@ -1314,6 +1328,7 @@ onBeforeUnmount(() => {
|
||||
uni.$off("audioEnded", onAudioEnded);
|
||||
uni.$off(PROGRESS_ZERO_EVENT, onProgressZero);
|
||||
uni.$off(COUNTDOWN_READY_EVENT, hideRestoreLoading);
|
||||
uni.$off(MATCH_WS_STATE_EVENT, onMatchSocketState);
|
||||
if (pendingRestoreTimer) {
|
||||
clearTimeout(pendingRestoreTimer);
|
||||
pendingRestoreTimer = null;
|
||||
|
||||
Reference in New Issue
Block a user