openbrawl-client/Client/BrawlClient.js
2025-06-21 00:41:45 +03:00

218 lines
5.8 KiB
JavaScript

const net = require("net")
const MessageFactory = require("../Protocol/MessageFactory")
const { Crypto } = require("../Utils/Crypto")
class BrawlClient {
constructor(config, logger) {
this.config = config
this.logger = logger
this.socket = null
this.connected = false
this.crypto = new Crypto(config.crypto)
this.messageFactory = new MessageFactory(config, this.crypto, logger)
this.packetTypes = [10100, 10101, 20108, 0]
this.currentPacketIndex = 0
this.sequenceNumber = 0
this.keepAliveCounter = 0
this.reconnectAttempts = 0
this.keepAliveInterval = null
}
connect() {
this.logger.info(`🌐 Connecting to ${this.config.server.host}:${this.config.server.port}...`)
this.socket = new net.Socket()
this.socket.setTimeout(this.config.network.timeout)
this.socket.connect(this.config.server.port, this.config.server.host, () => {
this.logger.connection("connected", {
host: this.config.server.host,
port: this.config.server.port,
})
this.connected = true
this.reconnectAttempts = 0
this.startProtocolSequence()
})
this.socket.on("data", (data) => {
this.handleServerData(data)
})
this.socket.on("close", () => {
this.logger.connection("disconnected")
this.connected = false
this.stopKeepAlive()
this.attemptReconnect()
})
this.socket.on("error", (err) => {
this.logger.error("Connection error:", err.message)
this.connected = false
this.stopKeepAlive()
})
this.socket.on("timeout", () => {
this.logger.warn("Connection timeout")
this.socket.destroy()
})
}
handleServerData(data) {
try {
let decryptedData = data
if (this.config.crypto.enabled) {
decryptedData = this.crypto.decrypt(data)
this.logger.debug("🔓 Packet decrypted")
}
const message = this.messageFactory.parseMessage(decryptedData)
switch (message.type) {
case 20100:
this.logger.success("🔐 Server Hello received")
break
case 20103:
this.logger.success("✅ Login OK received")
this.startKeepAlive()
break
case 20104:
this.logger.error("❌ Login Failed received")
break
case 20108:
this.logger.debug("💓 Keep Alive response received")
break
default:
this.logger.info(`📦 Unknown packet type: ${message.type}`)
}
} catch (error) {
this.logger.error("Error parsing server data:", error.message)
}
}
sendMessage(type, data = null) {
if (!this.connected) {
this.logger.warn("❌ Not connected to server")
return false
}
try {
const message = this.messageFactory.createMessage(type, data)
const payload = message.encode()
const packet = this.messageFactory.createPacketHeader(type, payload)
let finalPacket = packet
if (this.config.crypto.enabled) {
finalPacket = this.crypto.encrypt(packet)
this.logger.debug("🔐 Packet encrypted")
} else {
this.logger.debug("📤 Packet sent without encryption")
}
this.socket.write(finalPacket)
const typeName = this.getMessageTypeName(type)
this.logger.success(`${typeName} sent successfully`)
return true
} catch (error) {
this.logger.error(`💥 Error sending message ${type}:`, error.message)
return false
}
}
getMessageTypeName(type) {
const names = {
10100: "ClientHelloMessage",
10101: "LoginMessage",
20108: "KeepAliveMessage",
0: "HeartbeatMessage",
}
return names[type] || `Unknown(${type})`
}
startProtocolSequence() {
this.logger.info("🎯 Starting protocol sequence...")
setTimeout(() => this.sendMessage(10100), 100)
setTimeout(() => this.sendMessage(10101), 1000)
setTimeout(() => this.sendMessage(0, { sequence: this.sequenceNumber++ }), 2000)
setTimeout(() => {
this.startPeriodicSending()
}, 5000)
}
startPeriodicSending() {
setInterval(() => {
if (this.connected) {
const packetType = this.packetTypes[this.currentPacketIndex]
let data = null
if (packetType === 0) {
data = { sequence: this.sequenceNumber++ }
} else if (packetType === 20108) {
data = { counter: this.keepAliveCounter++ }
}
this.sendMessage(packetType, data)
this.currentPacketIndex = (this.currentPacketIndex + 1) % this.packetTypes.length
}
}, this.config.network.keepAliveInterval)
}
startKeepAlive() {
this.keepAliveInterval = setInterval(() => {
if (this.connected) {
this.sendMessage(20108, { counter: this.keepAliveCounter++ })
}
}, this.config.network.keepAliveInterval)
}
stopKeepAlive() {
if (this.keepAliveInterval) {
clearInterval(this.keepAliveInterval)
this.keepAliveInterval = null
}
}
attemptReconnect() {
if (this.reconnectAttempts < this.config.network.reconnectAttempts) {
this.reconnectAttempts++
this.logger.info(
`🔄 Attempting reconnection ${this.reconnectAttempts}/${this.config.network.reconnectAttempts}...`,
)
setTimeout(() => {
this.connect()
}, this.config.network.reconnectDelay)
} else {
this.logger.error("💀 Max reconnection attempts reached")
}
}
disconnect() {
this.logger.info("🔌 Disconnecting from server...")
this.connected = false
this.stopKeepAlive()
if (this.socket) {
this.socket.destroy()
this.socket = null
}
}
getStatus() {
return {
connected: this.connected,
sequenceNumber: this.sequenceNumber,
keepAliveCounter: this.keepAliveCounter,
reconnectAttempts: this.reconnectAttempts,
}
}
}
module.exports = BrawlClient