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, }); }