This commit is contained in:
пошел нахуй 2025-06-21 00:41:45 +03:00
parent 16b6f2f2d6
commit 8b31dfa980
14 changed files with 1193 additions and 0 deletions

174
ByteStream/ByteStream.js Normal file
View File

@ -0,0 +1,174 @@
class ByteStream {
constructor(buffer = null) {
this.buffer = buffer || Buffer.alloc(0)
this.offset = 0
}
readUInt8() {
if (this.offset >= this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.readUInt8(this.offset)
this.offset += 1
return value
}
readUInt16() {
if (this.offset + 2 > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.readUInt16BE(this.offset)
this.offset += 2
return value
}
readUInt24() {
if (this.offset + 3 > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value =
(this.buffer.readUInt8(this.offset) << 16) |
(this.buffer.readUInt8(this.offset + 1) << 8) |
this.buffer.readUInt8(this.offset + 2)
this.offset += 3
return value
}
readUInt32() {
if (this.offset + 4 > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.readUInt32BE(this.offset)
this.offset += 4
return value
}
readInt32() {
if (this.offset + 4 > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.readInt32BE(this.offset)
this.offset += 4
return value
}
readString() {
const length = this.readUInt32()
if (length === 0) return ""
if (this.offset + length > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.toString("utf8", this.offset, this.offset + length)
this.offset += length
return value
}
readBytes(length) {
if (this.offset + length > this.buffer.length) {
throw new Error("ByteStream: Cannot read beyond buffer length")
}
const value = this.buffer.slice(this.offset, this.offset + length)
this.offset += length
return value
}
writeUInt8(value) {
const buf = Buffer.alloc(1)
buf.writeUInt8(value, 0)
this.buffer = Buffer.concat([this.buffer, buf])
return this
}
writeUInt16(value) {
const buf = Buffer.alloc(2)
buf.writeUInt16BE(value, 0)
this.buffer = Buffer.concat([this.buffer, buf])
return this
}
writeUInt24(value) {
const buf = Buffer.alloc(3)
buf.writeUInt8((value >> 16) & 0xff, 0)
buf.writeUInt8((value >> 8) & 0xff, 1)
buf.writeUInt8(value & 0xff, 2)
this.buffer = Buffer.concat([this.buffer, buf])
return this
}
writeUInt32(value) {
const buf = Buffer.alloc(4)
buf.writeUInt32BE(value, 0)
this.buffer = Buffer.concat([this.buffer, buf])
return this
}
writeInt32(value) {
const buf = Buffer.alloc(4)
buf.writeInt32BE(value, 0)
this.buffer = Buffer.concat([this.buffer, buf])
return this
}
writeString(value) {
this.writeUInt32(value.length)
if (value.length > 0) {
const buf = Buffer.from(value, "utf8")
this.buffer = Buffer.concat([this.buffer, buf])
}
return this
}
writeBytes(bytes) {
this.buffer = Buffer.concat([this.buffer, bytes])
return this
}
// Utility methods
getBuffer() {
return this.buffer
}
getLength() {
return this.buffer.length
}
getRemainingLength() {
return this.buffer.length - this.offset
}
getOffset() {
return this.offset
}
setOffset(offset) {
if (offset < 0 || offset > this.buffer.length) {
throw new Error("ByteStream: Invalid offset")
}
this.offset = offset
return this
}
reset() {
this.buffer = Buffer.alloc(0)
this.offset = 0
return this
}
clone() {
const cloned = new ByteStream(Buffer.from(this.buffer))
cloned.offset = this.offset
return cloned
}
toHex() {
return this.buffer.toString("hex")
}
static fromHex(hexString) {
return new ByteStream(Buffer.from(hexString, "hex"))
}
}
module.exports = ByteStream

217
Client/BrawlClient.js Normal file
View File

@ -0,0 +1,217 @@
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

40
Config/config.js Normal file
View File

@ -0,0 +1,40 @@
module.exports = {
server: {
host: "127.0.0.1",
port: 9339,
},
auth: {
token: "",
},
crypto: {
rc4Key: "opahelpme",
nonce: "DSFDSDECFJDJDKSK",
enabled: false,
},
client: {
version: {
major: 52,
minor: 0,
build: 258,
},
device: "XIAOMI",
language: "RU",
},
network: {
timeout: 30000,
keepAliveInterval: 5000,
reconnectAttempts: 3,
reconnectDelay: 2000,
},
logging: {
level: "debug",
saveToFile: true,
maxFileSize: "10MB",
maxFiles: 5,
},
}

98
Protocol.js Normal file
View File

@ -0,0 +1,98 @@
const ByteStream = require("./ByteStream")
class Protocol {
constructor() {
this.version = "1.0.0"
this.clientVersion = 52
}
createPacketHeader(messageType, payload) {
const stream = new ByteStream()
stream.writeUInt16(messageType)
stream.writeUInt24(payload.length)
return Buffer.concat([stream.getBuffer(), payload])
}
createClientHello() {
const stream = new ByteStream()
// Protocol version
stream.writeUInt32(1)
// Key version
stream.writeUInt32(this.clientVersion)
// Major version
stream.writeUInt32(52)
// Minor version
stream.writeUInt32(0)
// Build version
stream.writeUInt32(258)
// Hash
stream.writeString("")
return this.createPacketHeader(10100, stream.getBuffer())
}
createLoginMessage() {
const stream = new ByteStream()
// High ID
stream.writeUInt32(0)
// Low ID
stream.writeUInt32(Math.floor(Math.random() * 1000000))
// Token
stream.writeString("")
// Major version
stream.writeUInt32(52)
// Minor version
stream.writeUInt32(0)
// Build version
stream.writeUInt32(258)
// Hash
stream.writeString("")
// Device
stream.writeString("OpenBrawlProject")
// Language
stream.writeString("EN")
return this.createPacketHeader(10101, stream.getBuffer())
}
createKeepAlive() {
const stream = new ByteStream()
// Keep alive doesn't need payload
return this.createPacketHeader(20108, stream.getBuffer())
}
createHeartbeat() {
const stream = new ByteStream()
// Heartbeat with timestamp
stream.writeUInt32(Date.now())
return this.createPacketHeader(0, stream.getBuffer())
}
createCustomPacket() {
const stream = new ByteStream()
// Custom data
stream.writeString("OpenBrawlProject")
stream.writeUInt32(Date.now())
stream.writeUInt8(1)
return this.createPacketHeader(6076, stream.getBuffer())
}
}
module.exports = Protocol

View File

@ -0,0 +1,69 @@
const ClientHelloMessage = require("./Messages/ClientHelloMessage")
const LoginMessage = require("./Messages/LoginMessage")
const KeepAliveMessage = require("./Messages/KeepAliveMessage")
const HeartbeatMessage = require("./Messages/HeartbeatMessage")
class MessageFactory {
constructor(config, crypto, logger) {
this.config = config
this.crypto = crypto
this.logger = logger
this.messageTypes = {
10100: ClientHelloMessage,
10101: LoginMessage,
20108: KeepAliveMessage,
0: HeartbeatMessage,
// 6076: CustomMessage,
}
}
createMessage(type, data = null) {
const MessageClass = this.messageTypes[type]
if (!MessageClass) {
throw new Error(`Unknown message type: ${type}`)
}
return new MessageClass(this.config, this.crypto, this.logger, data)
}
parseMessage(buffer) {
try {
const ByteStream = require("../ByteStream/ByteStream")
const stream = new ByteStream(buffer)
const messageType = stream.readUInt16()
const length = stream.readUInt24()
const payload = stream.readBytes(length)
this.logger.packet("in", messageType, length, { payload: payload.toString("hex") })
return {
type: messageType,
length: length,
payload: payload,
}
} catch (error) {
this.logger.error("Failed to parse message:", error.message)
throw error
}
}
createPacketHeader(messageType, payload) {
const ByteStream = require("../ByteStream/ByteStream")
const stream = new ByteStream()
stream.writeUInt16(messageType)
stream.writeUInt24(payload.length)
const header = stream.getBuffer()
const packet = Buffer.concat([header, payload])
this.logger.packet("out", messageType, packet.length, { payload: payload.toString("hex") })
return packet
}
}
module.exports = MessageFactory

View File

@ -0,0 +1,61 @@
const ByteStream = require("../../ByteStream/ByteStream")
class ClientHelloMessage {
constructor(config, crypto, logger, data = null) {
this.config = config
this.crypto = crypto
this.logger = logger
this.type = 10100
this.data = data
}
encode() {
const stream = new ByteStream()
// Protocol version
stream.writeUInt32(1)
stream.writeUInt32(this.config.client.version.major)
stream.writeUInt32(this.config.client.version.major)
stream.writeUInt32(this.config.client.version.minor)
stream.writeUInt32(this.config.client.version.build)
stream.writeString("")
const nonce = this.crypto.generateNonce()
stream.writeBytes(nonce)
this.logger.debug(`Encoding ClientHelloMessage`, {
version: this.config.client.version,
nonceLength: nonce.length,
})
return stream.getBuffer()
}
decode(buffer) {
const stream = new ByteStream(buffer)
const data = {
protocolVersion: stream.readUInt32(),
keyVersion: stream.readUInt32(),
majorVersion: stream.readUInt32(),
minorVersion: stream.readUInt32(),
buildVersion: stream.readUInt32(),
hash: stream.readString(),
}
this.logger.debug(`Decoded ClientHelloMessage`, data)
return data
}
getType() {
return this.type
}
}
module.exports = ClientHelloMessage

View File

@ -0,0 +1,49 @@
const ByteStream = require("../../ByteStream/ByteStream")
class HeartbeatMessage {
constructor(config, crypto, logger, data = null) {
this.config = config
this.crypto = crypto
this.logger = logger
this.type = 0
this.data = data
}
encode() {
const stream = new ByteStream()
const timestamp = Math.floor(Date.now() / 1000)
stream.writeUInt32(timestamp)
// Client status
stream.writeUInt8(1) // 1 = alive
stream.writeUInt32(this.data?.sequence || 0)
this.logger.debug(`Encoding HeartbeatMessage`, {
timestamp: timestamp,
sequence: this.data?.sequence || 0,
})
return stream.getBuffer()
}
decode(buffer) {
const stream = new ByteStream(buffer)
const data = {
timestamp: stream.readUInt32(),
status: stream.readUInt8(),
sequence: stream.readUInt32(),
}
this.logger.debug(`Decoded HeartbeatMessage`, data)
return data
}
getType() {
return this.type
}
}
module.exports = HeartbeatMessage

View File

@ -0,0 +1,44 @@
const ByteStream = require("../../ByteStream/ByteStream")
class KeepAliveMessage {
constructor(config, crypto, logger, data = null) {
this.config = config
this.crypto = crypto
this.logger = logger
this.type = 20108
this.data = data
}
encode() {
const stream = new ByteStream()
stream.writeUInt32(Math.floor(Date.now() / 1000))
stream.writeUInt32(this.data?.counter || 0)
this.logger.debug(`Encoding KeepAliveMessage`, {
timestamp: Math.floor(Date.now() / 1000),
counter: this.data?.counter || 0,
})
return stream.getBuffer()
}
decode(buffer) {
const stream = new ByteStream(buffer)
const data = {
timestamp: stream.readUInt32(),
counter: stream.readUInt32(),
}
this.logger.debug(`Decoded KeepAliveMessage`, data)
return data
}
getType() {
return this.type
}
}
module.exports = KeepAliveMessage

View File

@ -0,0 +1,71 @@
const ByteStream = require("../../ByteStream/ByteStream")
class LoginMessage {
constructor(config, crypto, logger, data = null) {
this.config = config
this.crypto = crypto
this.logger = logger
this.type = 10101
this.data = data
}
encode() {
const stream = new ByteStream()
stream.writeUInt32(0)
const playerId = Math.floor(Math.random() * 1000000)
stream.writeUInt32(playerId)
const token = this.config.auth.token || ""
stream.writeString(token)
stream.writeUInt32(this.config.client.version.major)
stream.writeUInt32(this.config.client.version.minor)
stream.writeUInt32(this.config.client.version.build)
stream.writeString("")
stream.writeString(this.config.client.device)
stream.writeString(this.config.client.language)
this.logger.debug(`Encoding LoginMessage`, {
playerId: playerId,
device: this.config.client.device,
language: this.config.client.language,
hasToken: token.length > 0,
cryptoEnabled: this.config.crypto.enabled,
})
return stream.getBuffer()
}
decode(buffer) {
const stream = new ByteStream(buffer)
const data = {
highId: stream.readUInt32(),
lowId: stream.readUInt32(),
token: stream.readString(),
majorVersion: stream.readUInt32(),
minorVersion: stream.readUInt32(),
buildVersion: stream.readUInt32(),
hash: stream.readString(),
device: stream.readString(),
language: stream.readString(),
}
this.logger.debug(`Decoded LoginMessage`, data)
return data
}
getType() {
return this.type
}
}
module.exports = LoginMessage

101
Utils/Crypto.js Normal file
View File

@ -0,0 +1,101 @@
class RC4 {
constructor(key) {
this.key = Buffer.isBuffer(key) ? key : Buffer.from(key, "utf8")
this.s = new Array(256)
this.init()
}
init() {
for (let i = 0; i < 256; i++) {
this.s[i] = i
}
let j = 0
for (let i = 0; i < 256; i++) {
j = (j + this.s[i] + this.key[i % this.key.length]) % 256
this.swap(i, j)
}
}
swap(i, j) {
const temp = this.s[i]
this.s[i] = this.s[j]
this.s[j] = temp
}
encrypt(data) {
const input = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf8")
const output = Buffer.alloc(input.length)
let i = 0,
j = 0
for (let k = 0; k < input.length; k++) {
i = (i + 1) % 256
j = (j + this.s[i]) % 256
this.swap(i, j)
const keystream = this.s[(this.s[i] + this.s[j]) % 256]
output[k] = input[k] ^ keystream
}
return output
}
decrypt(data) {
return this.encrypt(data)
}
}
class Crypto {
constructor(config) {
this.config = config
this.rc4 = new RC4(config.rc4Key)
this.nonce = Buffer.from(config.nonce, "utf8")
}
encrypt(data) {
if (!this.config.enabled) return data
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data)
return this.rc4.encrypt(buffer)
}
decrypt(data) {
if (!this.config.enabled) return data
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data)
return this.rc4.decrypt(buffer)
}
generateNonce() {
const timestamp = Date.now()
const random = Math.floor(Math.random() * 0xffffffff)
const nonceBuffer = Buffer.alloc(16)
nonceBuffer.writeUInt32BE(timestamp >> 32, 0)
nonceBuffer.writeUInt32BE(timestamp & 0xffffffff, 4)
nonceBuffer.writeUInt32BE(random, 8)
nonceBuffer.writeUInt32BE(random ^ timestamp, 12)
return nonceBuffer
}
hash(data) {
const crypto = require("crypto")
return crypto.createHash("sha256").update(data).digest()
}
xorWithNonce(data) {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data)
const result = Buffer.alloc(buffer.length)
for (let i = 0; i < buffer.length; i++) {
result[i] = buffer[i] ^ this.nonce[i % this.nonce.length]
}
return result
}
}
module.exports = { RC4, Crypto }

160
Utils/Logger.js Normal file
View File

@ -0,0 +1,160 @@
const fs = require("fs")
const path = require("path")
class Logger {
constructor(config = {}) {
this.config = {
level: config.level || "info",
saveToFile: config.saveToFile || true,
maxFileSize: config.maxFileSize || "10MB",
maxFiles: config.maxFiles || 5,
logDir: config.logDir || "./logs",
}
this.levels = {
debug: 0,
info: 1,
warn: 2,
error: 3,
success: 1,
}
this.colors = {
debug: "\x1b[36m", // Cyan
info: "\x1b[34m", // Blue
warn: "\x1b[33m", // Yellow
error: "\x1b[31m", // Red
success: "\x1b[32m", // Green
reset: "\x1b[0m",
}
this.initLogDirectory()
}
initLogDirectory() {
if (this.config.saveToFile && !fs.existsSync(this.config.logDir)) {
fs.mkdirSync(this.config.logDir, { recursive: true })
}
}
formatMessage(level, message, data = null) {
const timestamp = new Date().toISOString()
const upperLevel = level.toUpperCase().padEnd(7)
let logMessage = `[${timestamp}] [${upperLevel}] ${message}`
if (data) {
logMessage += ` ${JSON.stringify(data, null, 2)}`
}
return logMessage
}
shouldLog(level) {
return this.levels[level] >= this.levels[this.config.level]
}
writeToFile(message) {
if (!this.config.saveToFile) return
const logFile = path.join(this.config.logDir, `openbrawl-${new Date().toISOString().split("T")[0]}.log`)
try {
fs.appendFileSync(logFile, message + "\n")
this.rotateLogsIfNeeded(logFile)
} catch (error) {
console.error("Failed to write to log file:", error.message)
}
}
rotateLogsIfNeeded(logFile) {
try {
const stats = fs.statSync(logFile)
const maxSize = this.parseSize(this.config.maxFileSize)
if (stats.size > maxSize) {
const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
const rotatedFile = logFile.replace(".log", `-${timestamp}.log`)
fs.renameSync(logFile, rotatedFile)
this.cleanOldLogs()
}
} catch (error) {
console.error("Failed to rotate logs:", error.message)
}
}
cleanOldLogs() {
try {
const files = fs
.readdirSync(this.config.logDir)
.filter((file) => file.startsWith("openbrawl-") && file.endsWith(".log"))
.map((file) => ({
name: file,
path: path.join(this.config.logDir, file),
time: fs.statSync(path.join(this.config.logDir, file)).mtime,
}))
.sort((a, b) => b.time - a.time)
if (files.length > this.config.maxFiles) {
files.slice(this.config.maxFiles).forEach((file) => {
fs.unlinkSync(file.path)
})
}
} catch (error) {
console.error("Failed to clean old logs:", error.message)
}
}
parseSize(sizeStr) {
const units = { B: 1, KB: 1024, MB: 1024 * 1024, GB: 1024 * 1024 * 1024 }
const match = sizeStr.match(/^(\d+)(B|KB|MB|GB)$/i)
if (!match) return 10 * 1024 * 1024 // Default 10MB
return Number.parseInt(match[1]) * units[match[2].toUpperCase()]
}
log(level, message, data = null) {
if (!this.shouldLog(level)) return
const formattedMessage = this.formatMessage(level, message, data)
const coloredMessage = `${this.colors[level]}${formattedMessage}${this.colors.reset}`
console.log(coloredMessage)
this.writeToFile(formattedMessage)
}
debug(message, data = null) {
this.log("debug", message, data)
}
info(message, data = null) {
this.log("info", message, data)
}
warn(message, data = null) {
this.log("warn", message, data)
}
error(message, data = null) {
this.log("error", message, data)
}
success(message, data = null) {
this.log("success", message, data)
}
packet(direction, type, size, data = null) {
const arrow = direction === "in" ? "📨" : "📤"
const message = `${arrow} ${direction.toUpperCase()} | Type: ${type} | Size: ${size} bytes`
this.debug(message, data)
}
connection(status, details = null) {
const emoji = status === "connected" ? "✅" : status === "disconnected" ? "❌" : "🔄"
this.info(`${emoji} Connection ${status}`, details)
}
}
module.exports = Logger

86
index.js Normal file
View File

@ -0,0 +1,86 @@
const BrawlClient = require("./Client/BrawlClient")
const Logger = require("./Utils/Logger")
const config = require("./Config/config")
const asciiArt = `
`
async function main() {
console.log(asciiArt)
const logger = new Logger()
logger.info("🚀 Starting OpenBrawl Client...")
await sleep(500)
logger.info("📋 Loading configuration...")
await sleep(300)
logger.info("🔐 Initializing encryption...")
await sleep(400)
logger.info("🌐 Preparing network connection...")
await sleep(300)
logger.info("📦 Loading protocol handlers...")
await sleep(400)
logger.success("✅ All systems ready!")
await sleep(200)
const readline = require("readline")
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question("🔑 Enter password: ", (password) => {
if (password === "openproject") {
logger.success("🔓 Password correct! Starting client...")
rl.close()
const client = new BrawlClient(config, logger)
client.connect()
process.on("SIGINT", () => {
logger.info("\n👋 Shutting down client...")
client.disconnect()
process.exit(0)
})
} else {
logger.error("❌ Invalid password!")
rl.close()
process.exit(1)
}
})
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
main().catch(console.error)

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "openbrawl-client",
"version": "1.0.0",
"description": "Advanced Brawl Stars client for OpenBrawlProject with encryption and logging",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "node index.js",
"test": "node test.js"
},
"keywords": ["brawl-stars", "client", "openbrawlproject", "nodejs", "rc4", "encryption", "logging"],
"author": "OpenBrawlProject",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
},
"dependencies": {},
"devDependencies": {},
"repository": {
"type": "git",
"url": "https://github.com/OpenBrawlProject/openbrawl-client"
}
}

0
readme.md Normal file
View File