UNPKG

teeworlds-econ

Version:
372 lines (338 loc) 12.9 kB
var EconConnectionError, EconError, EventEmitter, Socket, TeeworldsEcon, Transaction, debug, escape, handlers, parseStatus, parseTextValue, ref, ref1, split, splitText, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, slice = [].slice; Socket = require('net').Socket; EventEmitter = require('events').EventEmitter; ref = require('./errors'), EconError = ref.EconError, EconConnectionError = ref.EconConnectionError; ref1 = require('./utils'), split = ref1.split, splitText = ref1.splitText, escape = ref1.escape, parseStatus = ref1.parseStatus, parseTextValue = ref1.parseTextValue, debug = ref1.debug; Transaction = require('./transaction'); handlers = require('./handlers'); TeeworldsEcon = (function(superClass) { extend(TeeworldsEcon, superClass); TeeworldsEcon.prototype.name = ''; TeeworldsEcon.prototype.clients = []; function TeeworldsEcon() { var args, host, password, port, ref2; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; this.disconnect = bind(this.disconnect, this); this.reconnect = bind(this.reconnect, this); this.handleMessage = bind(this.handleMessage, this); TeeworldsEcon.__super__.constructor.apply(this, arguments); if (typeof args[0] === 'object') { ref2 = args[0], host = ref2.host, port = ref2.port, password = ref2.password; } else { host = args[0], port = args[1], password = args[2]; } if (!host) { throw new EconError('Undefined host'); } if (!port) { throw new EconError('Undefined port'); } if (!password) { throw new EconError('Undefined password'); } this.server = { host: host, port: port, password: password }; this.connection = null; this.connected = false; this.retryDelay = null; this.retryCount = null; this.retryTimer = null; this.resetTransactions(); this.resetHandlers(); this.addHandler(handlers.handleEnterMessage); this.addHandler(handlers.handleLeaveMessage); this.addHandler(handlers.handlePickupMessage); this.addHandler(handlers.handleChatMessage); this.addHandler(handlers.handleKillMessage); this.addHandler(handlers.handleFlagGrabMessage); this.addHandler(handlers.handleFlagReturnMessage); this.addHandler(handlers.handleCaptureMessage); this.addHandler(handlers.handleNetBanMessage); } TeeworldsEcon.prototype.exec = function(command, arg) { var err, promise, ref2, timeout, transaction; timeout = (ref2 = (arg != null ? arg : {}).timeout) != null ? ref2 : 30000; if (!this.isConnected(false)) { err = new EconConnectionError('Not connected'); debug.connection('%s:%s econ error: %s', this.server.host, this.server.port, err.message); this.emit('error', err); return Promise.reject(err); } transaction = new Transaction(command, { timeout: timeout }); promise = new Promise((function(_this) { return function(resolve, reject) { transaction.on('end', function(arg1) { var id, result; id = arg1.id, result = arg1.result; resolve(result); debug.connection('%s:%s %s transaction complete', _this.server.host, _this.server.port, id); transaction.removeAllListeners('end'); transaction.removeAllListeners('error'); return _this.removeTransaction(transaction); }); return transaction.on('error', function(err) { reject(err); debug.connection('%s:%s transaction error: %s', _this.server.host, _this.server.port, err.message); transaction.removeAllListeners('end'); transaction.removeAllListeners('error'); return _this.removeTransaction(transaction); }); }; })(this)); this.scheduleTransaction(transaction); return promise; }; TeeworldsEcon.prototype.say = function(message, limit) { var chunk, chunks, i, len, results; if (limit == null) { limit = 256; } if (limit > 256) { limit = 256; } chunks = message.split('\n').map(escape).map(function(line) { return splitText(line, limit); }).reduce(function(a, b) { return a.concat(b); }); results = []; for (i = 0, len = chunks.length; i < len; i++) { chunk = chunks[i]; results.push(this.exec("say \"" + chunk + "\"")); } return results; }; TeeworldsEcon.prototype.status = function() { return this.exec('status').then(parseStatus); }; TeeworldsEcon.prototype.svMotd = function(message) { if (message) { return this.exec("sv_motd \"" + (escape(message)) + "\""); } return this.exec('sv_motd').then(parseTextValue); }; TeeworldsEcon.prototype.svName = function(name) { if (name) { return this.exec("sv_name \"" + (escape(name)) + "\""); } return this.exec('sv_name').then(parseTextValue); }; TeeworldsEcon.prototype.write = function(message) { if (!(this.connection && this.connection.writable)) { return; } debug.connection('writing to %s:%s econ: %s', this.server.host, this.server.port, message); return this.connection.write(message + '\n'); }; TeeworldsEcon.prototype.addHandler = function(handler) { return this.handlers.push(handler); }; TeeworldsEcon.prototype.removeHandler = function(handler) { var index; index = this.handlers.find(function(item) { return handler === item; }); if (index !== -1) { return this.handlers.splice(index, 1); } }; TeeworldsEcon.prototype.resetHandlers = function() { return this.handlers = []; }; TeeworldsEcon.prototype.scheduleTransaction = function(transaction) { this.transactions.push(transaction); return this.write(transaction.getCommand()); }; TeeworldsEcon.prototype.removeTransaction = function(transaction) { var index; index = this.transactions.find(function(item) { return transaction === item; }); if (index !== -1) { return this.transactions.splice(index, 1); } }; TeeworldsEcon.prototype.resetTransactions = function() { return this.transactions = []; }; TeeworldsEcon.prototype.handleMessage = function(message) { var err, handler, i, len, ref2, result, results; debug.connection('reading from %s:%s econ: %s', this.server.host, this.server.port, message); (function(_this) { return (function(message) { var cid, client, matches; if (matches = /^\[server\]: player has entered the game. ClientID=([0-9a-f]+) addr=(.+?):([0-9]+)$/.exec(message)) { cid = parseInt(matches[1], 16); client = matches[2] + ":" + matches[3]; debug.connection('new client (%s) with ip:port %s on %s:%s ', cid, client, _this.server.host, _this.server.port); return _this.setClientInfo(cid, client); } }); })(this)(message); (function(_this) { return (function(message) { var cid, matches; if (matches = /^\[game\]: leave player='([0-9]+):.*'$/.exec(message)) { cid = parseInt(matches[1]); debug.connection('client disconnect (%s) on %s:%s ', cid, _this.server.host, _this.server.port); return _this.removeClientInfo(cid); } }); })(this)(message); if (message === 'Enter password:') { debug.connection('%s:%s password request', this.server.host, this.server.port); this.write(this.server.password); return; } if (message === 'Authentication successful. External console access granted.') { Promise.all([this.svName(), this.status()]).then((function(_this) { return function(arg) { var i, len, name, player, status; name = arg[0], status = arg[1]; _this.name = name; if (status) { for (i = 0, len = status.length; i < len; i++) { player = status[i]; _this.setClientInfo(player.cid, player.client); } } if (!_this.connected) { _this.connected = true; debug.connection('%s:%s connected', _this.server.host, _this.server.port); return _this.emit('online'); } else { debug.connection('%s:%s reconnected', _this.server.host, _this.server.port); return _this.emit('reconnected'); } }; })(this))["catch"]((function(_this) { return function(err) { debug.connection('%s:%s econ error: %s', _this.server.host, _this.server.port, err.message); return _this.emit('error', err); }; })(this)); return; } if (/^Wrong password [0-9\/]+.$/.exec(message)) { err = new EconConnectionError(message + " Disconnecting"); debug.connection('%s:%s econ error: %s', this.server.host, this.server.port, err.message); this.emit('error', err); this.disconnect(); this.emit('end'); return; } if (message === 'authentication timeout') { err = new EconConnectionError('Authentication timeout. Disconnecting'); debug.connection('%s:%s econ error: %s', this.server.host, this.server.port, err.message); this.emit('error', err); this.disconnect(); this.emit('end'); return; } this.transactions.forEach(function(transaction) { return transaction.handleMessage(message); }); if (!this.isConnected()) { return; } ref2 = this.handlers; results = []; for (i = 0, len = ref2.length; i < len; i++) { handler = ref2[i]; result = handler.call(this, this, message); if (result === false) { break; } else { results.push(void 0); } } return results; }; TeeworldsEcon.prototype.setClientInfo = function(cid, client) { return this.clients[cid] = client; }; TeeworldsEcon.prototype.removeClientInfo = function(cid) { return delete this.clients[cid]; }; TeeworldsEcon.prototype.getClientInfo = function(cid) { var ref2; return (ref2 = this.clients[cid]) != null ? ref2 : null; }; TeeworldsEcon.prototype.connect = function(connectionParams) { if (connectionParams == null) { connectionParams = {}; } if (this.connection) { return; } this.retryDelay = connectionParams.retryDelay ? connectionParams.retryDelay : 30000; this.retryCount = connectionParams.retryCount ? connectionParams.retryCount : -1; this.connection = new Socket(); this.connection.pipe(split(/\r?\n\u0000*/)).on('data', this.handleMessage); this.connection.on('error', (function(_this) { return function(err) { debug.connection('%s:%s connection error: %s', _this.server.host, _this.server.port, err.message); return _this.emit('error', err); }; })(this)); this.connection.on('close', this.reconnect); this.connection.on('end', this.reconnect); this.connection.setKeepAlive(true); debug.connection('connecting to %s:%s', this.server.host, this.server.port); return this.connection.connect(this.server.port, this.server.host); }; TeeworldsEcon.prototype.reconnect = function() { if (this.retryTimer) { return; } debug.connection('reconnecting to %s:%s', this.server.host, this.server.port); if (this.retryCount === 0) { this.disconnect(); this.emit('end'); return; } if (this.retryCount > 0) { this.retryCount--; } this.emit('reconnect'); return this.retryTimer = setTimeout((function(_this) { return function() { _this.retryTimer = null; _this.disconnect(); return _this.connect({ retryDelay: _this.retryDelay, retryCount: _this.retryCount }); }; })(this), this.retryDelay); }; TeeworldsEcon.prototype.disconnect = function() { if (!this.connection) { return; } debug.connection('disconnecting from %s:%s', this.server.host, this.server.port); this.connection.removeAllListeners('data'); this.connection.removeAllListeners('end'); this.connection.removeAllListeners('error'); this.connection.destroy(); this.connection.unref(); return this.connection = null; }; TeeworldsEcon.prototype.isConnected = function(initialized) { if (initialized == null) { initialized = true; } return this.connection && this.connection.writable && (!initialized || this.connected); }; return TeeworldsEcon; })(EventEmitter); module.exports = TeeworldsEcon;