update:新增比赛服链接

This commit is contained in:
2026-07-08 14:51:35 +08:00
parent 348d490de8
commit 01ac4f9c03
10 changed files with 3160 additions and 326 deletions
+97
View File
@@ -0,0 +1,97 @@
import matchRoot from "./match.min.js";
// 比赛服 protobuf 协议适配层:
// 这里只负责 raw protobuf frame 的解码和客户端消息编码,不处理 UI 状态。
const ServerMessage = matchRoot.lookupType("rpc.ServerMessage");
const ClientMessage = matchRoot.lookupType("rpc.ClientMessage");
// 从反射 Root 中读取枚举,避免直接依赖生成文件内部结构。
function getEnumValues(name) {
return matchRoot.lookupEnum(name).values || {};
}
// protobufjs 的 enum 默认是 name -> value,这里反转成 value -> name 用于日志打印。
function invertEnum(values) {
return Object.keys(values).reduce((result, name) => {
result[values[name]] = name;
return result;
}, {});
}
export const ServerMessageType = getEnumValues("rpc.ServerMessageType");
export const ClientMessageType = getEnumValues("rpc.ClientMessageType");
const ServerMessageTypeNameByValue = invertEnum(ServerMessageType);
// 统一把 int64 转成字符串,避免小程序环境下大整数精度丢失。
const TO_OBJECT_OPTIONS = {
longs: String,
enums: Number,
bytes: Array,
oneofs: true,
};
// 小程序 websocket 收到的 data 可能是 ArrayBuffer、TypedArray 或字符串,这里统一成 Uint8Array。
function toUint8Array(data) {
if (data instanceof Uint8Array) return data;
if (data instanceof ArrayBuffer) return new Uint8Array(data);
if (ArrayBuffer.isView(data)) {
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
}
if (typeof data === "string") {
const bytes = new Uint8Array(data.length);
for (let i = 0; i < data.length; i += 1) {
bytes[i] = data.charCodeAt(i) & 0xff;
}
return bytes;
}
throw new TypeError("Unsupported match websocket message payload");
}
// uni.sendSocketMessage 发送二进制时使用 ArrayBuffer。
function toArrayBuffer(bytes) {
const output = new Uint8Array(bytes.length);
output.set(bytes);
return output.buffer;
}
export function decodeServerMessage(data) {
// 后端已确认比赛服 websocket 下发的是原始 protobuf frame,直接 ServerMessage.decode。
const message = ServerMessage.decode(toUint8Array(data));
return ServerMessage.toObject(message, TO_OBJECT_OPTIONS);
}
export function encodeClientMessage(payload) {
// ClientMessage 字段名按 proto 定义传入,例如 match_id、user_id。
const message = ClientMessage.create(payload);
return toArrayBuffer(ClientMessage.encode(message).finish());
}
export function getServerMessageTypeName(type) {
return ServerMessageTypeNameByValue[type] || String(type);
}
export function createHeartbeatAckMessage() {
// 心跳 ACK 按文档立即回,不等待语音播报。
return encodeClientMessage({
type: ClientMessageType.CLIENT_MSG_HEARTBEAT_ACK,
});
}
export function createAckMessage({ matchId, sequence }) {
// 普通消息 ACK 仍携带 sequence,但前端不做 sequence 补发或排序。
return encodeClientMessage({
type: ClientMessageType.CLIENT_MSG_ACK,
match_id: matchId,
sequence,
});
}
export function createLeaveMessage({ matchId, userId }) {
// 比赛结束或页面离开时通知比赛服释放当前玩家连接。
return encodeClientMessage({
type: ClientMessageType.CLIENT_MSG_LEAVE,
match_id: matchId,
user_id: userId,
});
}