UNPKG

mcraft-fun-mineflayer

Version:

Mineflayer viewer (connector) for mcraft.fun project and vanilla Minecraft client! Both TCP and WebSockets servers are supported.

191 lines (190 loc) 6.87 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.setNextWebsocketOptions = setNextWebsocketOptions; const net_1 = require("net"); const ws_1 = require("ws"); const server_1 = __importDefault(require("minecraft-protocol/src/server")); const minecraft_protocol_1 = require("minecraft-protocol"); const clientIgnoredPackets = [ 'position' ]; let nextWebsocketOptions; function setNextWebsocketOptions(options) { nextWebsocketOptions = options; } class WebsocketConnectionSocket extends net_1.Socket { ws; id = ''; constructor(ws, versionData, passwordValidation) { super(); this.ws = ws; let isFirstMessage = true; let dropMessages = false; this.ws.on('message', (data) => { if (dropMessages) return; // if data is string "version" then output info if (isFirstMessage && Buffer.isBuffer(data) && Buffer.from(data).toString() === 'version') { this.ws.send(JSON.stringify(versionData)); this.end(); return; } if (isFirstMessage && passwordValidation) { if (!Buffer.isBuffer(data) || Buffer.from(data).toString() !== passwordValidation) { dropMessages = true; setTimeout(() => { this.ws.send(JSON.stringify({ error: 'Invalid password' })); this.end(); }, 500); return; } isFirstMessage = false; return; } isFirstMessage = false; // console.log('message', data) this.emit('data', data); }); this.ws.on('close', () => { this.emit('end'); }); this.on('end', () => { this.ws.close(); }); this.ws.on('error', err => { this.emit('error', err); }); } write(data, callback) { // console.debug('write', data) this.ws.send(data, callback); return true; } //@ts-expect-error end() { this.ws.close(); } } class WebsocketServer extends server_1.default { i = 0; clientsPerIp = {}; listen(port, host) { // implement it with websocket instead // eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias const self = this; if (port === undefined) { this.socketServer = { close() { // self.emit('close') }, }; } else { let server; if (nextWebsocketOptions?.server) { server = nextWebsocketOptions.server; } const ws = new ws_1.WebSocketServer({ port: server ? undefined : port, server: server, host }); this.socketServer = ws; ws.on('connection', (webSocket, req) => { self.newConnection(webSocket, req); }); self.socketServer.on('error', err => { self.emit('error', err); }); self.socketServer.on('close', () => { self.emit('close'); }); self.socketServer.on('listening', () => { self.emit('listening'); }); } } newConnection(webSocket, req) { // eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias const self = this; const _socket = webSocket; const options = this.options; const versionData = { time: Date.now(), version: this.version, replEnabled: options.allowEval === true, consoleEnabled: options.sendConsole === true, requiresPass: Boolean(options.password), forwardChat: options.forwardChat === true, takeoverMode: options.takeoverMode === true, apiVersion: -1 // todo }; const socket = new WebsocketConnectionSocket(_socket, versionData, this.options.password); //@ts-expect-error const client = new minecraft_protocol_1.Client(true, this.version, this.customPackets, this.hideErrors); //@ts-expect-error client._end = client.end; client.end = function (endReason, fullReason = JSON.stringify({ text: endReason })) { if (client.state === minecraft_protocol_1.states.PLAY) { client.write('kick_disconnect', { reason: fullReason }); } else if (client.state === minecraft_protocol_1.states.LOGIN) { client.write('disconnect', { reason: fullReason }); } //@ts-expect-error client._end(endReason); }; const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for']?.split?.(',')?.[0] || req.connection?.remoteAddress || req.socket.remoteAddress; // Check IP whitelist if configured if (Array.isArray(this.options.ipFilter)) { if (!this.options.ipFilter.includes(ip)) { client.end('Your IP is not whitelisted'); return; } } _socket.remoteAddress = ip; client.id = ip + this.i++; socket.id = client.id; this.clients[client.id] = client; this.clientsPerIp[ip] ??= 0; this.clientsPerIp[ip]++; if (this.clientsPerIp[ip] > 3) { client.end('Too many connections from your IP'); return; } client.on('end', () => { delete self.clients[client.id]; self.clientsPerIp[ip]--; }); //@ts-expect-error client.setSocket(socket); this.emit('connection', client); } close() { for (const clientId of Object.keys(this.clients)) { const client = this.clients[clientId]; client.end('ServerShutdown'); } this.socketServer.close(); } writeToClients(clients, name, params) { if (clients.length === 0) return; try { const buffer = this.serializer.createPacketBuffer({ name, params }); for (const client of clients) client.writeRaw(buffer); } catch (err) { console.error(`Something went wrong with sending packet ${name} to ${clients.length} clients with params ${JSON.stringify(params)}`); console.error(err); } } } exports.default = WebsocketServer;