UNPKG

shardy

Version:

Framework for online games and applications on Node.js

329 lines 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Commander = exports.DisconnectReason = exports.CommanderMode = void 0; const Block_1 = require("./Block"); const Payload_1 = require("./Payload"); const Protocol_1 = require("./Protocol"); const Logger_1 = require("./Logger"); const Tools_1 = require("./Tools"); const Pulse_1 = require("./Pulse"); const Validator_1 = require("./Validator"); const LOG_TAG = Tools_1.Tools.getTag(module); const TIMEOUT_INTERVAL = 1000; const TIMEOUT_ERROR = 'timeout'; var CommanderMode; (function (CommanderMode) { CommanderMode[CommanderMode["Service"] = 0] = "Service"; CommanderMode[CommanderMode["Bot"] = 1] = "Bot"; })(CommanderMode || (exports.CommanderMode = CommanderMode = {})); var DisconnectReason; (function (DisconnectReason) { DisconnectReason[DisconnectReason["Normal"] = 0] = "Normal"; DisconnectReason[DisconnectReason["Timeout"] = 1] = "Timeout"; DisconnectReason[DisconnectReason["Handshake"] = 2] = "Handshake"; DisconnectReason[DisconnectReason["ServerDown"] = 3] = "ServerDown"; DisconnectReason[DisconnectReason["Unknown"] = 4] = "Unknown"; })(DisconnectReason || (exports.DisconnectReason = DisconnectReason = {})); class Commander { constructor(id, connection, service, options, log, mode = CommanderMode.Service) { this.id = id; this.connection = connection; this.service = service; this.options = options; this.log = log; this.mode = mode; this.onDisconnect = () => { }; this.onReady = () => { }; this.cid = 'unknown'; this.counter = 1; this.commands = new Map(); this.callbacks = new Map(); this.requests = new Map(); this.timeouts = new Map(); this.names = new Map(); this.reason = DisconnectReason.Normal; this.cid = this.id; this.protocol = new Protocol_1.Protocol(this.connection, this.log); this.protocol.onBlock = (block) => this.onBlock(block); this.protocol.onDisconnect = () => this.onClose(); this.pulse = new Pulse_1.Pulse(mode); this.pulse.onPulse = () => this.onPulse(); this.timer = setInterval(() => this.onCheckTimeout(), TIMEOUT_INTERVAL); this.counter = 0; } kick(reason) { this.log.info(`-> kick: ${reason}`, Logger_1.LoggerScope.Debug); this.protocol.kick(reason); this.protocol.disconnect(); this.pulse.clear(); } heartbeat() { this.log.info(`-> heartbeat`, Logger_1.LoggerScope.Debug); this.protocol.heartbeat(); } handshake(data) { this.log.info(`-> handshake`, Logger_1.LoggerScope.Debug); this.protocol.handshake(data); } acknowledge(data) { this.log.info(`-> acknowledge`, Logger_1.LoggerScope.Debug); this.protocol.acknowledge(data); } disconnect() { this.log.info(`-> disconnect`, Logger_1.LoggerScope.Debug); this.protocol.disconnect(); } command(command, data) { this.log.info(`-> command: ${command}, data: ${data}`, Logger_1.LoggerScope.Debug); const payload = Payload_1.Payload.encode(this.options.serializer, Payload_1.PayloadType.Command, command, 0, data); this.protocol.send(payload); } fetch(request, data) { this.log.info(`-> fetch: ${this.counter}.${request}, data: ${data}`, Logger_1.LoggerScope.Debug); const payload = Payload_1.Payload.encode(this.options.serializer, Payload_1.PayloadType.Request, request, this.counter, data); this.protocol.send(payload); const callback = new Promise((resolve) => this.callbacks.set(this.counter, resolve)); this.names.set(this.counter, request); this.timeouts.set(this.counter, Date.now()); this.counter++; return callback; } request(request, callback, data) { this.log.info(`-> request: ${this.counter}.${request}, data: ${data}`, Logger_1.LoggerScope.Debug); const payload = Payload_1.Payload.encode(this.options.serializer, Payload_1.PayloadType.Request, request, this.counter, data); this.protocol.send(payload); this.callbacks.set(this.counter, callback); this.names.set(this.counter, request); this.timeouts.set(this.counter, Date.now()); return this.counter++; } response(request, data) { this.log.info(`-> response: ${request.id}.${request.name}, data: ${data}`, Logger_1.LoggerScope.Debug); const payload = Payload_1.Payload.encode(this.options.serializer, Payload_1.PayloadType.Response, request.name, request.id, data); this.protocol.send(payload); } error(request, error, data) { this.log.info(`-> error: ${request.id}.${request.name}, error: ${error}, data: ${data}`, Logger_1.LoggerScope.Debug); const payload = Payload_1.Payload.encode(this.options.serializer, Payload_1.PayloadType.Response, request.name, request.id, data, error); this.protocol.send(payload); } clear() { clearInterval(this.timer); this.pulse.clear(); this.names.clear(); this.timeouts.clear(); this.commands.clear(); this.requests.clear(); this.callbacks.clear(); } cancelRequest(id) { this.names.delete(id); this.callbacks.delete(id); this.timeouts.delete(id); } addCommand(command, callback) { let list = new Array(); if (this.commands.has(command)) { list = this.commands.get(command); } list.push(callback); this.commands.set(command, list); } cancelCommand(command, callback) { if (this.commands.has(command)) { if (!callback) { const list = new Array(); this.commands.set(command, list); } else { const list = this.commands.get(command); const index = list.indexOf(callback, 0); if (index > -1) { list.splice(index, 1); } this.commands.set(command, list); } } } addOnRequest(request, callback) { if (this.requests.has(request)) { this.log.warn(`request already exists: ${request}, method: ${this.requests.get(request)}`, Logger_1.LoggerScope.Debug); return; } this.requests.set(request, callback); } cancelOnRequest(request) { if (this.requests.has(request)) { this.requests.delete(request); } } onCheckTimeout() { for (const [id, time] of this.timeouts) { const diff = Date.now() - time; if (diff > process.env.REQUEST_TIMEOUT) { const payload = Payload_1.Payload.create(Payload_1.PayloadType.Response, this.names.get(id), id, undefined, TIMEOUT_ERROR); this.onPayload(payload); } } } onBlock(block) { this.log.info(`[${LOG_TAG}] block: ${block.type}, data: ${block.body}`, Logger_1.LoggerScope.Debug); switch (block.type) { case Block_1.BlockType.Handshake: this.onHandshake(block); break; case Block_1.BlockType.Heartbeat: this.onHeartbeat(); break; case Block_1.BlockType.Kick: this.onKick(block); break; case Block_1.BlockType.HandshakeAcknowledgement: this.onAcknowledgement(block); break; case Block_1.BlockType.Data: const payload = Payload_1.Payload.decode(this.options.serializer, block.body); if (Payload_1.Payload.check(payload)) { this.onPayload(payload); } else { this.log.warn(`[${LOG_TAG}] invalid payload: ${payload}`, Logger_1.LoggerScope.Debug); } break; default: this.log.warn(`[${LOG_TAG}] not implemented block type: ${block.type}`, Logger_1.LoggerScope.Debug); break; } } onPayload(payload) { this.pulse.reset(); if (this.mode === CommanderMode.Bot) { this.heartbeat(); } switch (payload.type) { case Payload_1.PayloadType.Command: this.log.info(`<- command: ${payload.name}, data: ${payload.data}`, Logger_1.LoggerScope.Debug); if (this.mode === CommanderMode.Service) { const command = this.options.commands.get(payload.name); if (command) { command(this, payload, this.service); } else { this.log.warn(`[${LOG_TAG}] unknown command: ${payload.name}`, Logger_1.LoggerScope.Debug); } } else { const list = this.commands.get(payload.name); if (list) { for (const callback of list) { callback(payload); } } } break; case Payload_1.PayloadType.Request: this.log.info(`<- request: ${payload.id}.${payload.name}, data: ${payload.data}`, Logger_1.LoggerScope.Debug); if (this.mode === CommanderMode.Service) { const request = this.options.commands.get(payload.name); if (request) { request(this, payload, this.service); } else { this.log.warn(`[${LOG_TAG}] unknown request: ${payload.id}.${payload.name}`, Logger_1.LoggerScope.Debug); } } else { const callback = this.requests.get(payload.name); if (callback) { callback(payload); } } break; case Payload_1.PayloadType.Response: if (payload.error.trim().length === 0) { this.log.info(`<- response: ${payload.id}.${payload.name}, data: ${payload.data}`, Logger_1.LoggerScope.Debug); } else { this.log.info(`<- error: ${payload.id}.${payload.name}, error: ${payload.error}, data: ${payload.data}`, Logger_1.LoggerScope.Debug); } const callback = this.callbacks.get(payload.id); if (callback) { callback(payload); this.cancelRequest(payload.id); } else { this.log.warn(`[${LOG_TAG}] unknown response: ${payload.id}.${payload.name}`, Logger_1.LoggerScope.Debug); } break; default: break; } } onClose() { this.log.info(`<- disconnect`, Logger_1.LoggerScope.Debug); this.clear(); this.onDisconnect(this.reason); } onHandshake(block) { this.log.info(`<- handshake`, Logger_1.LoggerScope.Debug); this.pulse.reset(); const state = this.options.validator.verifyHandshake(block.body); this.log.info(`[${LOG_TAG}] handshake validation state: ${state}, data: ${block.body}`, Logger_1.LoggerScope.Debug); if (state === Validator_1.ValidatorState.Success) { this.acknowledge(this.options.validator.acknowledgement(block.body)); } else { this.kick(DisconnectReason.Handshake); } } onAcknowledgement(block) { this.log.info(`<- acknowledge`, Logger_1.LoggerScope.Debug); this.pulse.reset(); if (this.mode === CommanderMode.Bot) { const state = this.options.validator.verifyAcknowledgement(block.body); this.log.info(`[${LOG_TAG}] acknowledgement data: ${block.body}, validation state: ${state}`, Logger_1.LoggerScope.Debug); if (state === Validator_1.ValidatorState.Success) { this.acknowledge(this.options.validator.acknowledgement(block.body)); } else { this.reason = DisconnectReason.Handshake; this.disconnect(); return; } } else { this.log.info(`[${LOG_TAG}] acknowledgement data: ${block.body}`, Logger_1.LoggerScope.Debug); } this.log.info(`ready to work`, Logger_1.LoggerScope.Debug); this.onReady(); } onHeartbeat() { this.log.info(`<- heartbeat`, Logger_1.LoggerScope.Debug); this.pulse.reset(); if (this.mode === CommanderMode.Service) { this.heartbeat(); } } onKick(block) { this.reason = block.body; this.log.info(`<- kick: ${this.reason}`, Logger_1.LoggerScope.Debug); this.pulse.reset(); } onPulse() { if (this.mode === CommanderMode.Bot) { this.log.info(`[${LOG_TAG}] pulse timeout, send heartbeat`, Logger_1.LoggerScope.Debug); this.heartbeat(); } else { this.log.info(`pulse timeout, send kick`, Logger_1.LoggerScope.Debug); this.kick(DisconnectReason.Timeout); } } destroy() { this.log.info(`[${LOG_TAG}] destroy`, Logger_1.LoggerScope.Debug); this.clear(); this.protocol.destroy(); } } exports.Commander = Commander; //# sourceMappingURL=Commander.js.map