From 0bef5be00874a13e3697857d8c51072abc35a6fd Mon Sep 17 00:00:00 2001 From: q13x Date: Sun, 18 Dec 2022 05:39:38 -0800 Subject: [PATCH] first commit --- .gitignore | 1 + README.md | 49 ++ classes.js | 2 + classes.ts | 15 + config.js | 19 + config.ts | 21 + eaglerPacketDef.js | 22 + eaglerPacketDef.ts | 41 ++ globals.d.ts | 7 + index.js | 67 ++ index.ts | 76 ++ listener.js | 26 + listener.ts | 25 + logger.js | 54 ++ logger.ts | 61 ++ meta.js | 3 + meta.ts | 3 + motd.js | 34 + motd.ts | 66 ++ package-lock.json | 1681 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 15 + tsconfig.json | 9 + types.js | 6 + types.ts | 60 ++ utils.js | 229 ++++++ utils.ts | 241 +++++++ 26 files changed, 2833 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 classes.js create mode 100644 classes.ts create mode 100644 config.js create mode 100644 config.ts create mode 100644 eaglerPacketDef.js create mode 100644 eaglerPacketDef.ts create mode 100644 globals.d.ts create mode 100644 index.js create mode 100644 index.ts create mode 100644 listener.js create mode 100644 listener.ts create mode 100644 logger.js create mode 100644 logger.ts create mode 100644 meta.js create mode 100644 meta.ts create mode 100644 motd.js create mode 100644 motd.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 types.js create mode 100644 types.ts create mode 100644 utils.js create mode 100644 utils.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a08fa1b --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# EaglercraftX (1.8.9) WebSocket Proxy +## What is this? +A very primitive and small Node.js based alternative to the custom BungeeCord servers for Eaglercraft 1.8.9. Until the developers officially release the BungeeCord server, this is the only way you can create a EaglercraftX server. +## Issues +* Generic and vague "End of stream" error when disconnected by the proxy, not the server +* Inability to set a server icon +* Skins don't work +## Setup Guide +### Prerequisites +* Node.js v12 and up +* A 1.8.9-compatible Minecraft server or proxy +### Setup Guide +1. Download and extract this repository to a folder on your computer. +2. Open a terminal and go to the folder of the repository. Run `npm i`. +3. Edit `config.js` to configure your proxy. Below is a small breakdown of the configuration file. +```js +export const config = { + // The name of the proxy. Does nothing. + name: "BasedProxy", + // The port you want to run the proxy on. + port: 80, + // The amount of players that can join and use this proxy simultaneously. + maxPlayers: 20, + motd: { + // Does nothing. (icons do not work) + iconURL: null, + // The first line of the MOTD. + l1: "hi", + // The second line of the MOTD. + l2: "lol" + }, + server: { + // The IP/domain of the server you want the proxy to point to. + // Remember, the server HAS to be offline, or you can't connect. + host: "127.0.0.1", + // The port the server is running on. + port: 25565 + }, + security: { + // Whether or not encryption should be enabled. + // If you are using Repl.it, this should be left off. + enabled: false, + // The key issued to you by your certificate authority (CA). + key: null, + // The certificate issued to you by your certificate authority (CA). + cert: null + } +}; +``` diff --git a/classes.js b/classes.js new file mode 100644 index 0000000..0bda634 --- /dev/null +++ b/classes.js @@ -0,0 +1,2 @@ +export class ProxiedPlayer { +} diff --git a/classes.ts b/classes.ts new file mode 100644 index 0000000..8b073c6 --- /dev/null +++ b/classes.ts @@ -0,0 +1,15 @@ +import { Client } from "minecraft-protocol" +import { WebSocket } from "ws" +import { State } from "./types.js" + +export class ProxiedPlayer { + public username: string + public uuid: string + public clientBrand: string + public state: State + public ws: WebSocket + public ip: string + public remotePort: number + public remoteConnection: Client + public compressionThreshold: number +} \ No newline at end of file diff --git a/config.js b/config.js new file mode 100644 index 0000000..00ebabd --- /dev/null +++ b/config.js @@ -0,0 +1,19 @@ +export const config = { + name: "BasedProxy", + port: 80, + maxPlayers: 20, + motd: { + iconURL: null, + l1: "hi", + l2: "lol" + }, + server: { + host: "127.0.0.1", + port: 25565 + }, + security: { + enabled: false, + key: null, + cert: null + } +}; diff --git a/config.ts b/config.ts new file mode 100644 index 0000000..0afae1c --- /dev/null +++ b/config.ts @@ -0,0 +1,21 @@ +import { Config } from "./types.js"; + +export const config: Config = { + name: "BasedProxy", + port: 80, // 443 if using TLS + maxPlayers: 20, + motd: { + iconURL: null, + l1: "hi", + l2: "lol" + }, + server: { + host: "127.0.0.1", + port: 25565 + }, + security: { // provide path to key & cert if you want to enable encryption/secure websockets + enabled: false, + key: null, + cert: null + } +} \ No newline at end of file diff --git a/eaglerPacketDef.js b/eaglerPacketDef.js new file mode 100644 index 0000000..4ecf67f --- /dev/null +++ b/eaglerPacketDef.js @@ -0,0 +1,22 @@ +export var EaglerPacketId; +(function (EaglerPacketId) { + EaglerPacketId[EaglerPacketId["IDENTIFY_CLIENT"] = 1] = "IDENTIFY_CLIENT"; + EaglerPacketId[EaglerPacketId["IDENTIFY_SERVER"] = 2] = "IDENTIFY_SERVER"; + EaglerPacketId[EaglerPacketId["LOGIN"] = 4] = "LOGIN"; + EaglerPacketId[EaglerPacketId["LOGIN_ACK"] = 5] = "LOGIN_ACK"; + EaglerPacketId[EaglerPacketId["SKIN"] = 7] = "SKIN"; + EaglerPacketId[EaglerPacketId["C_READY"] = 8] = "C_READY"; + EaglerPacketId[EaglerPacketId["COMPLETE_HANDSHAKE"] = 9] = "COMPLETE_HANDSHAKE"; + EaglerPacketId[EaglerPacketId["DISCONNECT"] = 255] = "DISCONNECT"; +})(EaglerPacketId || (EaglerPacketId = {})); +export var DisconnectReason; +(function (DisconnectReason) { + DisconnectReason[DisconnectReason["UNEXPECTED_PACKET"] = 1] = "UNEXPECTED_PACKET"; + DisconnectReason[DisconnectReason["DUPLICATE_USERNAME"] = 2] = "DUPLICATE_USERNAME"; + DisconnectReason[DisconnectReason["BAD_USERNAME"] = 3] = "BAD_USERNAME"; + DisconnectReason[DisconnectReason["SERVER_DISCONNECT"] = 4] = "SERVER_DISCONNECT"; + DisconnectReason[DisconnectReason["CUSTOM"] = 8] = "CUSTOM"; +})(DisconnectReason || (DisconnectReason = {})); +export const MAGIC_BUILTIN_SKIN_BYTES = [0x00, 0x05, 0x01, 0x00, 0x00, 0x00]; +export const MAGIC_ENDING_IDENTIFYS_BYTES = [0x00, 0x00, 0x00]; +// Afterwards, forward 0x01 (Join Game, Clientbound) and everything after that diff --git a/eaglerPacketDef.ts b/eaglerPacketDef.ts new file mode 100644 index 0000000..c464ad6 --- /dev/null +++ b/eaglerPacketDef.ts @@ -0,0 +1,41 @@ +import { UUID } from "./types.js" + +export enum EaglerPacketId { + IDENTIFY_CLIENT = 0x01, + IDENTIFY_SERVER = 0x2, + LOGIN = 0x4, + LOGIN_ACK = 0x05, + SKIN = 0x07, + C_READY = 0x08, + COMPLETE_HANDSHAKE = 0x09, + DISCONNECT = 0xff +} + +export enum DisconnectReason { + UNEXPECTED_PACKET = 0x1, + DUPLICATE_USERNAME = 0x2, + BAD_USERNAME = 0x3, + SERVER_DISCONNECT = 0x4, + CUSTOM = 0x8 +} + +// TODO: get skins + server icon working +export type Bitmap = unknown +export const MAGIC_BUILTIN_SKIN_BYTES = [0x00, 0x05, 0x01, 0x00, 0x00, 0x00] +export const MAGIC_ENDING_IDENTIFYS_BYTES = [0x00, 0x00, 0x00] + +// NOTE: unless explicitly marked, a number (VarInt) preceding a string is a string + +export type IdentifyC = [EaglerPacketId.IDENTIFY_CLIENT, 0x01, 0x2f, number, string, number, string] +export type IdentifyS = [EaglerPacketId.IDENTIFY_SERVER, 0x01, number, string, number, string] +export type Login = [EaglerPacketId.LOGIN, number, string, number, "default", 0x0] +export type LoginAck = [EaglerPacketId.LOGIN_ACK, number, string, UUID] +export type BaseSkin = [EaglerPacketId.SKIN, number, string, ...typeof MAGIC_BUILTIN_SKIN_BYTES | [number]] +// IF base skin packet ends with magic bytes... +export type SkinBuiltIn = [EaglerPacketId.SKIN, number, string, ...typeof MAGIC_BUILTIN_SKIN_BYTES, number] +export type SkinCustom = [EaglerPacketId.SKIN, number, string, number, Bitmap] +export type ClientReady = [EaglerPacketId.C_READY] +export type Joined = [EaglerPacketId.COMPLETE_HANDSHAKE] +export type Disconnect = [EaglerPacketId.DISCONNECT, number, string, DisconnectReason] + +// Afterwards, forward 0x01 (Join Game, Clientbound) and everything after that \ No newline at end of file diff --git a/globals.d.ts b/globals.d.ts new file mode 100644 index 0000000..16f9bd6 --- /dev/null +++ b/globals.d.ts @@ -0,0 +1,7 @@ +import { ProxyGlobals } from "./types.js" + +declare global { + var PROXY: ProxyGlobals +} + +export {} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..1a90cd9 --- /dev/null +++ b/index.js @@ -0,0 +1,67 @@ +import { readFileSync } from "fs"; +import * as https from "https"; +import { WebSocketServer } from "ws"; +import { ProxiedPlayer } from "./classes.js"; +import { config } from "./config.js"; +import { handlePacket } from "./listener.js"; +import { Logger } from "./logger.js"; +import { BRANDING, NETWORK_VERSION, VERSION } from "./meta.js"; +import { State } from "./types.js"; +import { genUUID } from "./utils.js"; +const logger = new Logger("EagXProxy"); +const connectionLogger = new Logger("ConnectionHandler"); +global.PROXY = { + brand: BRANDING, + version: VERSION, + MOTDVersion: NETWORK_VERSION, + serverName: config.name, + secure: false, + proxyUUID: genUUID(config.name), + MOTD: { + icon: null, + motd: [config.motd.l1, config.motd.l2] + }, + playerStats: { + max: config.maxPlayers, + onlineCount: 0 + }, + wsServer: null, + players: new Map(), + logger: logger, + config: config +}; +PROXY.playerStats.onlineCount = PROXY.players.size; +let server; +if (PROXY.config.security.enabled) { + logger.info(`Starting SECURE WebSocket proxy on port ${config.port}...`); + if (process.env.REPL_SLUG) { + logger.warn("You appear to be running the proxy on Repl.it with encryption enabled. Please note that Repl.it by default provides encryption, and enabling encryption may or may not prevent you from connecting to the server."); + } + server = new WebSocketServer({ + server: https.createServer({ + key: readFileSync(config.security.key), + cert: readFileSync(config.security.cert) + }).listen(config.port) + }); +} +else { + logger.info(`Starting INSECURE WebSocket proxy on port ${config.port}...`); + server = new WebSocketServer({ + port: config.port + }); +} +PROXY.wsServer = server; +server.addListener('connection', c => { + connectionLogger.debug(`[CONNECTION] New inbound WebSocket connection from [/${c._socket.remoteAddress}:${c._socket.remotePort}]. (${c._socket.remotePort} -> ${config.port})`); + const plr = new ProxiedPlayer(); + plr.ws = c; + plr.ip = c._socket.remoteAddress; + plr.remotePort = c._socket.remotePort; + plr.state = State.PRE_HANDSHAKE; + c.on('message', msg => { + handlePacket(msg, plr); + }); +}); +server.on('listening', () => { + logger.info(`Successfully started${config.security.enabled ? " [secure]" : ""} WebSocket proxy on port ${config.port}!`); +}); diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..0e9a513 --- /dev/null +++ b/index.ts @@ -0,0 +1,76 @@ +import { readFileSync } from "fs"; +import * as http from "http" +import * as https from "https" +import { WebSocketServer } from "ws"; +import { ProxiedPlayer } from "./classes.js"; +import { config } from "./config.js"; +import { handlePacket } from "./listener.js"; +import { Logger } from "./logger.js"; +import { BRANDING, NETWORK_VERSION, VERSION } from "./meta.js"; +import { State } from "./types.js"; +import { genUUID } from "./utils.js"; + +const logger = new Logger("EagXProxy") +const connectionLogger = new Logger("ConnectionHandler") + +global.PROXY = { + brand: BRANDING, + version: VERSION, + MOTDVersion: NETWORK_VERSION, + + serverName: config.name, + secure: false, + proxyUUID: genUUID(config.name), + MOTD: { + icon: null, + motd: [config.motd.l1, config.motd.l2] + }, + + playerStats: { + max: config.maxPlayers, + onlineCount: 0 + }, + wsServer: null, + players: new Map(), + logger: logger, + config: config +} +PROXY.playerStats.onlineCount = PROXY.players.size + +let server: WebSocketServer + +if (PROXY.config.security.enabled) { + logger.info(`Starting SECURE WebSocket proxy on port ${config.port}...`) + if (process.env.REPL_SLUG) { + logger.warn("You appear to be running the proxy on Repl.it with encryption enabled. Please note that Repl.it by default provides encryption, and enabling encryption may or may not prevent you from connecting to the server.") + } + server = new WebSocketServer({ + server: https.createServer({ + key: readFileSync(config.security.key), + cert: readFileSync(config.security.cert) + }).listen(config.port) + }) +} else { + logger.info(`Starting INSECURE WebSocket proxy on port ${config.port}...`) + server = new WebSocketServer({ + port: config.port + }) +} + +PROXY.wsServer = server + +server.addListener('connection', c => { + connectionLogger.debug(`[CONNECTION] New inbound WebSocket connection from [/${(c as any)._socket.remoteAddress}:${(c as any)._socket.remotePort}]. (${(c as any)._socket.remotePort} -> ${config.port})`) + const plr = new ProxiedPlayer() + plr.ws = c + plr.ip = (c as any)._socket.remoteAddress + plr.remotePort = (c as any)._socket.remotePort + plr.state = State.PRE_HANDSHAKE + c.on('message', msg => { + handlePacket(msg as Buffer, plr) + }) +}) + +server.on('listening', () => { + logger.info(`Successfully started${config.security.enabled ? " [secure]" : ""} WebSocket proxy on port ${config.port}!`) +}) \ No newline at end of file diff --git a/listener.js b/listener.js new file mode 100644 index 0000000..7289525 --- /dev/null +++ b/listener.js @@ -0,0 +1,26 @@ +import { Logger } from "./logger.js"; +import { handleMotd } from "./motd.js"; +import { State } from "./types.js"; +import { doHandshake } from "./utils.js"; +const logger = new Logger("PacketHandler"); +export function handlePacket(packet, client) { + if (client.state == State.PRE_HANDSHAKE) { + if (packet.toString() === "Accept: MOTD") { + handleMotd(client); + } + else if (!client._handled) { + ; + client._handled = true; + doHandshake(client, packet); + } + } + else if (client.state == State.POST_HANDSHAKE) { + if (!client.remoteConnection || client.remoteConnection.socket.closed) { + logger.warn(`Player ${client.username} is marked as post handshake, but is disconnected from the game server? Disconnecting due to illegal state.`); + client.ws.close(); + } + else { + client.remoteConnection.writeRaw(packet); + } + } +} diff --git a/listener.ts b/listener.ts new file mode 100644 index 0000000..085854e --- /dev/null +++ b/listener.ts @@ -0,0 +1,25 @@ +import { ProxiedPlayer } from "./classes.js"; +import { Logger } from "./logger.js"; +import { handleMotd } from "./motd.js"; +import { State } from "./types.js"; +import { doHandshake } from "./utils.js"; + +const logger = new Logger("PacketHandler") + +export function handlePacket(packet: Buffer, client: ProxiedPlayer) { + if (client.state == State.PRE_HANDSHAKE) { + if (packet.toString() === "Accept: MOTD") { + handleMotd(client) + } else if (!(client as any)._handled) { + ;(client as any)._handled = true + doHandshake(client, packet) + } + } else if (client.state == State.POST_HANDSHAKE) { + if (!client.remoteConnection || client.remoteConnection.socket.closed) { + logger.warn(`Player ${client.username} is marked as post handshake, but is disconnected from the game server? Disconnecting due to illegal state.`) + client.ws.close() + } else { + client.remoteConnection.writeRaw(packet) + } + } +} \ No newline at end of file diff --git a/logger.js b/logger.js new file mode 100644 index 0000000..ffb53d7 --- /dev/null +++ b/logger.js @@ -0,0 +1,54 @@ +import { Chalk } from "chalk"; +const color = new Chalk({ level: 2 }); +let global_verbose = false; +export function verboseLogging(newVal) { + global_verbose = (newVal !== null && newVal !== void 0 ? newVal : global_verbose) ? false : true; +} +function jsonLog(type, message) { + return JSON.stringify({ + type: type, + message: message + }) + "\n"; +} +export class Logger { + constructor(name, verbose) { + this.jsonLog = process.argv.includes("--json") || process.argv.includes("-j"); + this.loggerName = name; + if (verbose) + this.verbose = verbose; + else + this.verbose = global_verbose; + } + info(s) { + if (!this.jsonLog) + process.stdout.write(`${color.green("I")} ${color.gray(new Date().toISOString())} ${color.reset(`${color.yellow(`${this.loggerName}:`)} ${s}`)}\n`); + else + process.stdout.write(jsonLog("info", s)); + } + warn(s) { + if (!this.jsonLog) + process.stdout.write(`${color.yellow("W")} ${color.gray(new Date().toISOString())} ${color.yellow(`${color.yellow(`${this.loggerName}:`)} ${s}`)}\n`); + else + process.stderr.write(jsonLog("warn", s)); + } + error(s) { + if (!this.jsonLog) + process.stderr.write(`* ${color.red("E")} ${color.gray(new Date().toISOString())} ${color.redBright(`${color.red(`${this.loggerName}:`)} ${s}`)}\n`); + else + process.stderr.write(jsonLog("error", s)); + } + fatal(s) { + if (!this.jsonLog) + process.stderr.write(`** ${color.red("F!")} ${color.gray(new Date().toISOString())} ${color.bgRedBright(color.redBright(`${color.red(`${this.loggerName}:`)} ${s}`))}\n`); + else + process.stderr.write(jsonLog("fatal", s)); + } + debug(s) { + if (this.verbose || global_verbose) { + if (!this.jsonLog) + process.stderr.write(`${color.gray("D")} ${color.gray(new Date().toISOString())} ${color.gray(`${color.gray(`${this.loggerName}:`)} ${s}`)}\n`); + else + process.stderr.write(jsonLog("debug", s)); + } + } +} diff --git a/logger.ts b/logger.ts new file mode 100644 index 0000000..98f4ee4 --- /dev/null +++ b/logger.ts @@ -0,0 +1,61 @@ +import { Chalk } from "chalk" + +const color = new Chalk({ level: 2 }) + +let global_verbose: boolean = false + +type JsonLogType = "info" | "warn" | "error" | "fatal" | "debug" +type JsonOutput = { + type: JsonLogType, + message: string +} + +export function verboseLogging(newVal?: boolean) { + global_verbose = newVal ?? global_verbose ? false : true +} + +function jsonLog(type: JsonLogType, message: string): string { + return JSON.stringify({ + type: type, + message: message + }) + "\n" +} + +export class Logger { + loggerName: string + verbose: boolean + private jsonLog: boolean = process.argv.includes("--json") || process.argv.includes("-j") + + constructor(name: string, verbose?: boolean) { + this.loggerName = name + if (verbose) this.verbose = verbose + else this.verbose = global_verbose + } + + info(s: string) { + if (!this.jsonLog) process.stdout.write(`${color.green("I")} ${color.gray(new Date().toISOString())} ${color.reset(`${color.yellow(`${this.loggerName}:`)} ${s}`)}\n`) + else process.stdout.write(jsonLog("info", s)) + } + + warn(s: string) { + if (!this.jsonLog) process.stdout.write(`${color.yellow("W")} ${color.gray(new Date().toISOString())} ${color.yellow(`${color.yellow(`${this.loggerName}:`)} ${s}`)}\n`) + else process.stderr.write(jsonLog("warn", s)) + } + + error(s: string) { + if (!this.jsonLog) process.stderr.write(`* ${color.red("E")} ${color.gray(new Date().toISOString())} ${color.redBright(`${color.red(`${this.loggerName}:`)} ${s}`)}\n`) + else process.stderr.write(jsonLog("error", s)) + } + + fatal(s: string) { + if (!this.jsonLog) process.stderr.write(`** ${color.red("F!")} ${color.gray(new Date().toISOString())} ${color.bgRedBright(color.redBright(`${color.red(`${this.loggerName}:`)} ${s}`))}\n`) + else process.stderr.write(jsonLog("fatal", s)) + } + + debug(s: string) { + if (this.verbose || global_verbose) { + if (!this.jsonLog) process.stderr.write(`${color.gray("D")} ${color.gray(new Date().toISOString())} ${color.gray(`${color.gray(`${this.loggerName}:`)} ${s}`)}\n`) + else process.stderr.write(jsonLog("debug", s)) + } + } +} \ No newline at end of file diff --git a/meta.js b/meta.js new file mode 100644 index 0000000..a25cb6f --- /dev/null +++ b/meta.js @@ -0,0 +1,3 @@ +export const BRANDING = Object.freeze("EaglerXProxy"); +export const VERSION = "1.0.0"; +export const NETWORK_VERSION = Object.freeze(BRANDING + "/" + VERSION); diff --git a/meta.ts b/meta.ts new file mode 100644 index 0000000..de715cc --- /dev/null +++ b/meta.ts @@ -0,0 +1,3 @@ +export const BRANDING: Readonly = Object.freeze("EaglerXProxy") +export const VERSION: Readonly = "1.0.0" +export const NETWORK_VERSION: Readonly = Object.freeze(BRANDING + "/" + VERSION) \ No newline at end of file diff --git a/motd.js b/motd.js new file mode 100644 index 0000000..e38ab82 --- /dev/null +++ b/motd.js @@ -0,0 +1,34 @@ +export function handleMotd(player) { + const names = []; + for (const [username, player] of PROXY.players) { + if (names.length > 0) { + names.push(`(and ${PROXY.players.size - names.length} more)`); + break; + } + else { + names.push(username); + } + } + player.ws.send(JSON.stringify({ + brand: PROXY.brand, + cracked: true, + data: { + cache: true, + icon: PROXY.MOTD.icon ? true : false, + max: PROXY.playerStats.max, + motd: PROXY.MOTD.motd, + online: PROXY.playerStats.onlineCount, + players: names + }, + name: PROXY.serverName, + secure: false, + time: Date.now(), + type: "motd", + uuid: PROXY.proxyUUID, + vers: PROXY.MOTDVersion + })); + if (PROXY.MOTD.icon) { + player.ws.send(PROXY.MOTD.icon); + } + player.ws.close(); +} diff --git a/motd.ts b/motd.ts new file mode 100644 index 0000000..db12d41 --- /dev/null +++ b/motd.ts @@ -0,0 +1,66 @@ +import { randomUUID } from "crypto"; +import { WebSocket } from "ws"; +import { ProxiedPlayer } from "./classes.js"; +import { UUID } from "./types.js" + +export type MotdPlayer = { + name: string, + id: UUID +} + +export type MotdJSONRes = { + brand: string, + cracked: true, + data: { + cache: true, + icon: boolean, + max: number, + motd: [string, string], + online: number, + players: string[], + }, + name: string, + secure: false, + time: ReturnType, + type: "motd", + uuid: ReturnType, + vers: string +} + +// a 16384 byte array +export type MotdServerLogo = Int8Array + +export function handleMotd(player: Partial) { + const names = [] + for (const [username, player] of PROXY.players) { + if (names.length > 0) { + names.push(`(and ${PROXY.players.size - names.length} more)`) + break + } else { + names.push(username) + } + } + + player.ws.send(JSON.stringify({ + brand: PROXY.brand, + cracked: true, + data: { + cache: true, + icon: PROXY.MOTD.icon ? true : false, + max: PROXY.playerStats.max, + motd: PROXY.MOTD.motd, + online: PROXY.playerStats.onlineCount, + players: names + }, + name: PROXY.serverName, + secure: false, + time: Date.now(), + type: "motd", + uuid: PROXY.proxyUUID, + vers: PROXY.MOTDVersion + } as MotdJSONRes)) + if (PROXY.MOTD.icon) { + player.ws.send(PROXY.MOTD.icon) + } + player.ws.close() +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c8b9bef --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1681 @@ +{ + "name": "eaglerxbungee-reimpl", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@thi.ng/leb128": "^3.0.1", + "@types/node": "^18.11.16", + "@types/uuid": "^9.0.0", + "@types/ws": "^8.5.3", + "chalk": "^5.2.0", + "minecraft-protocol": "^1.36.2", + "tsc": "^2.0.4", + "uuid": "^9.0.0", + "uuid-buffer": "^1.0.3", + "ws": "^8.11.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.0.1.tgz", + "integrity": "sha512-eNNHIW/cwPTZDWs9KtYgb1X6gtQ+cC+FGX2YN+t4AUVsBdUbqlMTnUs6/c/VBxC2AAGIhgLREuNnO3F66AN2zQ==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.14.5.tgz", + "integrity": "sha512-NcVdMfn8Z3ogN+9RjOSF7uwf2Gki5DEJl0BdDSL83KUAgVAobtkZi5W8EqxbJLrTO/ET0jv5DregrcR5qg2pEA==", + "dependencies": { + "@azure/msal-common": "^9.0.1", + "jsonwebtoken": "^8.5.1", + "uuid": "^8.3.0" + }, + "engines": { + "node": "10 || 12 || 14 || 16 || 18" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@thi.ng/api": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@thi.ng/api/-/api-8.6.0.tgz", + "integrity": "sha512-Gd30r7SEzByG+ymyrR9/Weh++Y+6e7a6+28NGf+9ja+Qje2bbqPBxpONdooyLDPb+1DjqQ3SwDLTMyHAOJcUSg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/arrays": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@thi.ng/arrays/-/arrays-2.4.5.tgz", + "integrity": "sha512-YFzpHIUxtetobyaFoMKGqWdaPHnCa5n7TEYpGX/Af7kN5cY7itrFW/zsjbl8lrQIpMYFRq71ItYjpmudkejR9w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/compare": "^2.1.19", + "@thi.ng/equiv": "^2.1.15", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/random": "^3.3.18" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/binary": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/@thi.ng/binary/-/binary-3.3.13.tgz", + "integrity": "sha512-Ibv6MEzx5a77BizzRbTvKWApcXreHxCYpLl+RJR26nWVHNGUO4SVX5z9s6YShyYHSBE1xNWYr57RoFgbB7Solg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/checks": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@thi.ng/checks/-/checks-3.3.5.tgz", + "integrity": "sha512-niNsu1mL6ZdYKx81OxjLUmKtShqCiJhusB/haUQu/4sih2ttmAckSq/UsCMQzi0GiyZw0zmQHlHkIl9fWhRrkg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/compare": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@thi.ng/compare/-/compare-2.1.19.tgz", + "integrity": "sha512-5TgbCD8kYk3P9kcERxcZBSVSgbxc2d6fcDY7t3QRHaaM2dGeXJXODEbUJ8HGgHscbKtAZQnIfW8h+BNOb2Q51Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/compose": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/@thi.ng/compose/-/compose-2.1.21.tgz", + "integrity": "sha512-Ny+zrsZ3MiiSXWuW52fcO5hblQXFrJI58aUAgZ/h7GSpeceJS4UtcHqu0OmykSuaflMP95WTs8IDDzPiO/2BVQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/errors": "^2.2.6" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/equiv": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@thi.ng/equiv/-/equiv-2.1.15.tgz", + "integrity": "sha512-QN5OjHr9JvEZYvwYyKeQkrfqa2RpYZIXOUw/N6k1Vpgt6fjaL2ZMUk8+sLzIB7qdVJtS/76lQwZJ62Csa/49zw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/errors": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.2.6.tgz", + "integrity": "sha512-hXhUdjCbTNZcC/pkkHgm38gwSlul9Ii6ahap3nJR14L5WbqrbooejAkq60GDHQm2KIbEbLfvyiN2BGId3y4+Bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/hex": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@thi.ng/hex/-/hex-2.3.2.tgz", + "integrity": "sha512-vLFWGh8D+U5RiYJIb3iouCrCTwpEfnax2j3X2nOyn0sdaDQc/FR//r/d9ZqNpeRZMR5GssQlh+s2x/m9mw0QGg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/leb128": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@thi.ng/leb128/-/leb128-3.0.1.tgz", + "integrity": "sha512-sqp1T1xj0PfJSNFJDzmNSmi22KJPcX0l12AkFQxhhYvffWuGugqv//dGkWYMRbl+b0xSsRbUoUSQT6kiVSyonA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/checks": "^3.3.5", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/transducers-binary": "^2.1.31" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/math": { + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/@thi.ng/math/-/math-5.3.16.tgz", + "integrity": "sha512-0nsAhYy/HNYEPhSk8g0aYhR5L4jnnPndhn7M3fyMeHn0bk0KbKaB+eJ4Tb7gsDG/OUbJM3Ovsn6304GCmWkAtw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/random": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/@thi.ng/random/-/random-3.3.18.tgz", + "integrity": "sha512-TD0mmidRUFeXgvvj4I4pE8ljTh+0zeU9whyLYXHehlQjU3qRpkSXXVSDyTTr5tXEi95b604uUpbqWXaUCmJAFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/hex": "^2.3.2" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/transducers": { + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@thi.ng/transducers/-/transducers-8.3.26.tgz", + "integrity": "sha512-fP79ip4WyGKn/OjYjmrqlGpvZbZq/48OVyzT8fScI8lMJGYaQRDTh6LDN+91RgSC0EdjrVO9QcIucDIlIdWdow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/arrays": "^2.4.5", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/compare": "^2.1.19", + "@thi.ng/compose": "^2.1.21", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/math": "^5.3.16", + "@thi.ng/random": "^3.3.18" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@thi.ng/transducers-binary": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/@thi.ng/transducers-binary/-/transducers-binary-2.1.31.tgz", + "integrity": "sha512-pVKbe6M7bLLDw4rRTJN6+6amzg+BNlm9PfzbVpv6qqNmHqzHfBJTfNF5Zw16dY9BPP8NcCV11zfZx+iMN1c6RA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + } + ], + "dependencies": { + "@thi.ng/binary": "^3.3.13", + "@thi.ng/compose": "^2.1.21", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/hex": "^2.3.2", + "@thi.ng/random": "^3.3.18", + "@thi.ng/transducers": "^8.3.26" + }, + "engines": { + "node": ">=12.7" + } + }, + "node_modules/@types/node": { + "version": "18.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.16.tgz", + "integrity": "sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA==" + }, + "node_modules/@types/readable-stream": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", + "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==" + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xboxreplay/errors": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@xboxreplay/errors/-/errors-0.1.0.tgz", + "integrity": "sha512-Tgz1d/OIPDWPeyOvuL5+aai5VCcqObhPnlI3skQuf80GVF3k1I0lPCnGC+8Cm5PV9aLBT5m8qPcJoIUQ2U4y9g==" + }, + "node_modules/@xboxreplay/xboxlive-auth": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@xboxreplay/xboxlive-auth/-/xboxlive-auth-3.3.3.tgz", + "integrity": "sha512-j0AU8pW10LM8O68CTZ5QHnvOjSsnPICy0oQcP7zyM7eWkDQ/InkiQiirQKsPn1XRYDl4ccNu0WM582s3UKwcBg==", + "dependencies": { + "@xboxreplay/errors": "^0.1.0", + "axios": "^0.21.1" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha512-6i37w/+EhlWlGUJff3T/Q8u1RGmP5wgbiwYnOnbOqvtrPxT63/sYFyP9RcpxtxGymtfA075IvmOnL7ycNOWl3w==" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/endian-toggle": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/endian-toggle/-/endian-toggle-0.0.0.tgz", + "integrity": "sha512-ShfqhXeHRE4TmggSlHXG8CMGIcsOsqDw/GcoPcosToE59Rm9e4aXaMhEQf2kPBsBRrKem1bbOAv5gOKnkliMFQ==" + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/jose": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "node_modules/macaddress": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.5.3.tgz", + "integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==" + }, + "node_modules/minecraft-data": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.20.0.tgz", + "integrity": "sha512-xBQZwY0FoVq7jirFTD8mexNatSddP72CpDYweISHKE3J0co+CgfjtvQ94ykdQW+QPTMgrIIlATxVGIlk0Xjm8w==" + }, + "node_modules/minecraft-folder-path": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minecraft-folder-path/-/minecraft-folder-path-1.2.0.tgz", + "integrity": "sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==" + }, + "node_modules/minecraft-protocol": { + "version": "1.36.2", + "resolved": "https://registry.npmjs.org/minecraft-protocol/-/minecraft-protocol-1.36.2.tgz", + "integrity": "sha512-Z7wNk1FTe0bnlmHxJEbpVnIpWLOsAfjoKDFR6DbUHVuL+rNuG9gR/Z3la3a20o4ZrlnToV6vbJkkPhvtn1rSYg==", + "dependencies": { + "@types/readable-stream": "^2.3.13", + "aes-js": "^3.1.2", + "buffer-equal": "^1.0.0", + "debug": "^4.3.2", + "endian-toggle": "^0.0.0", + "lodash.get": "^4.1.2", + "lodash.merge": "^4.3.0", + "minecraft-data": "^3.8.0", + "minecraft-folder-path": "^1.2.0", + "node-fetch": "^2.6.1", + "node-rsa": "^0.4.2", + "prismarine-auth": "^2.0.0", + "prismarine-nbt": "^2.0.0", + "protodef": "^1.8.0", + "readable-stream": "^4.1.0", + "uuid-1345": "^1.0.1", + "yggdrasil": "^1.4.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-rsa": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", + "integrity": "sha512-Bvso6Zi9LY4otIZefYrscsUpo2mUpiAVIEmSZV2q41sP8tHZoert3Yu6zv4f/RXJqMNZQKCtnhDugIuCma23YA==", + "dependencies": { + "asn1": "0.2.3" + } + }, + "node_modules/prismarine-auth": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/prismarine-auth/-/prismarine-auth-2.1.1.tgz", + "integrity": "sha512-KPWA2ZL4Tewl2gDKL2WT9yUSRKfE7MNgQ++RY+mSDUo+dJpRsqKnR7VZIhyRn8ai/iA8v/SFc/MmJiZ2TsZGmQ==", + "dependencies": { + "@azure/msal-node": "^1.1.0", + "@xboxreplay/xboxlive-auth": "^3.3.3", + "debug": "^4.3.3", + "jose": "^4.1.4", + "node-fetch": "^2.6.1", + "smart-buffer": "^4.1.0", + "uuid-1345": "^1.0.2" + } + }, + "node_modules/prismarine-nbt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prismarine-nbt/-/prismarine-nbt-2.2.1.tgz", + "integrity": "sha512-Mb50c58CPnuZ+qvM31DBa08tf9UumlTq1LkvpMoUpKfCuN05GZHTqCUwER3lxTSHLL0GZKghIPbYR/JQkINijQ==", + "dependencies": { + "protodef": "^1.9.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/protodef": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/protodef/-/protodef-1.15.0.tgz", + "integrity": "sha512-bZ2Omw8dT+DACjJHLrBWZlqN4MlT9g9oSpJDdkUAJOStUzgJp+Zn42FJfPUdwutUxjaxA0PftN0PDlNa2XbneA==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.reduce": "^4.6.0", + "protodef-validator": "^1.3.0", + "readable-stream": "^3.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/protodef-validator": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/protodef-validator/-/protodef-validator-1.3.1.tgz", + "integrity": "sha512-lZ5FWKZYR9xOjpMw1+EfZRfCjzNRQWPq+Dk+jki47Sikl2EeWEPnTfnJERwnU/EwFq6us+0zqHHzSsmLeYX+Lg==", + "dependencies": { + "ajv": "^6.5.4" + }, + "bin": { + "protodef-validator": "cli.js" + } + }, + "node_modules/protodef/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/readable-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", + "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tsc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz", + "integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==", + "bin": { + "tsc": "bin/tsc" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uuid-1345": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uuid-1345/-/uuid-1345-1.0.2.tgz", + "integrity": "sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==", + "dependencies": { + "macaddress": "^0.5.1" + } + }, + "node_modules/uuid-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/uuid-buffer/-/uuid-buffer-1.0.3.tgz", + "integrity": "sha512-UGuYz0MDQRNf/n8PrO2OXGyl4PDUP4zURZFB+GwJuYL2cDAo39rfaI2R1MI8skh3rns/znegRu8ik/TDlzwp+w==", + "engines": { + "node": ">6.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yggdrasil": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/yggdrasil/-/yggdrasil-1.7.0.tgz", + "integrity": "sha512-QBIo5fiNd7688G3FqXXYGr36uyrYzczlNuzpWFy2zL3+R+3KT2lF+wFxm51synfA3l3z6IBiGOc1/EVXWCYY1Q==", + "dependencies": { + "node-fetch": "^2.6.1", + "uuid": "^8.2.0" + } + }, + "node_modules/yggdrasil/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + } + }, + "dependencies": { + "@azure/msal-common": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-9.0.1.tgz", + "integrity": "sha512-eNNHIW/cwPTZDWs9KtYgb1X6gtQ+cC+FGX2YN+t4AUVsBdUbqlMTnUs6/c/VBxC2AAGIhgLREuNnO3F66AN2zQ==" + }, + "@azure/msal-node": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.14.5.tgz", + "integrity": "sha512-NcVdMfn8Z3ogN+9RjOSF7uwf2Gki5DEJl0BdDSL83KUAgVAobtkZi5W8EqxbJLrTO/ET0jv5DregrcR5qg2pEA==", + "requires": { + "@azure/msal-common": "^9.0.1", + "jsonwebtoken": "^8.5.1", + "uuid": "^8.3.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "@thi.ng/api": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/@thi.ng/api/-/api-8.6.0.tgz", + "integrity": "sha512-Gd30r7SEzByG+ymyrR9/Weh++Y+6e7a6+28NGf+9ja+Qje2bbqPBxpONdooyLDPb+1DjqQ3SwDLTMyHAOJcUSg==" + }, + "@thi.ng/arrays": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@thi.ng/arrays/-/arrays-2.4.5.tgz", + "integrity": "sha512-YFzpHIUxtetobyaFoMKGqWdaPHnCa5n7TEYpGX/Af7kN5cY7itrFW/zsjbl8lrQIpMYFRq71ItYjpmudkejR9w==", + "requires": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/compare": "^2.1.19", + "@thi.ng/equiv": "^2.1.15", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/random": "^3.3.18" + } + }, + "@thi.ng/binary": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/@thi.ng/binary/-/binary-3.3.13.tgz", + "integrity": "sha512-Ibv6MEzx5a77BizzRbTvKWApcXreHxCYpLl+RJR26nWVHNGUO4SVX5z9s6YShyYHSBE1xNWYr57RoFgbB7Solg==", + "requires": { + "@thi.ng/api": "^8.6.0" + } + }, + "@thi.ng/checks": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@thi.ng/checks/-/checks-3.3.5.tgz", + "integrity": "sha512-niNsu1mL6ZdYKx81OxjLUmKtShqCiJhusB/haUQu/4sih2ttmAckSq/UsCMQzi0GiyZw0zmQHlHkIl9fWhRrkg==", + "requires": { + "tslib": "^2.4.1" + } + }, + "@thi.ng/compare": { + "version": "2.1.19", + "resolved": "https://registry.npmjs.org/@thi.ng/compare/-/compare-2.1.19.tgz", + "integrity": "sha512-5TgbCD8kYk3P9kcERxcZBSVSgbxc2d6fcDY7t3QRHaaM2dGeXJXODEbUJ8HGgHscbKtAZQnIfW8h+BNOb2Q51Q==", + "requires": { + "@thi.ng/api": "^8.6.0" + } + }, + "@thi.ng/compose": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/@thi.ng/compose/-/compose-2.1.21.tgz", + "integrity": "sha512-Ny+zrsZ3MiiSXWuW52fcO5hblQXFrJI58aUAgZ/h7GSpeceJS4UtcHqu0OmykSuaflMP95WTs8IDDzPiO/2BVQ==", + "requires": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/errors": "^2.2.6" + } + }, + "@thi.ng/equiv": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@thi.ng/equiv/-/equiv-2.1.15.tgz", + "integrity": "sha512-QN5OjHr9JvEZYvwYyKeQkrfqa2RpYZIXOUw/N6k1Vpgt6fjaL2ZMUk8+sLzIB7qdVJtS/76lQwZJ62Csa/49zw==" + }, + "@thi.ng/errors": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.2.6.tgz", + "integrity": "sha512-hXhUdjCbTNZcC/pkkHgm38gwSlul9Ii6ahap3nJR14L5WbqrbooejAkq60GDHQm2KIbEbLfvyiN2BGId3y4+Bw==" + }, + "@thi.ng/hex": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@thi.ng/hex/-/hex-2.3.2.tgz", + "integrity": "sha512-vLFWGh8D+U5RiYJIb3iouCrCTwpEfnax2j3X2nOyn0sdaDQc/FR//r/d9ZqNpeRZMR5GssQlh+s2x/m9mw0QGg==" + }, + "@thi.ng/leb128": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@thi.ng/leb128/-/leb128-3.0.1.tgz", + "integrity": "sha512-sqp1T1xj0PfJSNFJDzmNSmi22KJPcX0l12AkFQxhhYvffWuGugqv//dGkWYMRbl+b0xSsRbUoUSQT6kiVSyonA==", + "requires": { + "@thi.ng/checks": "^3.3.5", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/transducers-binary": "^2.1.31" + } + }, + "@thi.ng/math": { + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/@thi.ng/math/-/math-5.3.16.tgz", + "integrity": "sha512-0nsAhYy/HNYEPhSk8g0aYhR5L4jnnPndhn7M3fyMeHn0bk0KbKaB+eJ4Tb7gsDG/OUbJM3Ovsn6304GCmWkAtw==", + "requires": { + "@thi.ng/api": "^8.6.0" + } + }, + "@thi.ng/random": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/@thi.ng/random/-/random-3.3.18.tgz", + "integrity": "sha512-TD0mmidRUFeXgvvj4I4pE8ljTh+0zeU9whyLYXHehlQjU3qRpkSXXVSDyTTr5tXEi95b604uUpbqWXaUCmJAFA==", + "requires": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/hex": "^2.3.2" + } + }, + "@thi.ng/transducers": { + "version": "8.3.26", + "resolved": "https://registry.npmjs.org/@thi.ng/transducers/-/transducers-8.3.26.tgz", + "integrity": "sha512-fP79ip4WyGKn/OjYjmrqlGpvZbZq/48OVyzT8fScI8lMJGYaQRDTh6LDN+91RgSC0EdjrVO9QcIucDIlIdWdow==", + "requires": { + "@thi.ng/api": "^8.6.0", + "@thi.ng/arrays": "^2.4.5", + "@thi.ng/checks": "^3.3.5", + "@thi.ng/compare": "^2.1.19", + "@thi.ng/compose": "^2.1.21", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/math": "^5.3.16", + "@thi.ng/random": "^3.3.18" + } + }, + "@thi.ng/transducers-binary": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/@thi.ng/transducers-binary/-/transducers-binary-2.1.31.tgz", + "integrity": "sha512-pVKbe6M7bLLDw4rRTJN6+6amzg+BNlm9PfzbVpv6qqNmHqzHfBJTfNF5Zw16dY9BPP8NcCV11zfZx+iMN1c6RA==", + "requires": { + "@thi.ng/binary": "^3.3.13", + "@thi.ng/compose": "^2.1.21", + "@thi.ng/errors": "^2.2.6", + "@thi.ng/hex": "^2.3.2", + "@thi.ng/random": "^3.3.18", + "@thi.ng/transducers": "^8.3.26" + } + }, + "@types/node": { + "version": "18.11.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.16.tgz", + "integrity": "sha512-6T7P5bDkRhqRxrQtwj7vru+bWTpelgtcETAZEUSdq0YISKz8WKdoBukQLYQQ6DFHvU9JRsbFq0JH5C51X2ZdnA==" + }, + "@types/readable-stream": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", + "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", + "requires": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==" + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "@xboxreplay/errors": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@xboxreplay/errors/-/errors-0.1.0.tgz", + "integrity": "sha512-Tgz1d/OIPDWPeyOvuL5+aai5VCcqObhPnlI3skQuf80GVF3k1I0lPCnGC+8Cm5PV9aLBT5m8qPcJoIUQ2U4y9g==" + }, + "@xboxreplay/xboxlive-auth": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@xboxreplay/xboxlive-auth/-/xboxlive-auth-3.3.3.tgz", + "integrity": "sha512-j0AU8pW10LM8O68CTZ5QHnvOjSsnPICy0oQcP7zyM7eWkDQ/InkiQiirQKsPn1XRYDl4ccNu0WM582s3UKwcBg==", + "requires": { + "@xboxreplay/errors": "^0.1.0", + "axios": "^0.21.1" + } + }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha512-6i37w/+EhlWlGUJff3T/Q8u1RGmP5wgbiwYnOnbOqvtrPxT63/sYFyP9RcpxtxGymtfA075IvmOnL7ycNOWl3w==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==" + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "endian-toggle": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/endian-toggle/-/endian-toggle-0.0.0.tgz", + "integrity": "sha512-ShfqhXeHRE4TmggSlHXG8CMGIcsOsqDw/GcoPcosToE59Rm9e4aXaMhEQf2kPBsBRrKem1bbOAv5gOKnkliMFQ==" + }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "jose": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==" + }, + "macaddress": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.5.3.tgz", + "integrity": "sha512-vGBKTA+jwM4KgjGZ+S/8/Mkj9rWzePyGY6jManXPGhiWu63RYwW8dKPyk5koP+8qNVhPhHgFa1y/MJ4wrjsNrg==" + }, + "minecraft-data": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/minecraft-data/-/minecraft-data-3.20.0.tgz", + "integrity": "sha512-xBQZwY0FoVq7jirFTD8mexNatSddP72CpDYweISHKE3J0co+CgfjtvQ94ykdQW+QPTMgrIIlATxVGIlk0Xjm8w==" + }, + "minecraft-folder-path": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minecraft-folder-path/-/minecraft-folder-path-1.2.0.tgz", + "integrity": "sha512-qaUSbKWoOsH9brn0JQuBhxNAzTDMwrOXorwuRxdJKKKDYvZhtml+6GVCUrY5HRiEsieBEjCUnhVpDuQiKsiFaw==" + }, + "minecraft-protocol": { + "version": "1.36.2", + "resolved": "https://registry.npmjs.org/minecraft-protocol/-/minecraft-protocol-1.36.2.tgz", + "integrity": "sha512-Z7wNk1FTe0bnlmHxJEbpVnIpWLOsAfjoKDFR6DbUHVuL+rNuG9gR/Z3la3a20o4ZrlnToV6vbJkkPhvtn1rSYg==", + "requires": { + "@types/readable-stream": "^2.3.13", + "aes-js": "^3.1.2", + "buffer-equal": "^1.0.0", + "debug": "^4.3.2", + "endian-toggle": "^0.0.0", + "lodash.get": "^4.1.2", + "lodash.merge": "^4.3.0", + "minecraft-data": "^3.8.0", + "minecraft-folder-path": "^1.2.0", + "node-fetch": "^2.6.1", + "node-rsa": "^0.4.2", + "prismarine-auth": "^2.0.0", + "prismarine-nbt": "^2.0.0", + "protodef": "^1.8.0", + "readable-stream": "^4.1.0", + "uuid-1345": "^1.0.1", + "yggdrasil": "^1.4.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-rsa": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", + "integrity": "sha512-Bvso6Zi9LY4otIZefYrscsUpo2mUpiAVIEmSZV2q41sP8tHZoert3Yu6zv4f/RXJqMNZQKCtnhDugIuCma23YA==", + "requires": { + "asn1": "0.2.3" + } + }, + "prismarine-auth": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/prismarine-auth/-/prismarine-auth-2.1.1.tgz", + "integrity": "sha512-KPWA2ZL4Tewl2gDKL2WT9yUSRKfE7MNgQ++RY+mSDUo+dJpRsqKnR7VZIhyRn8ai/iA8v/SFc/MmJiZ2TsZGmQ==", + "requires": { + "@azure/msal-node": "^1.1.0", + "@xboxreplay/xboxlive-auth": "^3.3.3", + "debug": "^4.3.3", + "jose": "^4.1.4", + "node-fetch": "^2.6.1", + "smart-buffer": "^4.1.0", + "uuid-1345": "^1.0.2" + } + }, + "prismarine-nbt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prismarine-nbt/-/prismarine-nbt-2.2.1.tgz", + "integrity": "sha512-Mb50c58CPnuZ+qvM31DBa08tf9UumlTq1LkvpMoUpKfCuN05GZHTqCUwER3lxTSHLL0GZKghIPbYR/JQkINijQ==", + "requires": { + "protodef": "^1.9.0" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "protodef": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/protodef/-/protodef-1.15.0.tgz", + "integrity": "sha512-bZ2Omw8dT+DACjJHLrBWZlqN4MlT9g9oSpJDdkUAJOStUzgJp+Zn42FJfPUdwutUxjaxA0PftN0PDlNa2XbneA==", + "requires": { + "lodash.get": "^4.4.2", + "lodash.reduce": "^4.6.0", + "protodef-validator": "^1.3.0", + "readable-stream": "^3.0.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "protodef-validator": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/protodef-validator/-/protodef-validator-1.3.1.tgz", + "integrity": "sha512-lZ5FWKZYR9xOjpMw1+EfZRfCjzNRQWPq+Dk+jki47Sikl2EeWEPnTfnJERwnU/EwFq6us+0zqHHzSsmLeYX+Lg==", + "requires": { + "ajv": "^6.5.4" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "readable-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.2.0.tgz", + "integrity": "sha512-gJrBHsaI3lgBoGMW/jHZsQ/o/TIWiu5ENCJG1BB7fuCKzpFM8GaS2UoBVt9NO+oI+3FcrBNbUkl3ilDe09aY4A==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tsc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz", + "integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==" + }, + "tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, + "uuid-1345": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uuid-1345/-/uuid-1345-1.0.2.tgz", + "integrity": "sha512-bA5zYZui+3nwAc0s3VdGQGBfbVsJLVX7Np7ch2aqcEWFi5lsAEcmO3+lx3djM1npgpZI8KY2FITZ2uYTnYUYyw==", + "requires": { + "macaddress": "^0.5.1" + } + }, + "uuid-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/uuid-buffer/-/uuid-buffer-1.0.3.tgz", + "integrity": "sha512-UGuYz0MDQRNf/n8PrO2OXGyl4PDUP4zURZFB+GwJuYL2cDAo39rfaI2R1MI8skh3rns/znegRu8ik/TDlzwp+w==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "requires": {} + }, + "yggdrasil": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/yggdrasil/-/yggdrasil-1.7.0.tgz", + "integrity": "sha512-QBIo5fiNd7688G3FqXXYGr36uyrYzczlNuzpWFy2zL3+R+3KT2lF+wFxm51synfA3l3z6IBiGOc1/EVXWCYY1Q==", + "requires": { + "node-fetch": "^2.6.1", + "uuid": "^8.2.0" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b4f4d78 --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "@thi.ng/leb128": "^3.0.1", + "@types/node": "^18.11.16", + "@types/uuid": "^9.0.0", + "@types/ws": "^8.5.3", + "chalk": "^5.2.0", + "minecraft-protocol": "^1.36.2", + "tsc": "^2.0.4", + "uuid": "^9.0.0", + "uuid-buffer": "^1.0.3", + "ws": "^8.11.0" + }, + "type": "module" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..28f63ac --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "esnext", + "esModuleInterop": true, + "target": "es2017", + "moduleResolution": "node", + "sourceMap": false + } + } \ No newline at end of file diff --git a/types.js b/types.js new file mode 100644 index 0000000..7744d66 --- /dev/null +++ b/types.js @@ -0,0 +1,6 @@ +export var State; +(function (State) { + State[State["PRE_HANDSHAKE"] = 0] = "PRE_HANDSHAKE"; + State[State["POST_HANDSHAKE"] = 1] = "POST_HANDSHAKE"; + State[State["DISCONNECTED"] = 2] = "DISCONNECTED"; +})(State || (State = {})); diff --git a/types.ts b/types.ts new file mode 100644 index 0000000..32a2dfa --- /dev/null +++ b/types.ts @@ -0,0 +1,60 @@ +import { randomUUID } from "crypto" +import { WebSocketServer } from "ws" +import { ProxiedPlayer } from "./classes.js" +import { Logger } from "./logger.js" +import { BRANDING, NETWORK_VERSION, VERSION } from "./meta.js" + +export type UUID = ReturnType + +export enum State { + PRE_HANDSHAKE, + POST_HANDSHAKE, + DISCONNECTED +} + +export type MOTD = { + icon?: Int8Array, // 16384 + motd: [string, string] +} + +export type PlayerStats = { + max: number, + onlineCount: number +} + +export type ProxyGlobals = { + brand: typeof BRANDING, + version: typeof VERSION, + MOTDVersion: typeof NETWORK_VERSION, + + serverName: string, + secure: false, + proxyUUID: UUID, + MOTD: MOTD, + + playerStats: PlayerStats, + wsServer: WebSocketServer, + players: Map, + logger: Logger, + config: Config +} + +export type Config = { + name: string, + port: number, + maxPlayers: number, + motd: { + iconURL?: string, + l1: string, + l2: string + }, + server: { + host: string, + port: number + }, + security: { + enabled: boolean + key: string, + cert: string + } +} \ No newline at end of file diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..375ab53 --- /dev/null +++ b/utils.js @@ -0,0 +1,229 @@ +import { v3 } from "uuid"; +import { encodeULEB128 as encodeVarInt, decodeULEB128 as decodeVarInt, decodeSLEB128 as decodeSVarInt } from "@thi.ng/leb128"; +import { DisconnectReason, EaglerPacketId, MAGIC_ENDING_IDENTIFYS_BYTES } from "./eaglerPacketDef.js"; +import { Logger } from "./logger.js"; +import { State } from "./types.js"; +import { toBuffer } from "uuid-buffer"; +import * as mc from "minecraft-protocol"; +import { config } from "./config.js"; +const MAGIC_UUID = "a7e774bc-7ea4-11ed-9a58-1f9e14304a59"; +const logger = new Logger("LoginHandler"); +const USERNAME_REGEX = /[^0-9^a-z^A-Z^_]/gi; +export function genUUID(user) { + return v3(user, MAGIC_UUID); +} +export function bufferizeUUID(uuid) { + return toBuffer(uuid); +} +export function validateUsername(user) { + if (user.length > 20) + throw new Error("Username is too long!"); + if (!!user.match(USERNAME_REGEX)) + throw new Error("Invalid username. Username can only contain alphanumeric characters, and the underscore (_) character."); +} +export function disconnect(player, message, code) { + if (player.state == State.POST_HANDSHAKE) { + const messageLen = encodeVarInt(message.length); + const d = Buffer.alloc(1 + messageLen.length + message.length); + d.set([0x40, ...messageLen, ...Buffer.from(message)]); + player.ws.send(d); + player.ws.close(); + } + else { + const messageLen = encodeVarInt(message.length), codeEnc = encodeVarInt(code !== null && code !== void 0 ? code : DisconnectReason.CUSTOM); + const d = Buffer.alloc(1 + codeEnc.length + messageLen.length + message.length); + d.set([0xff, ...codeEnc, ...messageLen, ...Buffer.from(message)]); + player.ws.send(d); + player.ws.close(); + } +} +export function awaitPacket(ws, id) { + return new Promise((res, rej) => { + let resolved = false; + const msgCb = (msg) => { + if (id != null && msg[0] == id) { + resolved = true; + ws.removeEventListener('message', msgCb); + ws.removeEventListener('close', discon); + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2); + res(msg); + } + else if (id == null) { + resolved = true; + ws.removeEventListener('message', msgCb); + ws.removeEventListener('close', discon); + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2); + res(msg); + } + }; + const discon = () => { + resolved = true; + ws.removeEventListener('message', msgCb); + ws.removeEventListener('close', discon); + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2); + rej("Connection closed"); + }; + ws.setMaxListeners(ws.getMaxListeners() + 2); + ws.on('message', msgCb); + ws.on('close', discon); + setTimeout(() => { + ws.removeEventListener('message', msgCb); + ws.removeEventListener('close', discon); + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2); + rej("Timed out"); + }, 10000); + }); +} +export function loginServer(ip, port, client) { + return new Promise((res, rej) => { + let receivedCompression = false; + const mcClient = mc.createClient({ + host: ip, + port: port, + auth: 'offline', + version: '1.8.8', + username: client.username + }); + mcClient.on('error', err => { + mcClient.end(); + rej(err); + }); + mcClient.on('end', () => { + client.ws.close(); + }); + mcClient.on('connect', () => { + client.remoteConnection = mcClient; + logger.info(`Player ${client.username} has been connected to the server.`); + res(); + }); + mcClient.on('raw', p => { + if (p[0] == 0x03 && !receivedCompression) { + receivedCompression = true; + const compT = { + id: null, + thres: null + }; + const id = decodeVarInt(p); + compT.id = Number(id[0]); + const thres = decodeSVarInt(p.subarray(id[1])); + compT.thres = thres[0]; + client.compressionThreshold = compT.thres; + client.ws.send(p); + } + else { + client.ws.send(p); + } + }); + }); +} +export async function doHandshake(client, initialPacket) { + client.ws.on('close', () => { + client.state = State.DISCONNECTED; + if (client.remoteConnection) { + client.remoteConnection.end(); + } + PROXY.players.delete(client.username); + PROXY.playerStats.onlineCount -= 1; + logger.info(`Client [/${client.ip}:${client.remotePort}]${client.username ? ` (${client.username})` : ""} disconnected from the server.`); + }); + if (PROXY.players.size + 1 > PROXY.playerStats.max) { + disconnect(client, "The proxy is full!", DisconnectReason.CUSTOM); + return; + } + const identifyC = { + id: null, + brandingLen: null, + branding: null, + verLen: null, + ver: null + }; + if (true) { + // save namespace by nesting func declarations in a if true statement + const Iid = decodeVarInt(initialPacket); + identifyC.id = Number(Iid[0]); + const brandingLen = decodeVarInt(initialPacket.subarray(Iid[1] + 2)); + identifyC.brandingLen = Number(brandingLen[0]); + identifyC.branding = initialPacket.subarray(brandingLen[1] + Iid[1] + 2, brandingLen[1] + Iid[1] + Number(brandingLen[0]) + 2).toString(); + const verLen = decodeVarInt(initialPacket.subarray(brandingLen[1] + Iid[1] + Number(brandingLen[0]) + 2)); + identifyC.verLen = Number(verLen[0]); + identifyC.ver = initialPacket.subarray(brandingLen[1] + Number(brandingLen[0]) + Iid[1] + verLen[1] + 2).toString(); + } + if (true) { + const brandingLen = encodeVarInt(PROXY.brand.length), brand = PROXY.brand; + const verLen = encodeVarInt(PROXY.version.length), version = PROXY.version; + const buff = Buffer.alloc(2 + MAGIC_ENDING_IDENTIFYS_BYTES.length + brandingLen.length + brand.length + verLen.length + version.length); + buff.set([EaglerPacketId.IDENTIFY_SERVER, 0x01, ...brandingLen, ...Buffer.from(brand), ...verLen, ...Buffer.from(version), ...Buffer.from(MAGIC_ENDING_IDENTIFYS_BYTES)]); + client.ws.send(buff); + } + client.clientBrand = identifyC.branding; + const login = await awaitPacket(client.ws); + const loginP = { + id: null, + usernameLen: null, + username: null, + randomStrLen: null, + randomStr: null, + nullByte: null + }; + if (login[0] === EaglerPacketId.LOGIN) { + const Iid = decodeVarInt(login); + loginP.id = Number(Iid[0]); + const usernameLen = decodeVarInt(login.subarray(loginP[1])); + loginP.usernameLen = Number(usernameLen[0]); + loginP.username = login.subarray(Iid[1] + usernameLen[1], Iid[1] + usernameLen[1] + loginP.usernameLen).toString(); + const randomStrLen = decodeVarInt(login.subarray(Iid[1] + usernameLen[1] + loginP.usernameLen)); + loginP.randomStrLen = Number(randomStrLen[0]); + loginP.randomStr = login.subarray(Iid[1] + usernameLen[1] + loginP.usernameLen + randomStrLen[1], Iid[1] + usernameLen[1] + loginP.usernameLen + randomStrLen[1] + loginP.randomStrLen).toString(); + client.username = loginP.username; + client.uuid = genUUID(client.username); + try { + validateUsername(client.username); + } + catch (err) { + disconnect(client, err.message, DisconnectReason.BAD_USERNAME); + return; + } + if (PROXY.players.has(client.username)) { + disconnect(client, `Duplicate username: ${client.username}. Please connect under a different username.`, DisconnectReason.DUPLICATE_USERNAME); + return; + } + PROXY.players.set(client.username, client); + if (true) { + const usernameLen = encodeVarInt(client.username.length), username = client.username; + const uuidLen = encodeVarInt(client.uuid.length), uuid = client.uuid; + const buff = Buffer.alloc(1 + usernameLen.length + username.length + uuidLen.length + uuid.length); + buff.set([EaglerPacketId.LOGIN_ACK, ...usernameLen, ...Buffer.from(username), ...uuidLen, ...Buffer.from(uuid)]); + client.ws.send(buff); + if (true) { + const [skin, ready] = await Promise.all([awaitPacket(client.ws, EaglerPacketId.SKIN), awaitPacket(client.ws, EaglerPacketId.C_READY)]); + if (ready[0] != 0x08) { + logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`); + disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET); + client.ws.close(); + return; + } + const buff = Buffer.alloc(1); + buff.set([EaglerPacketId.COMPLETE_HANDSHAKE]); + client.ws.send(buff); + client.state = State.POST_HANDSHAKE; + PROXY.playerStats.onlineCount += 1; + logger.info(`Client [/${client.ip}:${client.remotePort}] authenticated as player "${client.username}" and passed handshake. Connecting!`); + try { + await loginServer(config.server.host, config.server.port, client); + } + catch (err) { + logger.error(`Could not connect to remote server at [/${config.server.host}:${config.server.port}]: ${err}`); + disconnect(client, "Failed to connect to server. Please try again later.", DisconnectReason.CUSTOM); + client.state = State.DISCONNECTED; + client.ws.close(); + return; + } + } + } + } + else { + logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`); + disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET); + client.ws.close(); + } +} diff --git a/utils.ts b/utils.ts new file mode 100644 index 0000000..098653e --- /dev/null +++ b/utils.ts @@ -0,0 +1,241 @@ +import { v3 } from "uuid" +import WebSocket from "ws" +import { ProxiedPlayer } from "./classes.js" +import { + encodeULEB128 as encodeVarInt, + decodeULEB128 as decodeVarInt, + encodeSLEB128 as encodeSVarInt, + decodeSLEB128 as decodeSVarInt +} from "@thi.ng/leb128" +import { DisconnectReason, EaglerPacketId, MAGIC_ENDING_IDENTIFYS_BYTES } from "./eaglerPacketDef.js" +import { Logger } from "./logger.js" +import { State } from "./types.js" +import { toBuffer } from "uuid-buffer" +import * as mc from "minecraft-protocol" +import { config } from "./config.js" + +const MAGIC_UUID = "a7e774bc-7ea4-11ed-9a58-1f9e14304a59" +const logger = new Logger("LoginHandler") +const USERNAME_REGEX = /[^0-9^a-z^A-Z^_]/gi + +export function genUUID(user: string): string { + return v3(user, MAGIC_UUID) +} + +export function bufferizeUUID(uuid: string): Buffer { + return toBuffer(uuid) +} + +export function validateUsername(user: string): void | never { + if (user.length > 20) + throw new Error("Username is too long!") + if (!!user.match(USERNAME_REGEX)) + throw new Error("Invalid username. Username can only contain alphanumeric characters, and the underscore (_) character.") +} + +export function disconnect(player: ProxiedPlayer, message: string, code?: DisconnectReason) { + if (player.state == State.POST_HANDSHAKE) { + const messageLen = encodeVarInt(message.length) + const d = Buffer.alloc(1 + messageLen.length + message.length) + d.set([0x40, ...messageLen, ...Buffer.from(message)]) + player.ws.send(d) + player.ws.close() + } else { + const messageLen = encodeVarInt(message.length), codeEnc = encodeVarInt(code ?? DisconnectReason.CUSTOM) + const d = Buffer.alloc(1 + codeEnc.length + messageLen.length + message.length) + d.set([0xff,...codeEnc, ...messageLen, ...Buffer.from(message)]) + player.ws.send(d) + player.ws.close() + } +} + +export function awaitPacket(ws: WebSocket, id?: EaglerPacketId): Promise { + return new Promise((res, rej) => { + let resolved = false + const msgCb = (msg: any) => { + if (id != null && msg[0] == id) { + resolved = true + ws.removeEventListener('message', msgCb) + ws.removeEventListener('close', discon) + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2) + res(msg) + } else if (id == null) { + resolved = true + ws.removeEventListener('message', msgCb) + ws.removeEventListener('close', discon) + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2) + res(msg) + } + } + const discon = () => { + resolved = true + ws.removeEventListener('message', msgCb) + ws.removeEventListener('close', discon) + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2) + rej("Connection closed") + } + ws.setMaxListeners(ws.getMaxListeners() + 2) + ws.on('message', msgCb) + ws.on('close', discon) + setTimeout(() => { + ws.removeEventListener('message', msgCb) + ws.removeEventListener('close', discon) + ws.setMaxListeners(ws.getMaxListeners() - 2 < 0 ? 5 : ws.getMaxListeners() - 2) + rej("Timed out") + }, 10000) + }) +} + +export function loginServer(ip: string, port: number, client: ProxiedPlayer) { + return new Promise((res, rej) => { + let receivedCompression = false + const mcClient = mc.createClient({ + host: ip, + port: port, + auth: 'offline', + version: '1.8.8', + username: client.username + }) + mcClient.on('error', err => { + mcClient.end() + rej(err) + }) + mcClient.on('end', () => { + client.ws.close() + }) + mcClient.on('connect', () => { + client.remoteConnection = mcClient + logger.info(`Player ${client.username} has been connected to the server.`) + res() + }) + mcClient.on('raw', p => { + if (p[0] == 0x03 && !receivedCompression) { + receivedCompression = true + const compT = { + id: null, + thres: null + } + const id = decodeVarInt(p) + compT.id = Number(id[0]) + const thres = decodeSVarInt(p.subarray(id[1])) + compT.thres = thres[0] + + client.compressionThreshold = compT.thres + client.ws.send(p) + } else { + client.ws.send(p) + } + }) + }) +} + +export async function doHandshake(client: ProxiedPlayer, initialPacket: Buffer) { + client.ws.on('close', () => { + client.state = State.DISCONNECTED + if (client.remoteConnection) { + client.remoteConnection.end() + } + PROXY.players.delete(client.username) + PROXY.playerStats.onlineCount -= 1 + logger.info(`Client [/${client.ip}:${client.remotePort}]${client.username ? ` (${client.username})` : ""} disconnected from the server.`) + }) + if (PROXY.players.size + 1 > PROXY.playerStats.max) { + disconnect(client, "The proxy is full!", DisconnectReason.CUSTOM) + return + } + const identifyC = { + id: null, + brandingLen: null, + branding: null, + verLen: null, + ver: null + } + if (true) { + // save namespace by nesting func declarations in a if true statement + const Iid = decodeVarInt(initialPacket) + identifyC.id = Number(Iid[0]) + const brandingLen = decodeVarInt(initialPacket.subarray(Iid[1] + 2)) + identifyC.brandingLen = Number(brandingLen[0]) + identifyC.branding = initialPacket.subarray(brandingLen[1] + Iid[1] + 2, brandingLen[1] + Iid[1] + Number(brandingLen[0]) + 2).toString() + const verLen = decodeVarInt(initialPacket.subarray(brandingLen[1] + Iid[1] + Number(brandingLen[0]) + 2)) + identifyC.verLen = Number(verLen[0]) + identifyC.ver = initialPacket.subarray(brandingLen[1] + Number(brandingLen[0]) + Iid[1] + verLen[1] + 2).toString() + } + if (true) { + const brandingLen = encodeVarInt(PROXY.brand.length), brand = PROXY.brand + const verLen = encodeVarInt(PROXY.version.length), version = PROXY.version + const buff = Buffer.alloc(2 + MAGIC_ENDING_IDENTIFYS_BYTES.length + brandingLen.length + brand.length + verLen.length + version.length) + buff.set([EaglerPacketId.IDENTIFY_SERVER, 0x01, ...brandingLen, ...Buffer.from(brand), ...verLen, ...Buffer.from(version), ...Buffer.from(MAGIC_ENDING_IDENTIFYS_BYTES)]) + client.ws.send(buff) + } + client.clientBrand = identifyC.branding + const login = await awaitPacket(client.ws) + const loginP = { + id: null, + usernameLen: null, + username: null, + randomStrLen: null, + randomStr: null, + nullByte: null + } + if (login[0] === EaglerPacketId.LOGIN) { + const Iid = decodeVarInt(login) + loginP.id = Number(Iid[0]) + const usernameLen = decodeVarInt(login.subarray(loginP[1])) + loginP.usernameLen = Number(usernameLen[0]) + loginP.username = login.subarray(Iid[1] + usernameLen[1], Iid[1] + usernameLen[1] + loginP.usernameLen).toString() + const randomStrLen = decodeVarInt(login.subarray(Iid[1] + usernameLen[1] + loginP.usernameLen)) + loginP.randomStrLen = Number(randomStrLen[0]) + loginP.randomStr = login.subarray(Iid[1] + usernameLen[1] + loginP.usernameLen + randomStrLen[1], Iid[1] + usernameLen[1] + loginP.usernameLen + randomStrLen[1] + loginP.randomStrLen).toString() + client.username = loginP.username + client.uuid = genUUID(client.username) + try { validateUsername(client.username) } + catch (err) { + disconnect(client, err.message, DisconnectReason.BAD_USERNAME) + return + } + if (PROXY.players.has(client.username)) { + disconnect(client, `Duplicate username: ${client.username}. Please connect under a different username.`, DisconnectReason.DUPLICATE_USERNAME) + return + } + PROXY.players.set(client.username, client) + if (true) { + const usernameLen = encodeVarInt(client.username.length), username = client.username + const uuidLen = encodeVarInt(client.uuid.length), uuid = client.uuid + const buff = Buffer.alloc(1 + usernameLen.length + username.length + uuidLen.length + uuid.length) + buff.set([EaglerPacketId.LOGIN_ACK, ...usernameLen, ...Buffer.from(username), ...uuidLen, ...Buffer.from(uuid)]) + client.ws.send(buff) + + if (true) { + const [skin, ready] = await Promise.all([awaitPacket(client.ws, EaglerPacketId.SKIN), awaitPacket(client.ws, EaglerPacketId.C_READY)]) + if (ready[0] != 0x08) { + logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`) + disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET) + client.ws.close() + return + } + + const buff = Buffer.alloc(1) + buff.set([EaglerPacketId.COMPLETE_HANDSHAKE]) + client.ws.send(buff) + + client.state = State.POST_HANDSHAKE + PROXY.playerStats.onlineCount += 1 + + logger.info(`Client [/${client.ip}:${client.remotePort}] authenticated as player "${client.username}" and passed handshake. Connecting!`) + try { await loginServer(config.server.host, config.server.port, client) } + catch (err) { + logger.error(`Could not connect to remote server at [/${config.server.host}:${config.server.port}]: ${err}`) + disconnect(client, "Failed to connect to server. Please try again later.", DisconnectReason.CUSTOM) + client.state = State.DISCONNECTED + client.ws.close() + return + } + } + } + } else { + logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`) + disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET) + client.ws.close() + } +} \ No newline at end of file