Files
client-web/proto.js

303 lines
9.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
};
}