mirror of
https://github.com/WorldEditAxe/eaglerproxy.git
synced 2024-11-21 13:06:05 -08:00
Mostly working handshake handler & MOTD handler
This commit is contained in:
parent
958a060115
commit
083762fce1
232
utils.ts
232
utils.ts
|
@ -1,25 +1,31 @@
|
||||||
import { v3 } from "uuid"
|
|
||||||
import WebSocket from "ws"
|
import WebSocket from "ws"
|
||||||
import { ProxiedPlayer } from "./classes.js"
|
|
||||||
import {
|
import {
|
||||||
encodeULEB128 as encodeVarInt,
|
encodeULEB128 as encodeVarInt,
|
||||||
decodeULEB128 as decodeVarInt,
|
decodeULEB128 as decodeVarInt,
|
||||||
encodeSLEB128 as encodeSVarInt,
|
encodeSLEB128 as encodeSVarInt,
|
||||||
decodeSLEB128 as decodeSVarInt
|
decodeSLEB128 as decodeSVarInt
|
||||||
} from "@thi.ng/leb128"
|
} from "@thi.ng/leb128"
|
||||||
import { DisconnectReason, EaglerPacketId, MAGIC_ENDING_IDENTIFYS_BYTES } from "./eaglerPacketDef.js"
|
import { DisconnectReason, EAGLERCRAFT_SKIN_CHANNEL_NAME, EaglerPacketId, MAGIC_BUILTIN_SKIN_BYTES, MAGIC_ENDING_IDENTIFY_S_BYTES } from "./eaglerPacketDef.js"
|
||||||
import { Logger } from "./logger.js"
|
import { Logger } from "./logger.js"
|
||||||
import { State } from "./types.js"
|
import { ChannelMessageType, Chat, ChatColor, ProxiedPlayer, State, UUID } from "./types.js"
|
||||||
import { toBuffer } from "uuid-buffer"
|
import { toBuffer, toString as uuidToString } from "uuid-buffer"
|
||||||
import * as mc from "minecraft-protocol"
|
import * as mc from "minecraft-protocol"
|
||||||
import { config } from "./config.js"
|
import { config } from "./config.js"
|
||||||
|
import sharp from "sharp"
|
||||||
|
import { createHash, randomUUID } from "crypto"
|
||||||
|
import { encodeSSkinDl, encodeSSkinDlBuiltin, packChannelMessage, processClientReqPacket } from "./eaglerSkin.js"
|
||||||
|
|
||||||
const MAGIC_UUID = "a7e774bc-7ea4-11ed-9a58-1f9e14304a59"
|
|
||||||
const logger = new Logger("LoginHandler")
|
const logger = new Logger("LoginHandler")
|
||||||
const USERNAME_REGEX = /[^0-9^a-z^A-Z^_]/gi
|
const USERNAME_REGEX = /[^0-9^a-z^A-Z^_]/gi
|
||||||
|
|
||||||
export function genUUID(user: string): string {
|
export function genUUID(user: string): string {
|
||||||
return v3(user, MAGIC_UUID)
|
const str = `OfflinePlayer:${user}`
|
||||||
|
let md5Bytes = createHash('md5').update(str).digest()
|
||||||
|
md5Bytes[6] &= 0x0f; /* clear version */
|
||||||
|
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||||
|
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||||
|
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||||
|
return uuidToString(md5Bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bufferizeUUID(uuid: string): Buffer {
|
export function bufferizeUUID(uuid: string): Buffer {
|
||||||
|
@ -33,17 +39,30 @@ export function validateUsername(user: string): void | never {
|
||||||
throw new Error("Invalid username. Username can only contain alphanumeric characters, and the underscore (_) character.")
|
throw new Error("Invalid username. Username can only contain alphanumeric characters, and the underscore (_) character.")
|
||||||
}
|
}
|
||||||
|
|
||||||
export function disconnect(player: ProxiedPlayer, message: string, code?: DisconnectReason) {
|
export function chatToPlainString(chat: Chat): string {
|
||||||
|
let ret = ''
|
||||||
|
if (chat.text != null) ret += chat.text
|
||||||
|
if (chat.extra != null) {
|
||||||
|
chat.extra.forEach(extra => {
|
||||||
|
ret += extra.text
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
export function disconnect(player: ProxiedPlayer, message: Chat | string, code?: DisconnectReason) {
|
||||||
if (player.state == State.POST_HANDSHAKE) {
|
if (player.state == State.POST_HANDSHAKE) {
|
||||||
const messageLen = encodeVarInt(message.length)
|
const message_m = (typeof message == 'string' ? JSON.stringify({ text: message }) : JSON.stringify(message)) + 0x0
|
||||||
const d = Buffer.alloc(1 + messageLen.length + message.length)
|
const messageLen = encodeVarInt(message_m.length)
|
||||||
d.set([0x40, ...messageLen, ...Buffer.from(message)])
|
const d = Buffer.alloc([0x40, ...messageLen, ...Buffer.from(message_m)].length)
|
||||||
|
d.set([0x40, ...messageLen, ...Buffer.from(message_m)])
|
||||||
player.ws.send(d)
|
player.ws.send(d)
|
||||||
player.ws.close()
|
player.ws.close()
|
||||||
} else {
|
} else {
|
||||||
const messageLen = encodeVarInt(message.length), codeEnc = encodeVarInt(code ?? DisconnectReason.CUSTOM)
|
const message_m = (typeof message == 'string' ? message : chatToPlainString(message))
|
||||||
const d = Buffer.alloc(1 + codeEnc.length + messageLen.length + message.length)
|
const messageLen = encodeVarInt(message_m.length), codeEnc = encodeVarInt(code ?? DisconnectReason.CUSTOM)
|
||||||
d.set([0xff,...codeEnc, ...messageLen, ...Buffer.from(message)])
|
const d = Buffer.alloc([0xff,...codeEnc, ...messageLen, ...Buffer.from(message_m)].length)
|
||||||
|
d.set([0xff,...codeEnc, ...messageLen, ...Buffer.from(message_m)])
|
||||||
player.ws.send(d)
|
player.ws.send(d)
|
||||||
player.ws.close()
|
player.ws.close()
|
||||||
}
|
}
|
||||||
|
@ -88,7 +107,7 @@ export function awaitPacket(ws: WebSocket, id?: EaglerPacketId): Promise<Buffer>
|
||||||
|
|
||||||
export function loginServer(ip: string, port: number, client: ProxiedPlayer) {
|
export function loginServer(ip: string, port: number, client: ProxiedPlayer) {
|
||||||
return new Promise<void>((res, rej) => {
|
return new Promise<void>((res, rej) => {
|
||||||
let receivedCompression = false
|
let blockedSuccessLogin = false
|
||||||
const mcClient = mc.createClient({
|
const mcClient = mc.createClient({
|
||||||
host: ip,
|
host: ip,
|
||||||
port: port,
|
port: port,
|
||||||
|
@ -100,28 +119,20 @@ export function loginServer(ip: string, port: number, client: ProxiedPlayer) {
|
||||||
mcClient.end()
|
mcClient.end()
|
||||||
rej(err)
|
rej(err)
|
||||||
})
|
})
|
||||||
mcClient.on('end', () => {
|
|
||||||
client.ws.close()
|
|
||||||
})
|
|
||||||
mcClient.on('connect', () => {
|
mcClient.on('connect', () => {
|
||||||
client.remoteConnection = mcClient
|
client.remoteConnection = mcClient
|
||||||
|
mcClient.on('end', () => {
|
||||||
|
client.ws.close()
|
||||||
|
})
|
||||||
logger.info(`Player ${client.username} has been connected to the server.`)
|
logger.info(`Player ${client.username} has been connected to the server.`)
|
||||||
res()
|
res()
|
||||||
})
|
})
|
||||||
mcClient.on('raw', p => {
|
mcClient.on('raw', p => {
|
||||||
if (p[0] == 0x03 && !receivedCompression) {
|
// block the login success packet to fix the bug that prints the UUID in chat on join
|
||||||
receivedCompression = true
|
if (p[0] == 0x02 && blockedSuccessLogin) {
|
||||||
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)
|
client.ws.send(p)
|
||||||
|
} else if (p[0] == 0x02) {
|
||||||
|
blockedSuccessLogin = !blockedSuccessLogin
|
||||||
} else {
|
} else {
|
||||||
client.ws.send(p)
|
client.ws.send(p)
|
||||||
}
|
}
|
||||||
|
@ -136,11 +147,10 @@ export async function doHandshake(client: ProxiedPlayer, initialPacket: Buffer)
|
||||||
client.remoteConnection.end()
|
client.remoteConnection.end()
|
||||||
}
|
}
|
||||||
PROXY.players.delete(client.username)
|
PROXY.players.delete(client.username)
|
||||||
PROXY.playerStats.onlineCount -= 1
|
|
||||||
logger.info(`Client [/${client.ip}:${client.remotePort}]${client.username ? ` (${client.username})` : ""} disconnected from the server.`)
|
logger.info(`Client [/${client.ip}:${client.remotePort}]${client.username ? ` (${client.username})` : ""} disconnected from the server.`)
|
||||||
})
|
})
|
||||||
if (PROXY.players.size + 1 > PROXY.playerStats.max) {
|
if (PROXY.players.size + 1 > PROXY.config.maxPlayers) {
|
||||||
disconnect(client, "The proxy is full!", DisconnectReason.CUSTOM)
|
disconnect(client, ChatColor.YELLOW + "The proxy is full!", DisconnectReason.CUSTOM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const identifyC = {
|
const identifyC = {
|
||||||
|
@ -164,8 +174,8 @@ export async function doHandshake(client: ProxiedPlayer, initialPacket: Buffer)
|
||||||
if (true) {
|
if (true) {
|
||||||
const brandingLen = encodeVarInt(PROXY.brand.length), brand = PROXY.brand
|
const brandingLen = encodeVarInt(PROXY.brand.length), brand = PROXY.brand
|
||||||
const verLen = encodeVarInt(PROXY.version.length), version = PROXY.version
|
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)
|
const buff = Buffer.alloc(2 + MAGIC_ENDING_IDENTIFY_S_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)])
|
buff.set([EaglerPacketId.IDENTIFY_SERVER, 0x01, ...brandingLen, ...Buffer.from(brand), ...verLen, ...Buffer.from(version), ...Buffer.from(MAGIC_ENDING_IDENTIFY_S_BYTES)])
|
||||||
client.ws.send(buff)
|
client.ws.send(buff)
|
||||||
}
|
}
|
||||||
client.clientBrand = identifyC.branding
|
client.clientBrand = identifyC.branding
|
||||||
|
@ -181,61 +191,189 @@ export async function doHandshake(client: ProxiedPlayer, initialPacket: Buffer)
|
||||||
if (login[0] === EaglerPacketId.LOGIN) {
|
if (login[0] === EaglerPacketId.LOGIN) {
|
||||||
const Iid = decodeVarInt(login)
|
const Iid = decodeVarInt(login)
|
||||||
loginP.id = Number(Iid[0])
|
loginP.id = Number(Iid[0])
|
||||||
const usernameLen = decodeVarInt(login.subarray(loginP[1]))
|
const usernameLen = decodeVarInt(login.subarray(Iid[1]))
|
||||||
loginP.usernameLen = Number(usernameLen[0])
|
loginP.usernameLen = Number(usernameLen[0])
|
||||||
loginP.username = login.subarray(Iid[1] + usernameLen[1], Iid[1] + usernameLen[1] + loginP.usernameLen).toString()
|
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))
|
const randomStrLen = decodeVarInt(login.subarray(Iid[1] + usernameLen[1] + loginP.usernameLen))
|
||||||
loginP.randomStrLen = Number(randomStrLen[0])
|
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()
|
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.username = loginP.username
|
||||||
client.uuid = genUUID(client.username)
|
client.uuid = genUUID(client.username)
|
||||||
try { validateUsername(client.username) }
|
try { validateUsername(client.username) }
|
||||||
catch (err) {
|
catch (err) {
|
||||||
disconnect(client, err.message, DisconnectReason.BAD_USERNAME)
|
disconnect(client, ChatColor.RED + err.message, DisconnectReason.CUSTOM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (PROXY.players.has(client.username)) {
|
if (PROXY.players.has(client.username)) {
|
||||||
disconnect(client, `Duplicate username: ${client.username}. Please connect under a different username.`, DisconnectReason.DUPLICATE_USERNAME)
|
disconnect(client, `Duplicate username: ${client.username}. Please connect under a different username.`, DisconnectReason.CUSTOM)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
PROXY.players.set(client.username, client)
|
PROXY.players.set(client.username, client)
|
||||||
if (true) {
|
if (true) {
|
||||||
const usernameLen = encodeVarInt(client.username.length), username = client.username
|
const usernameLen = encodeVarInt(client.username.length), username = client.username
|
||||||
const uuidLen = encodeVarInt(client.uuid.length), uuid = client.uuid
|
const uuid = bufferizeUUID(client.uuid)
|
||||||
const buff = Buffer.alloc(1 + usernameLen.length + username.length + uuidLen.length + uuid.length)
|
const buff = Buffer.alloc(1 + usernameLen.length + username.length + uuid.length)
|
||||||
buff.set([EaglerPacketId.LOGIN_ACK, ...usernameLen, ...Buffer.from(username), ...uuidLen, ...Buffer.from(uuid)])
|
buff.set([EaglerPacketId.LOGIN_ACK, ...usernameLen, ...Buffer.from(username), ...Buffer.from(uuid)])
|
||||||
client.ws.send(buff)
|
client.ws.send(buff)
|
||||||
|
|
||||||
if (true) {
|
if (true) {
|
||||||
const [skin, ready] = await Promise.all([awaitPacket(client.ws, EaglerPacketId.SKIN), awaitPacket(client.ws, EaglerPacketId.C_READY)])
|
const [skin, ready] = await Promise.all([awaitPacket(client.ws, EaglerPacketId.SKIN), awaitPacket(client.ws, EaglerPacketId.C_READY)])
|
||||||
if (ready[0] != 0x08) {
|
if (ready[0] != 0x08) {
|
||||||
logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`)
|
logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`)
|
||||||
disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET)
|
disconnect(client, ChatColor.RED + "Received bad packet.", DisconnectReason.CUSTOM)
|
||||||
client.ws.close()
|
client.ws.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (true) {
|
||||||
|
const skinP = {
|
||||||
|
id: null,
|
||||||
|
skinVerLen: null,
|
||||||
|
skinVer: null, // skin_v1
|
||||||
|
type: null, // CUSTOM or BUILTIN
|
||||||
|
skinId: null,
|
||||||
|
skinDimens: null,
|
||||||
|
skin: null
|
||||||
|
}
|
||||||
|
const Iid = decodeVarInt(skin)
|
||||||
|
skinP.id = Number(Iid[0])
|
||||||
|
const skinVerLen = decodeVarInt(skin.subarray(Iid[1]))
|
||||||
|
skinP.skinVerLen = Number(skinVerLen[0])
|
||||||
|
skinP.skinVer = skin.subarray(Iid[1] + skinVerLen[1], Iid[1] + skinVerLen[1] + skinP.skinVerLen).toString()
|
||||||
|
const typebuff = skin.subarray(Iid[1] + skinVerLen[1] + skinP.skinVerLen, Iid[1] + skinVerLen[1] + skinP.skinVerLen + MAGIC_BUILTIN_SKIN_BYTES.length)
|
||||||
|
if (typebuff.compare(Buffer.from(MAGIC_BUILTIN_SKIN_BYTES)) == 0) {
|
||||||
|
skinP.type = "BUILTIN"
|
||||||
|
skinP.skinId = Number(decodeVarInt(skin.subarray(Iid[1] + skinVerLen[1] + skinP.skinVerLen + MAGIC_BUILTIN_SKIN_BYTES.length))[0])
|
||||||
|
} else {
|
||||||
|
skinP.type = "CUSTOM"
|
||||||
|
const skinSqrt = decodeVarInt(skin.subarray(Iid[1] + skinVerLen[1] + skinP.skinVerLen)), dimensions = Number(skinSqrt[0]) * Number(skinSqrt[0]) * 3
|
||||||
|
skinP.skinDimens = dimensions
|
||||||
|
skinP.skin = skin.subarray(Iid[1] + skinVerLen[1] + skinP.skinVerLen + skinSqrt[1] + 16)
|
||||||
|
console.log(skinP.skin.length)
|
||||||
|
if (skinP.skin.length > 16385) {
|
||||||
|
disconnect(client, ChatColor.RED + "Invalid skin received!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(skinP.skin[skinP.skin.length - 1])
|
||||||
|
}
|
||||||
|
client.skin = {
|
||||||
|
type: skinP.type,
|
||||||
|
skinId: skinP.skinId,
|
||||||
|
customSkin: skinP.skin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const buff = Buffer.alloc(1)
|
const buff = Buffer.alloc(1)
|
||||||
buff.set([EaglerPacketId.COMPLETE_HANDSHAKE])
|
buff.set([EaglerPacketId.COMPLETE_HANDSHAKE])
|
||||||
client.ws.send(buff)
|
client.ws.send(buff)
|
||||||
|
|
||||||
client.state = State.POST_HANDSHAKE
|
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!`)
|
logger.info(`Client [/${client.ip}:${client.remotePort}] authenticated as player ${client.username} (${client.uuid}) and passed handshake. Connecting!`)
|
||||||
try { await loginServer(config.server.host, config.server.port, client) }
|
try { await loginServer(config.server.host, config.server.port, client) }
|
||||||
catch (err) {
|
catch (err) {
|
||||||
logger.error(`Could not connect to remote server at [/${config.server.host}:${config.server.port}]: ${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)
|
disconnect(client, ChatColor.RED + "Failed to connect to server. Please try again later.", DisconnectReason.CUSTOM)
|
||||||
client.state = State.DISCONNECTED
|
client.state = State.DISCONNECTED
|
||||||
client.ws.close()
|
client.ws.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client.queuedEaglerSkinPackets.length > 0) {
|
||||||
|
for (const packet of client.queuedEaglerSkinPackets) {
|
||||||
|
processClientReqPacket(packet, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`)
|
logger.error(`Client [/${client.ip}:${client.remotePort}] sent an unexpected packet! Disconnecting.`)
|
||||||
disconnect(client, "Received bad packet", DisconnectReason.UNEXPECTED_PACKET)
|
disconnect(client, ChatColor.RED + "Received bad packet", DisconnectReason.CUSTOM)
|
||||||
client.ws.close()
|
client.ws.close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<typeof Date.now>,
|
||||||
|
type: "motd",
|
||||||
|
uuid: ReturnType<typeof randomUUID>,
|
||||||
|
vers: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// a 16384 byte array
|
||||||
|
export type MotdServerLogo = Buffer
|
||||||
|
|
||||||
|
const ICON_SQRT = 64
|
||||||
|
|
||||||
|
export function generateMOTDImage(file: Buffer): Promise<MotdServerLogo> {
|
||||||
|
return new Promise<MotdServerLogo>((res, rej) => {
|
||||||
|
sharp(file)
|
||||||
|
.resize(ICON_SQRT, ICON_SQRT, {
|
||||||
|
kernel: 'nearest'
|
||||||
|
})
|
||||||
|
.raw({
|
||||||
|
depth: 'uchar'
|
||||||
|
})
|
||||||
|
.toBuffer()
|
||||||
|
.then(buff => {
|
||||||
|
for (const pixel of buff) {
|
||||||
|
if ((pixel & 0xFFFFFF) == 0) {
|
||||||
|
buff[buff.indexOf(pixel)] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res(buff)
|
||||||
|
})
|
||||||
|
.catch(rej)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleMotd(player: Partial<ProxiedPlayer>) {
|
||||||
|
const names = []
|
||||||
|
for (const [username, player] of PROXY.players) {
|
||||||
|
if (names.length > 0) {
|
||||||
|
names.push(`${ChatColor.GRAY}${ChatColor.ITALIC}(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.config.maxPlayers,
|
||||||
|
motd: PROXY.MOTD.motd,
|
||||||
|
online: PROXY.players.size,
|
||||||
|
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()
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user