UNPKG

pinusmod-kcp

Version:

kcp 的 connector (基于 node-kcp-x)

217 lines (195 loc) 7.44 kB
/** * Copyright 2016 leenjewel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { pinus } from 'pinusmod'; import handler from '../common/handler'; import * as Kick from '../commands/kick'; import { HandshakeCommand } from '../commands/handshake'; import { HeartbeatCommand } from '../commands/heartbeat'; import * as coder from '../common/coder'; import { Protocol, Package, Message } from 'pinusmod-protocol'; import { Protobuf } from 'pinusmod-protobuf'; import { IConnector } from '../interfaces/IConnector'; import { ISocket } from '../interfaces/ISocket'; const RES_OK = 200; let protobuf: any; export const setupHandler = function (connector: any, socket: any, opts: any) { connector.handshake = connector.handshake || new HandshakeCommand(opts); if (!connector.heartbeat) { if (!opts.heartbeat) { opts.heartbeat = opts.interval / 1000; opts.timeout = opts.heartbeat * 2; } if (opts.heartbeat * 1000 < opts.interval) { console.warn('heartbeat interval must longer than kcp interval'); opts.heartbeat = opts.interval / 1000; } if (opts.timeout * 1000 < 2 * opts.interval) { console.warn('timeout must longer than kcp interval * 2'); opts.timeout = opts.heartbeat * 2; } connector.heartbeat = new HeartbeatCommand(Object.assign(opts, { disconnectOnTimeout: true })); } socket.on('handshake', connector.handshake.handle.bind(connector.handshake, socket)); socket.on('heartbeat', connector.heartbeat.handle.bind(connector.heartbeat, socket)); socket.on('heartbeatreset', connector.heartbeat.reset.bind(connector.heartbeat, socket)); socket.on('disconnect', connector.heartbeat.clear.bind(connector.heartbeat, socket.id)); socket.on('disconnect', connector.emit.bind(connector, 'disconnect', socket)); socket.on('closing', Kick.handle.bind(null, socket)); }; export const handlePackage = function (socket: any, pkg: any) { if (!pkg) { return 1; } pkg = Package.decode(pkg); if (pkg.type == 6) { return 0; } if (Array.isArray(pkg)) { let result; for (let p in pkg) { result = handler(socket, pkg[p]); if (result !== 0) { return result; } } } else { return handler(socket, pkg); } }; let heartbeatInterval = 0; export const getHeartbeatInterval = function () { return heartbeatInterval; }; let heartbeatTimeout = 0; export const getHeartbeatTimeout = function () { return heartbeatTimeout; }; let pomeloCoderData: { dict: any, abbrs: any, protos: any } = { dict: null, abbrs: null, protos: null }; export const initProtocol = function (data: any) { if (!!data) { if (data.code !== RES_OK) { console.warn('Handshake response code : ' + data.code); return; } if (!data || !data.sys) { console.warn('Handshake response sys is undefained'); return; } if (!!data.sys && !!data.sys.heartbeat) { heartbeatInterval = data.sys.heartbeat * 1000; heartbeatTimeout = heartbeatInterval * 2; } let dict = data.sys.dict; let protos = data.sys.protos; if (!!dict) { pomeloCoderData.dict = dict; pomeloCoderData.abbrs = {}; for (let route in dict) { pomeloCoderData.abbrs[dict[route]] = route; } } if (!!protos) { pomeloCoderData.protos = { server: protos.server || {}, client: protos.client || {} }; if (!!Protobuf) { protobuf = new Protobuf({ encoderProtos: protos.client, decoderProtos: protos.server }); } } } }; export const handshakePackage = function (userdata: object) { userdata = userdata || {}; return (Package.encode( Package.TYPE_HANDSHAKE, Protocol.strencode(JSON.stringify({ sys: { version: '1.1.1', type: 'socket' }, user: userdata })) )); }; let pomeloHandshakeAckPkg = Package.encode(Package.TYPE_HANDSHAKE_ACK); export const handshakeAckPackage = function () { return pomeloHandshakeAckPkg; }; let pomeloHeartbeatPkg = Package.encode(Package.TYPE_HEARTBEAT); export const heartbeatPackage = function () { return pomeloHeartbeatPkg; }; export const messagePackage = function (reqid: number, route: string, msg: any) { let type = reqid ? Message.TYPE_REQUEST : Message.TYPE_NOTIFY; let protos = !!pomeloCoderData.protos ? pomeloCoderData.protos.client : {}; if (!!protos[route]) { msg = protobuf.encode(route, msg); } else { msg = Protocol.strencode(JSON.stringify(msg)); } let compressRoute = 0; if (!!pomeloCoderData.dict && !!pomeloCoderData.dict[route]) { route = pomeloCoderData.dict[route]; compressRoute = 1; } msg = Message.encode(reqid, type, !!compressRoute, route, msg); return Package.encode(Package.TYPE_DATA, msg); }; export const isHandshakePackage = function (type: number) { return type == Package.TYPE_HANDSHAKE; } export const isHandshakeACKPackage = function (type: number) { return type == Package.TYPE_HANDSHAKE_ACK; } export const isHeartbeatPackage = function (type: number) { return type == Package.TYPE_HEARTBEAT; } export const isDataPackage = function (type: number) { return type == Package.TYPE_DATA; } export const isKickPackage = function (type: number) { return type == Package.TYPE_KICK; } export const kcpHeadDecode = function (bytes: Buffer) { //小端 let offset = 0; let conv = ((bytes[offset++]) | (bytes[offset++] << 8) | (bytes[offset++] << 16) | (bytes[offset++] << 24)) >>> 0; let cmd = bytes[offset++]; let frg = bytes[offset++]; let wnd = ((bytes[offset++]) | (bytes[offset++] << 8)) >>> 0; let ts = ((bytes[offset++]) | (bytes[offset++] << 8) | (bytes[offset++] << 16) | (bytes[offset++] << 24)) >>> 0; let sn = ((bytes[offset++]) | (bytes[offset++] << 8) | (bytes[offset++] << 16) | (bytes[offset++] << 24)) >>> 0; let una = ((bytes[offset++]) | (bytes[offset++] << 8) | (bytes[offset++] << 16) | (bytes[offset++] << 24)) >>> 0; let len = ((bytes[offset++]) | (bytes[offset++] << 8) | (bytes[offset++] << 16) | (bytes[offset++] << 24)) >>> 0; let rs = { conv: conv, cmd: cmd, frg: frg, wnd: wnd, ts: ts, sn: sn, una: una, len: len }; // if (bytes.length >= len + 24) { // rs.data = Buffer.from(bytes, 24, len); // } return rs; }; export const coders = { decodePackage: Package.decode, decodeMessage: Message.decode, };