const readline = require('readline'); const protobuf = require('protobufjs'); const ws = require('nodejs-websocket'); function prettyJsonEncode(obj) { return JSON.stringify(obj, "", " "); } class ClientNet { constructor(url, protoPbFile, msgIdPbFile) { this.url = url; this.conn = null; this.protoPbFile = protoPbFile; this.msgIdPbFile = msgIdPbFile; this.protoPb = null; this.msgIdPb = null; this.cmMsgId = null; this.smMsgId = null; this.recvBuf = Buffer.alloc(0); this.uniqId = 1000; this.msgHandlerMap = new Map(); this.rl = readline.createInterface({ input: process.stdin, //output: process.stdout }); this.rl.on("line", (line)=> { this.parseCmdLine(line); }); } selfRegisterMsgHandle(msgName) { this.registerMsgHandle( msgName, (msg) => { if (this[msgName]) { this[msgName](msg); } }); } async init() { { this.protoPb = await (new protobuf.Root()).load (this.protoPbFile, { 'keepCase': true } ); } { this.msgIdPb = await (new protobuf.Root()).load (this.msgIdPbFile, { 'keepCase': true } ); this.cmMsgId = this.msgIdPb.lookup('CMMessageId_e'); this.smMsgId = this.msgIdPb.lookup('SMMessageId_e'); } { for (let id in this.smMsgId.valuesById) { const msgName = this.smMsgId.valuesById[id]; this.selfRegisterMsgHandle(msgName.slice(1)); } } } async connect() { console.log(this.url); this.conn = await ws.connect(this.url); this.on('binary', this.onReceive.bind(this)); } on(eventName, ...args) { this.conn.on(eventName, ...args); } async onReceive(inStream) { inStream.on('readable', async () => { //console.log('inStream.readable'); const newData = inStream.read(); if (newData) { this.recvBuf = Buffer.concat([this.recvBuf, newData]); await this.onParsePacket(); } }); inStream.on('end', () => { //console.log('inStream.end', this.recvBuf.length); }); inStream.on('close', () => { //console.log('inStream.close'); }); } async onParsePacket() { let offset = 0; while (this.recvBuf.length >= offset + 12) { const msgSize = this.recvBuf.readUInt16LE(offset + 0); const msgId = this.recvBuf.readUInt16LE(offset + 2); const seqId = this.recvBuf.readUInt32LE(offset + 4); const magicCode = this.recvBuf.readUInt16LE(offset + 8); //console.log('onParsePacket', msgSize, msgId, seqId, magicCode); if (this.recvBuf.length >= offset + 12 + msgSize) { await this.processMsg(msgId, this.recvBuf.slice ( offset + 12, offset + 12 + msgSize) ); offset += 12 + msgSize; } else { break; } } this.recvBuf = this.recvBuf.slice(offset); } async processMsg(msgId, buff) { const handlers = this.msgHandlerMap.get(msgId); if (handlers) { let msg = null; handlers.forEach((value, key) => { if (!msg) { msg = value.msgType.decode(buff); } console.log(value.msgType['name'], prettyJsonEncode(msg)); value.cb(msg); }); } } registerMsgHandle(msgName, cb) { try { const msgType = this.protoPb.lookupType(msgName); const msgId = this.smMsgId.values['_' + msgName]; if (!msgId) { return null; } const handle = { msgId: msgId, msgName: msgName, msgType: msgType, cb: cb, uniqId: ++this.uniqId} ; if (!this.msgHandlerMap.has(msgId)) { this.msgHandlerMap.set(msgId, new Map([ [handle.uniqId, handle] ])); } else { this.msgHandlerMap.get(msgId).set(handle.uniqId, handle); } return handle; } catch (err) { return null; } } unRegisterMsgHandle(handle) { if (this.msgHandlerMap.has(handle.msgId)) { if (this.msgHandlerMap[handle.msgId].has(handle.uniqId)) { this.msgHandlerMap[handle.msgId].delete(handle.uniqId); } } } async sendMsg(name, msg) { const msgType = this.protoPb.lookupType(name); const msgId = this.cmMsgId.values['_' + name]; const msgPb = msgType.create(msg); const msgBuf = msgType.encode(msg).finish(); let buf = Buffer.alloc(12); buf.writeUInt16LE(msgBuf.length, 0); buf.writeUInt16LE(msgId, 2); buf.writeUInt32LE(0, 4); buf.writeUInt8('K'.charCodeAt(0), 8); buf.writeUInt8('S'.charCodeAt(0), 9); buf.writeUInt16LE(0, 10); this.conn.sendBinary(Buffer.concat([buf, msgBuf])); console.log(name, msg, (Buffer.concat([buf, msgBuf])).length); } SMLogin(msg) { if (msg.errcode) { return; } } SMPing(msg) { } parseCmdLine(line) { const params = line.split(' '); if (params.length <= 0) { console.log('unknown command'); return; } const msgName = params[0]; const msgFields = params.slice(1); const msgType = this.protoPb.lookupType(msgName); if (!msgType) { console.log('unknown command'); return; } const msgBody = line.slice(line.indexOf(msgName) + msgName.length); const msg = eval('({' + msgBody + '})'); /*const msg = {}; for (let i = 0; i < msgType.fieldsArray.length; ++i) { if (i < msgFields.length) { const name = msgType.fieldsArray[i].name; const type = msgType.fieldsArray[i].type; if (['int32', 'int', 'float', 'double'].indexOf(type) >= 0) { msg[name] = Number(msgFields[i]); } else { msg[name] = msgFields[i]; } } } console.log(msg);*/ this.sendMsg(msgName, msg); } } module.exports = ClientNet;