update:新增比赛服断线重连机制
This commit is contained in:
+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