update:修复日志和防抖
This commit is contained in:
@@ -36,6 +36,7 @@ const createRoom = debounce(async () => {
|
|||||||
}
|
}
|
||||||
if (loading.value === true) return;
|
if (loading.value === true) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
let keepLoading = false;
|
||||||
let size = 2;
|
let size = 2;
|
||||||
if (battleMode.value === 2) size = 10;
|
if (battleMode.value === 2) size = 10;
|
||||||
if (battleMode.value === 3) size = 4;
|
if (battleMode.value === 3) size = 4;
|
||||||
@@ -49,14 +50,18 @@ const createRoom = debounce(async () => {
|
|||||||
if (result.number) {
|
if (result.number) {
|
||||||
props.onConfirm();
|
props.onConfirm();
|
||||||
await joinRoomAPI(result.number);
|
await joinRoomAPI(result.number);
|
||||||
|
keepLoading = true;
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/battle-room?roomNumber=" + result.number + "&target=" + targetMode.value,
|
url: "/pages/battle-room?roomNumber=" + result.number + "&target=" + targetMode.value,
|
||||||
|
fail: () => {
|
||||||
|
loading.value = false;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
if (!keepLoading) loading.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -112,7 +117,7 @@ const createRoom = debounce(async () => {
|
|||||||
<text>40厘米全环靶</text>
|
<text>40厘米全环靶</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<SButton :onClick="() => $clickSound(createRoom)">创建房间</SButton>
|
<SButton :disabled="loading" :onClick="() => { $clickSound(); return createRoom(); }">创建房间</SButton>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
+41
-14
@@ -159,7 +159,6 @@ function emitBusinessMessage(message) {
|
|||||||
scheduleReadyRouteFallback(message);
|
scheduleReadyRouteFallback(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[match-ws] emit socket-inbox", message.matchWsTypeName, message);
|
|
||||||
uni.$emit("socket-inbox", message);
|
uni.$emit("socket-inbox", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +222,13 @@ function getAckAudioKeys(message, businessMessage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldCloseAfterAck(message) {
|
||||||
|
return (
|
||||||
|
message.type === ServerMessageType.SERVER_MSG_MATCH_END ||
|
||||||
|
message.type === ServerMessageType.SERVER_MSG_TIMEOUT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 追加 token 查询参数;如果后端地址已经带 token,则保持原样。
|
// 追加 token 查询参数;如果后端地址已经带 token,则保持原样。
|
||||||
function appendQuery(url, key, value) {
|
function appendQuery(url, key, value) {
|
||||||
if (!value || new RegExp(`[?&]${key}=`).test(url)) return url;
|
if (!value || new RegExp(`[?&]${key}=`).test(url)) return url;
|
||||||
@@ -251,32 +257,47 @@ function normalizeServerUrl(serverAddr, token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 统一发送二进制 protobuf 消息,并在日志里保留发送类型。
|
// 统一发送二进制 protobuf 消息,并在日志里保留发送类型。
|
||||||
function sendBuffer(buffer, label) {
|
function sendBuffer(buffer, label, clientMessage) {
|
||||||
if (!socket || !buffer) return;
|
if (!socket || !buffer) return;
|
||||||
|
|
||||||
const currentSocket = socket;
|
const currentSocket = socket;
|
||||||
currentSocket.send({
|
currentSocket.send({
|
||||||
data: buffer,
|
data: buffer,
|
||||||
success: () => {
|
success: () => {
|
||||||
if (label === "heartbeat ack") return;
|
console.log("发送比赛服 WebSocket 消息", label, clientMessage);
|
||||||
console.log(`[match-ws] ${label} sent`, new Date());
|
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
if (socket !== currentSocket) return;
|
if (socket !== currentSocket) return;
|
||||||
console.log(`[match-ws] ${label} send failed`, err);
|
console.log("比赛服 WebSocket 消息发送失败", label, clientMessage, err);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendHeartbeatAck() {
|
function sendHeartbeatAck() {
|
||||||
// 心跳 ACK 不进入 pendingAcks,收到后立即回复。
|
// 心跳 ACK 不进入 pendingAcks,收到后立即回复。
|
||||||
sendBuffer(createHeartbeatAckMessage(), "heartbeat ack");
|
const clientMessage = {
|
||||||
|
type: ClientMessageType.CLIENT_MSG_HEARTBEAT_ACK,
|
||||||
|
};
|
||||||
|
sendBuffer(
|
||||||
|
createHeartbeatAckMessage(),
|
||||||
|
"CLIENT_MSG_HEARTBEAT_ACK",
|
||||||
|
clientMessage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendAck({ matchId, sequence }) {
|
function sendAck({ matchId, sequence }) {
|
||||||
// sequence 由后端处理,前端只原样带回,不做重排和断线补发。
|
// sequence 由后端处理,前端只原样带回,不做重排和断线补发。
|
||||||
if (sequence === undefined || sequence === null || sequence === "") return;
|
if (sequence === undefined || sequence === null || sequence === "") return;
|
||||||
sendBuffer(createAckMessage({ matchId, sequence }), `ack ${sequence}`);
|
const clientMessage = {
|
||||||
|
type: ClientMessageType.CLIENT_MSG_ACK,
|
||||||
|
match_id: matchId,
|
||||||
|
sequence,
|
||||||
|
};
|
||||||
|
sendBuffer(
|
||||||
|
createAckMessage({ matchId, sequence }),
|
||||||
|
"CLIENT_MSG_ACK",
|
||||||
|
clientMessage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function completeAckTask(task) {
|
function completeAckTask(task) {
|
||||||
@@ -348,7 +369,7 @@ function queueAckAfterAudio(message, businessMessage) {
|
|||||||
pickField(message, "matchId", "match_id") || currentContext?.matchId
|
pickField(message, "matchId", "match_id") || currentContext?.matchId
|
||||||
),
|
),
|
||||||
sequence: message.sequence,
|
sequence: message.sequence,
|
||||||
leaveAfterAck: message.type === ServerMessageType.SERVER_MSG_MATCH_END,
|
leaveAfterAck: shouldCloseAfterAck(message),
|
||||||
};
|
};
|
||||||
|
|
||||||
const audioKeys = getAckAudioKeys(message, businessMessage).filter(Boolean);
|
const audioKeys = getAckAudioKeys(message, businessMessage).filter(Boolean);
|
||||||
@@ -394,9 +415,6 @@ function handleMessage(data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeName = getServerMessageTypeName(message.type);
|
|
||||||
console.log("[match-ws] message decoded", typeName, message);
|
|
||||||
|
|
||||||
const decodedMatchId = normalizeId(pickField(message, "matchId", "match_id"));
|
const decodedMatchId = normalizeId(pickField(message, "matchId", "match_id"));
|
||||||
if (decodedMatchId && currentContext) {
|
if (decodedMatchId && currentContext) {
|
||||||
currentContext.matchId = decodedMatchId;
|
currentContext.matchId = decodedMatchId;
|
||||||
@@ -407,6 +425,9 @@ function handleMessage(data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeName = getServerMessageTypeName(message.type);
|
||||||
|
console.log("收到比赛服 WebSocket 消息", typeName, message);
|
||||||
|
|
||||||
const businessMessage = buildBusinessMessage(message);
|
const businessMessage = buildBusinessMessage(message);
|
||||||
if (businessMessage?.matchId && currentContext) {
|
if (businessMessage?.matchId && currentContext) {
|
||||||
currentContext.matchId = businessMessage.matchId;
|
currentContext.matchId = businessMessage.matchId;
|
||||||
@@ -418,12 +439,18 @@ function handleMessage(data) {
|
|||||||
function sendLeave() {
|
function sendLeave() {
|
||||||
// 主动关闭、比赛结束、页面离开时发 CLIENT_MSG_LEAVE。
|
// 主动关闭、比赛结束、页面离开时发 CLIENT_MSG_LEAVE。
|
||||||
if (!socket || !currentContext?.matchId) return;
|
if (!socket || !currentContext?.matchId) return;
|
||||||
|
const clientMessage = {
|
||||||
|
type: ClientMessageType.CLIENT_MSG_LEAVE,
|
||||||
|
match_id: currentContext.matchId,
|
||||||
|
user_id: currentContext.userId,
|
||||||
|
};
|
||||||
sendBuffer(
|
sendBuffer(
|
||||||
createLeaveMessage({
|
createLeaveMessage({
|
||||||
matchId: currentContext.matchId,
|
matchId: clientMessage.match_id,
|
||||||
userId: currentContext.userId,
|
userId: clientMessage.user_id,
|
||||||
}),
|
}),
|
||||||
"leave"
|
"CLIENT_MSG_LEAVE",
|
||||||
|
clientMessage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
getReadyAPI,
|
getReadyAPI,
|
||||||
kickPlayerAPI,
|
kickPlayerAPI,
|
||||||
} from "@/apis";
|
} from "@/apis";
|
||||||
import { isLimitError } from "@/util";
|
import { debounce, isLimitError } from "@/util";
|
||||||
import { MESSAGETYPES, MESSAGETYPESV2 } from "@/constants";
|
import { MESSAGETYPES, MESSAGETYPESV2 } from "@/constants";
|
||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
@@ -59,6 +59,7 @@ const allReady = ref(false);
|
|||||||
const timer = ref(null);
|
const timer = ref(null);
|
||||||
const goBattle = ref(false);
|
const goBattle = ref(false);
|
||||||
const showLimitModal = ref(false);
|
const showLimitModal = ref(false);
|
||||||
|
const readySubmitting = ref(false);
|
||||||
/** 从结算页返回时为 true,跳过进场靶纸语音 */
|
/** 从结算页返回时为 true,跳过进场靶纸语音 */
|
||||||
const skipTargetAudio = ref(false);
|
const skipTargetAudio = ref(false);
|
||||||
|
|
||||||
@@ -139,17 +140,22 @@ async function refreshRoomData() {
|
|||||||
// timer.value = setTimeout(refreshRoomData, 2000);
|
// timer.value = setTimeout(refreshRoomData, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getReady = async () => {
|
const getReady = debounce(async () => {
|
||||||
|
if (readySubmitting.value) return;
|
||||||
|
readySubmitting.value = true;
|
||||||
try {
|
try {
|
||||||
await getReadyAPI(roomNumber.value);
|
await getReadyAPI(roomNumber.value);
|
||||||
|
ready.value = true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isLimitError(error)) {
|
if (isLimitError(error)) {
|
||||||
showLimitModal.value = true;
|
showLimitModal.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("room ready error", error);
|
console.log("room ready error", error);
|
||||||
|
} finally {
|
||||||
|
readySubmitting.value = false;
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
const closeLimitModal = () => {
|
const closeLimitModal = () => {
|
||||||
showLimitModal.value = false;
|
showLimitModal.value = false;
|
||||||
@@ -465,7 +471,10 @@ onBeforeUnmount(() => {
|
|||||||
<PlayerSeats v-if="room.battleType === 2" :total="room.count || 10" :players="players"
|
<PlayerSeats v-if="room.battleType === 2" :total="room.count || 10" :players="players"
|
||||||
:removePlayer="removePlayer" :isOwner="owner.id === user.id" />
|
:removePlayer="removePlayer" :isOwner="owner.id === user.id" />
|
||||||
<view>
|
<view>
|
||||||
<SButton :disabled="!canClick" :onClick="() => $clickSound(getReady)">
|
<SButton
|
||||||
|
:disabled="!canClick || readySubmitting"
|
||||||
|
:onClick="() => { $clickSound(); return getReady(); }"
|
||||||
|
>
|
||||||
{{
|
{{
|
||||||
allReady.value
|
allReady.value
|
||||||
? "即将进入对局..."
|
? "即将进入对局..."
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const data = ref({});
|
|||||||
const roomID = ref("");
|
const roomID = ref("");
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const showLimitModal = ref(false);
|
const showLimitModal = ref(false);
|
||||||
|
const createRoomLoading = ref(false);
|
||||||
const isSVip = computed(() => user.value.sVip === true);
|
const isSVip = computed(() => user.value.sVip === true);
|
||||||
const isVip = computed(() => user.value.vip === true && !isSVip.value);
|
const isVip = computed(() => user.value.vip === true && !isSVip.value);
|
||||||
const challengeLimitText = computed(() =>
|
const challengeLimitText = computed(() =>
|
||||||
@@ -45,6 +46,7 @@ const enterRoom = debounce(async (number) => {
|
|||||||
showModal.value = true;
|
showModal.value = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const room = await getRoomAPI(number);
|
const room = await getRoomAPI(number);
|
||||||
if (!room.number) {
|
if (!room.number) {
|
||||||
@@ -63,7 +65,6 @@ const enterRoom = debounce(async (number) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loading.value = true;
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/battle-room?roomNumber=" + number,
|
url: "/pages/battle-room?roomNumber=" + number,
|
||||||
});
|
});
|
||||||
@@ -71,8 +72,11 @@ const enterRoom = debounce(async (number) => {
|
|||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const onCreateRoom = async () => {
|
const onCreateRoom = debounce(async () => {
|
||||||
|
if (createRoomLoading.value) return;
|
||||||
if (!canEenter(user.value, device.value, online.value)) return;
|
if (!canEenter(user.value, device.value, online.value)) return;
|
||||||
|
createRoomLoading.value = true;
|
||||||
|
try {
|
||||||
const countData = await loadDailyCount();
|
const countData = await loadDailyCount();
|
||||||
if (isLimitReached(countData.challenge)) {
|
if (isLimitReached(countData.challenge)) {
|
||||||
showLimitModal.value = true;
|
showLimitModal.value = true;
|
||||||
@@ -80,7 +84,10 @@ const onCreateRoom = async () => {
|
|||||||
}
|
}
|
||||||
warnning.value = "";
|
warnning.value = "";
|
||||||
showModal.value = true;
|
showModal.value = true;
|
||||||
};
|
} finally {
|
||||||
|
createRoomLoading.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const closeLimitModal = () => {
|
const closeLimitModal = () => {
|
||||||
showLimitModal.value = false;
|
showLimitModal.value = false;
|
||||||
@@ -208,7 +215,12 @@ onLoad(async (options) => {
|
|||||||
<view v-if="challengeLimitText" class="pp-text">
|
<view v-if="challengeLimitText" class="pp-text">
|
||||||
{{ challengeLimitText }}
|
{{ challengeLimitText }}
|
||||||
</view>
|
</view>
|
||||||
<SButton width="80%" :rounded="30" :onClick="() => $clickSound(onCreateRoom)">
|
<SButton
|
||||||
|
width="80%"
|
||||||
|
:rounded="30"
|
||||||
|
:disabled="createRoomLoading"
|
||||||
|
:onClick="() => { $clickSound(); return onCreateRoom(); }"
|
||||||
|
>
|
||||||
创建约战房
|
创建约战房
|
||||||
</SButton>
|
</SButton>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import TestDistance from "@/components/TestDistance.vue";
|
|||||||
import SModal from "@/components/SModal.vue";
|
import SModal from "@/components/SModal.vue";
|
||||||
import audioManager from "@/audioManager";
|
import audioManager from "@/audioManager";
|
||||||
import { getBattleAPI, laserCloseAPI } from "@/apis";
|
import { getBattleAPI, laserCloseAPI } from "@/apis";
|
||||||
import { closeMatchWebSocket, connectMatchWebSocket } from "@/matchWebsocket";
|
import {
|
||||||
|
closeMatchWebSocket,
|
||||||
|
connectMatchWebSocket,
|
||||||
|
MATCH_WS_AUDIO_ACK_EVENT,
|
||||||
|
} from "@/matchWebsocket";
|
||||||
import { MESSAGETYPESV2 } from "@/constants";
|
import { MESSAGETYPESV2 } from "@/constants";
|
||||||
import { takeMatchReturnSnapshot } from "@/utils/matchReturn";
|
import { takeMatchReturnSnapshot } from "@/utils/matchReturn";
|
||||||
import useStore from "@/store";
|
import useStore from "@/store";
|
||||||
@@ -172,6 +176,23 @@ function closeBattleServer(reason) {
|
|||||||
closeMatchWebSocket({ reason });
|
closeMatchWebSocket({ reason });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifyMatchAudioAck(message) {
|
||||||
|
if (
|
||||||
|
message?.sequence === undefined ||
|
||||||
|
message?.sequence === null ||
|
||||||
|
message?.sequence === ""
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.$emit(MATCH_WS_AUDIO_ACK_EVENT, {
|
||||||
|
matchId: message.matchId || battleId.value,
|
||||||
|
sequence: message.sequence,
|
||||||
|
matchWsType: message.matchWsType,
|
||||||
|
matchWsTypeName: message.matchWsTypeName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听设备在线状态,大乱斗比赛进行中设备离线时弹窗提示用户
|
* 监听设备在线状态,大乱斗比赛进行中设备离线时弹窗提示用户
|
||||||
*/
|
*/
|
||||||
@@ -346,6 +367,7 @@ async function onReceiveMessage(msg) {
|
|||||||
startHalfRestCountdown();
|
startHalfRestCountdown();
|
||||||
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
||||||
battleEnded = true;
|
battleEnded = true;
|
||||||
|
notifyMatchAudioAck(msg);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 全部跳转到新结算页
|
// 全部跳转到新结算页
|
||||||
uni.redirectTo({
|
uni.redirectTo({
|
||||||
|
|||||||
Reference in New Issue
Block a user