98 lines
3.3 KiB
JavaScript
98 lines
3.3 KiB
JavaScript
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,
|
|
});
|
|
}
|