// Proto.js - 游戏协议处理 // 基于 proto/action.proto 和 proto/define.proto 生成 // 协议定义(将由本地 .proto 文件动态填充枚举值) const GameProto = {}; // 协议编码器 class ProtoEncoder { constructor() { this.protobuf = window.protobuf; this.root = null; this.readyPromise = this.initProtobuf(); } async initProtobuf() { try { // 使用本地 .proto 文件,避免硬编码 const root = await this.protobuf.load([ "proto/define.proto", "proto/action.proto" ]); this.root = root; // 缓存枚举,供其他模块使用 this.MessageID = this.root.lookupEnum('MessageID').values; this.ActionID = this.root.lookupEnum('ActionID').values; // 更新全局GameProto枚举(避免硬编码) GameProto.MessageID = this.MessageID; GameProto.ActionID = this.ActionID; utils.info('Protobuf loaded from local files successfully'); } catch (error) { utils.error('Failed to load protobuf files:', error); } } // 编码消息 encodeMessage(messageId, payload) { try { if (!this.root) { throw new Error('Protobuf not initialized'); } const Message = this.root.lookupType('Message'); const message = { ID: messageId, Payload: payload }; const buffer = Message.encode(message).finish(); return buffer; } catch (error) { utils.error('Failed to encode message:', error); return null; } } // 编码动作 encodeAction(actionId, payload) { try { if (!this.root) { throw new Error('Protobuf not initialized'); } const C2S_Action = this.root.lookupType('C2S_Action'); const action = { Action: actionId, Payload: payload }; const buffer = C2S_Action.encode(action).finish(); return buffer; } catch (error) { utils.error('Failed to encode action:', error); return null; } } // 解码消息 decodeMessage(buffer) { try { if (!this.root) { throw new Error('Protobuf not initialized'); } const Message = this.root.lookupType('Message'); const message = Message.decode(buffer); return message; } catch (error) { utils.error('Failed to decode message:', error); return null; } } // 解码动作 decodeAction(buffer) { try { if (!this.root) { throw new Error('Protobuf not initialized'); } const C2S_Action = this.root.lookupType('C2S_Action'); const action = C2S_Action.decode(buffer); return action; } catch (error) { utils.error('Failed to decode action:', error); return null; } } } // 协议工具类 class ProtoUtils { constructor() { this.encoder = new ProtoEncoder(); this.readyPromise = this.encoder.readyPromise; } // 创建移动指令 createMoveAction(x, y) { const moveData = new Uint8Array([x, y]); return this.encoder.encodeAction(this.encoder.ActionID.ACTION_ID_MOVE, moveData); } // 创建攻击指令 createAttackAction(targetId) { const attackData = new Uint8Array([targetId]); return this.encoder.encodeAction(this.encoder.ActionID.ACTION_ID_ATTACK, attackData); } // 创建消息 createMessage(messageId, payload) { return this.encoder.encodeMessage(messageId, payload); } // 解析接收到的数据 parseReceivedData(data) { try { // 如果是字符串,尝试解析为JSON if (typeof data === 'string') { const jsonData = JSON.parse(data); return { type: 'json', data: jsonData }; } // 如果是二进制数据,尝试解析为protobuf if (data instanceof ArrayBuffer || data instanceof Uint8Array) { const message = this.encoder.decodeMessage(data); return { type: 'protobuf', data: message }; } return { type: 'unknown', data: data }; } catch (error) { utils.error('Failed to parse received data:', error); return { type: 'error', data: null, error: error.message }; } } // 验证坐标数据 validateCoordinateData(data) { if (!Array.isArray(data) || data.length !== 2) { return false; } const [x, y] = data; return utils.validateCoordinates(x, y); } // 格式化坐标数据 formatCoordinateData(data) { if (this.validateCoordinateData(data)) { const [x, y] = data; return { x: Math.round(x), y: Math.round(y), timestamp: Date.now() }; } return null; } // 创建进入实例消息(外层Message) createEnterInstanceMessage(instanceId = 0) { try { if (!this.encoder.root) { utils.warn('Protobuf root not ready'); return null; } const C2S_EnterInstance = this.encoder.root.lookupType('C2S_EnterInstance'); const enterInstance = { InstanceID: instanceId }; const innerBuffer = C2S_EnterInstance.encode(enterInstance).finish(); return this.encoder.encodeMessage( this.encoder.MessageID.MESSAGE_ID_ENTER_INSTANCE, innerBuffer ); } catch (error) { utils.error('Failed to create EnterInstance message:', error); return null; } } // 创建动作消息(外层Message) createActionMessage(actionId, x, y) { try { if (!this.encoder.root) { utils.warn('Protobuf root not ready'); return null; } // 创建移动指令的payload let payload; if (actionId === this.encoder.ActionID.ACTION_ID_MOVE) { // 创建包含x,y坐标的二进制数据 // 注意:这里使用Float64Array来存储双精度浮点数,因为proto中X和Y是double类型 const buffer = new ArrayBuffer(16); // 8字节 * 2 = 16字节 const view = new Float64Array(buffer); view[0] = x; view[1] = y; payload = new Uint8Array(buffer); } else { // 其他类型的动作可以在这里添加 utils.warn('Unsupported action ID:', actionId); return null; } // 创建C2S_Action消息 const C2S_Action = this.encoder.root.lookupType('C2S_Action'); const action = { Action: actionId, Payload: payload }; const actionBuffer = C2S_Action.encode(action).finish(); // 封装到外层Message return this.encoder.encodeMessage( this.encoder.MessageID.MESSAGE_ID_ACTION, actionBuffer ); } catch (error) { utils.error('Failed to create Action message:', error); return null; } } // 创建WASD方向动作消息(外层Message) createDirectionActionMessage(directionBits) { try { if (!this.encoder.root) { utils.warn('Protobuf root not ready'); return null; } // 创建C2S_Action消息,payload为空 const C2S_Action = this.encoder.root.lookupType('C2S_Action'); const action = { Action: directionBits, // 使用位运算结果作为动作ID Payload: new Uint8Array(0) // 空payload }; const actionBuffer = C2S_Action.encode(action).finish(); // 封装到外层Message return this.encoder.encodeMessage( this.encoder.MessageID.MESSAGE_ID_ACTION, actionBuffer ); } catch (error) { utils.error('Failed to create Direction Action message:', error); return null; } } } // 创建全局协议工具实例 const protoUtils = new ProtoUtils(); // 导出到全局 window.GameProto = GameProto; window.ProtoEncoder = ProtoEncoder; window.ProtoUtils = ProtoUtils; window.protoUtils = protoUtils; // 兼容性处理 if (typeof module !== 'undefined' && module.exports) { module.exports = { GameProto, ProtoEncoder, ProtoUtils, protoUtils }; }