update:新增比赛服链接
This commit is contained in:
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable */
|
||||
import * as $protobuf from "protobufjs";
|
||||
|
||||
if (typeof $protobuf.Root.create !== "function") {
|
||||
const isObject = (value) => value && typeof value === "object" && !Array.isArray(value);
|
||||
|
||||
const normalizeField = (field) => {
|
||||
if (!isObject(field)) return field;
|
||||
const next = { ...field };
|
||||
if (next.keytype && !next.keyType) {
|
||||
next.keyType = next.keytype;
|
||||
delete next.keytype;
|
||||
}
|
||||
delete next.oneof;
|
||||
return next;
|
||||
};
|
||||
|
||||
const normalizeType = (json) => {
|
||||
const next = { ...json };
|
||||
if (next.fields) {
|
||||
const oneofs = { ...(next.oneofs || {}) };
|
||||
next.fields = Object.keys(next.fields).reduce((fields, name) => {
|
||||
const field = next.fields[name];
|
||||
fields[name] = normalizeField(field);
|
||||
if (field && field.oneof) {
|
||||
if (!oneofs[field.oneof]) oneofs[field.oneof] = { oneof: [] };
|
||||
oneofs[field.oneof].oneof.push(name);
|
||||
}
|
||||
return fields;
|
||||
}, {});
|
||||
if (Object.keys(oneofs).length) next.oneofs = oneofs;
|
||||
}
|
||||
if (next.nested) next.nested = normalizeNested(next.nested);
|
||||
return next;
|
||||
};
|
||||
|
||||
const normalizeDescriptor = (json) => {
|
||||
if (!isObject(json)) return json;
|
||||
if (json.fields) return normalizeType(json);
|
||||
if (json.values || json.methods || json.id !== undefined) return json;
|
||||
if (Object.keys(json).every((key) => typeof json[key] === "number")) {
|
||||
return { values: json };
|
||||
}
|
||||
if (json.nested) return { ...json, nested: normalizeNested(json.nested) };
|
||||
return { nested: normalizeNested(json) };
|
||||
};
|
||||
|
||||
const normalizeNested = (nested) =>
|
||||
Object.keys(nested || {}).reduce((result, name) => {
|
||||
result[name] = normalizeDescriptor(nested[name]);
|
||||
return result;
|
||||
}, {});
|
||||
|
||||
$protobuf.Root.create = (json) => $protobuf.Root.fromJSON(normalizeDescriptor(json));
|
||||
}
|
||||
const $root=$protobuf.Root.create({nested:{rpc:{ServerMessageType:{SERVER_MSG_UNKNOWN:0,SERVER_MSG_MATCH_READY:1,SERVER_MSG_MATCH_START:2,SERVER_MSG_NOW_YOU:3,SERVER_MSG_SHOT:4,SERVER_MSG_NEW_ROUND:5,SERVER_MSG_MATCH_END:6,SERVER_MSG_TIMEOUT:7,SERVER_MSG_CHECK:8,SERVER_MSG_RAND:9,SERVER_MSG_NOT_ENOUGH_DISTANCE:10,SERVER_MSG_PLAYER_LEFT:11,SERVER_MSG_HEARTBEAT:12,SERVER_MSG_PRACTICE_END:13},ClientMessageType:{CLIENT_MSG_UNKNOWN:0,CLIENT_MSG_HEARTBEAT_ACK:1,CLIENT_MSG_SHOOT_DATA:2,CLIENT_MSG_ACK:3,CLIENT_MSG_LEAVE:4},MatchStatus:{MATCH_STATUS_READY:0,MATCH_STATUS_STARTED:1,MATCH_STATUS_END:2,MATCH_STATUS_TIMEOUT:3,MATCH_STATUS_UNEXPECTEDLY:4},MatchPlayerStatus:{MATCH_PLAYER_STATUS_READY:0,MATCH_PLAYER_STATUS_STARTED:1,MATCH_PLAYER_STATUS_END:2},MatchShoot:{fields:{player_id:{type:"int64",id:1},status:{type:"int32",id:2},x:{type:"float",id:3},y:{type:"float",id:4},ring:{type:"int32",id:5},ring_x:{type:"bool",id:6},angle:{type:"float",id:7},distance:{type:"float",id:8}}},MatchShootList:{fields:{items:{rule:"repeated",type:"MatchShoot",id:1}}},RoundScore:{fields:{total_ring:{type:"int32",id:1},score:{type:"int32",id:2},if_win:{type:"bool",id:3}}},MatchRound:{fields:{shoots:{rule:"map",type:"MatchShootList",id:1,keytype:"int64"},scores:{rule:"map",type:"RoundScore",id:2,keytype:"int32"},round:{type:"int32",id:3},if_gold:{type:"bool",id:4},status:{type:"int32",id:5},gold_round:{type:"int32",id:6}}},PlayerMatchResult:{fields:{total_ring:{type:"int32",id:1},user_id:{type:"int64",id:2},ten_ring_count:{type:"int32",id:3},average_ring:{type:"float",id:4}}},PlayerFull:{fields:{id:{type:"int64",id:1},name:{type:"string",id:2},avatar:{type:"string",id:3},exp:{type:"int32",id:4},score:{type:"int32",id:5},level:{type:"int32",id:6},before_level:{type:"int32",id:7},before_exp:{type:"int32",id:8},current_exp:{type:"int32",id:9},upgrade_exp:{type:"int32",id:10},active:{type:"int32",id:11},device_id:{type:"string",id:12},s_vip:{type:"bool",id:13},vip:{type:"bool",id:14},player_match_result:{type:"PlayerMatchResult",id:15}}},TeamInfo:{fields:{players:{rule:"repeated",type:"PlayerFull",id:1},id:{type:"int32",id:2},name:{type:"string",id:3},score:{type:"int32",id:4}}},CurrentShoot:{fields:{round:{type:"int32",id:1},round_id:{type:"int64",id:2},index:{type:"int32",id:3},start_time:{type:"int64",id:4},player_id:{type:"int64",id:5},gold_round:{type:"bool",id:6},start_time_text:{type:"string",id:7},my_index:{type:"int32",id:8},index_map:{rule:"map",type:"int32",id:9,keytype:"int64"}}},ShootData:{fields:{x:{type:"float",id:1},y:{type:"float",id:2},r:{type:"float",id:3},dst:{type:"float",id:4},m:{type:"string",id:5},adc:{type:"float",id:6},device_id:{type:"string",id:7},shoot_id:{type:"string",id:8}}},PracticeInfo:{fields:{id:{type:"int64",id:1},user_id:{type:"int64",id:2},status:{type:"int32",id:3},status_text:{type:"string",id:4},start_time:{type:"int64",id:5},target_type:{type:"int32",id:6},vip:{type:"bool",id:7},s_vip:{type:"bool",id:8},device_id:{type:"string",id:9},shoot_data:{type:"MatchShoot",id:10},details:{rule:"repeated",type:"MatchShoot",id:11}}},MatchInfo:{fields:{match_id:{type:"int64",id:1},create_time:{type:"int64",id:2},start_time:{type:"int64",id:3},server_time:{type:"int64",id:4},shoot_time:{type:"int32",id:5},shoot_number:{type:"int32",id:6},ready_time:{type:"int32",id:7},way:{type:"int32",id:8},mode:{type:"int32",id:9},status:{type:"MatchStatus",id:10},status_text:{type:"string",id:11},rounds:{rule:"repeated",type:"MatchRound",id:12},teams:{rule:"map",type:"TeamInfo",id:13,keytype:"int32"},current:{type:"CurrentShoot",id:14},next:{type:"CurrentShoot",id:15},shoot_data:{type:"MatchShoot",id:16},win_team:{type:"int32",id:17},mvp:{type:"PlayerFull",id:18},room_id:{type:"string",id:19},result_list:{rule:"repeated",type:"PlayerMatchResult",id:20},timeout_time:{type:"int64",id:21},target_type:{type:"int32",id:22},event_type:{type:"int32",id:23},timeout:{type:"int32",id:24},server_addr:{type:"string",id:25}}},ServerMessage:{fields:{type:{type:"ServerMessageType",id:1},match_id:{type:"int64",id:2},timestamp:{type:"int64",id:3},match_info:{type:"MatchInfo",id:4,oneof:"payload"},shoot_data:{type:"ShootData",id:5,oneof:"payload"},practice_info:{type:"PracticeInfo",id:6,oneof:"payload"},sequence:{type:"int64",id:7}}},ClientMessage:{fields:{type:{type:"ClientMessageType",id:1},match_id:{type:"int64",id:2},user_id:{type:"int64",id:3},sequence:{type:"int64",id:4},data:{type:"bytes",id:5}}}}}});
|
||||
export default $root;
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user