import md from 'minecraft-data' import Cubbot from '../Cubbot' import Data from '../utils/Data' import Module from '../utils/Module' import Chat, { IMessage } from './Chat' import Spawn from './Spawn' type DisconnectReason = 'authservers_down' | 'banned' | 'banned.reason' | 'banned.expiration' | 'banned_ip.reason' | 'banned_ip.expiration' | 'duplicate_login' | 'flying' | 'generic' | 'idling' | 'illegal_characters' | 'invalid_entity_attacked' | 'invalid_player_movement' | 'invalid_vehicle_movement' | 'ip_banned' | 'kicked' | 'outdated_client' | 'outdated_server' | 'server_shutdown' | 'slow_login' | 'unverified_username' | 'not_whitelisted' | 'server_full' | 'name_taken' | 'unexpected_query_response' | 'genericReason' | 'disconnected' | 'lost' | 'kicked' | 'timeout' | 'closed' | 'loginFailed' | 'loginFailedInfo' | 'loginFailedInfo.serversUnavailable' | 'loginFailedInfo.invalidSession' | 'quitting' | 'endOfStream' | 'overflow' | 'spam' const REASON_PREFIX = 'disconnect.' interface IConfig { /** Game data url */ dataSource: string /** Game data cache path */ dataDir: string reconnect: DisconnectReason[] | boolean reconnectDelay: number } /** Connection informations */ export default class Connection extends Module { private _connected = false public get connected() { return this._connected } private _oldData = md('1.14') public get oldData() { return this._oldData } private _data = new Data() public get data() { return this._data } private _engine?: Cubbot public setEngine(value: Cubbot) { this._engine = value } protected mount() { const spawn = this.load(Spawn) spawn.addCheck('data') this.client.on('connect', () => { this._connected = true this.logger.trace('Connected') this._data.onReady(() => spawn.validateCheck('data')) }) this.client.on('disconnect', ({ reason }) => { this._connected = false const message: IMessage = JSON.parse(reason) this.logger.warn({ msg: 'Disconnected', type: 'reason', value: Chat.parse(message, this.oldData.language) }) this.bye(message.translate) }) this.client.on('kick_disconnect', ({ reason }) => { this._connected = false const message: IMessage = JSON.parse(reason) this.logger.warn({ msg: 'Kicked', type: 'reason', value: Chat.parse(message, this.oldData.language) }) this.bye(message.translate) }) this.client.on('login', () => { this.logger.trace('Logged') this._data.load(this.conf.dataSource, this.conf.dataDir, (this.client as any).version, this.logger.child({ name: 'Data', level: 'info' })) // FIXME: use 'minecraft-data' when it include 1.15+ }) this.client.on('error', error => { this.logger.error({ msg: 'Error', value: error }) }) // MAYBE: on 'end' } protected getConf() { return { dataDir: './data/', dataSource: 'https://pokechu22.github.io/Burger/', reconnect: false, reconnectDelay: 5000, } } private bye(reason?: string) { if (this.conf.reconnect === true || (this.conf.reconnect !== false && reason !== undefined && this.conf.reconnect.some(r => reason.endsWith(REASON_PREFIX + r))) ) { this._engine?.umount(false) this.logger.info({ msg: 'Reconnecting', type: 'ms', value: this.conf.reconnectDelay }) // MAYBE: exponental backoff setTimeout(() => this._engine?.mount(), this.conf.reconnectDelay) } else { this.logger.info('Bye') this._engine?.umount(true) } } }