初次提交
This commit is contained in:
303
proto.js
Normal file
303
proto.js
Normal file
@@ -0,0 +1,303 @@
|
||||
// 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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user