Add experimental username scheme & organize playerdata by first uuid octet

This commit is contained in:
Chipmunk 2024-11-05 23:53:18 -05:00
parent d2166651be
commit 9d41812df0
6 changed files with 65 additions and 28 deletions

59
bot.js
View file

@ -1,7 +1,10 @@
const mc = require('minecraft-protocol')
const nbt = require('prismarine-nbt')
const { EventEmitter } = require('events')
const fs = require('fs')
const path = require('path')
const fileExists = require('./util/file_exists')
const crypto = require('crypto')
const plugins = []
for (const filename of fs.readdirSync(path.resolve(__dirname, 'plugins'))) {
@ -9,6 +12,32 @@ for (const filename of fs.readdirSync(path.resolve(__dirname, 'plugins'))) {
plugins.push(require(path.resolve(__dirname, 'plugins', filename)))
}
const usernameSchemes = {
async random_playerdata (bot, options) {
const playerDataDir = await path.join(bot.paths.persistent, 'playerdata')
if (!await fileExists(playerDataDir)) return undefined
let subdir
try {
const subdirs = (await fs.promises.readdir(playerDataDir)).filter(filename => !filename.endsWith('_old'))
subdir = path.join(playerDataDir, subdirs[crypto.randomInt(0, subdirs.length)])
} catch {
return undefined
}
let data
try {
const files = await fs.promises.readdir(subdir)
const raw = await fs.promises.readFile(path.join(subdir, files[crypto.randomInt(0, files.length)]))
data = await nbt.parse(raw)
} catch {
return undefined
}
return data?.parsed?.value?.username?.value
}
}
function createBot (options = {}) {
// defaults
options.username ??= 'Bot'
@ -27,6 +56,7 @@ function createBot (options = {}) {
options['online-mode'].username ??= null
options['online-mode'].password ??= null
options.paths ??= {}
options.paths.logs ??= 'logs'
options.paths.music ??= 'music'
options.paths.images ??= 'images'
@ -68,13 +98,10 @@ function createBot (options = {}) {
bot.loggedIn = false
bot.emit('end', reason)
// auto reconnect
if (bot.autoReconnect) {
if (bot.autoReconnect && !options.client) {
setTimeout(() => {
if (bot.randomizeUsername && !bot['online-mode'].enabled) { options.username = options.username.slice(0, -2) + '\u00a7' + String.fromCharCode(Math.floor(Math.random() * 65535)) }
bot._client = mc.createClient(options)
bot.emit('set_client', bot._client)
}, 6000)
bot._initClient()
}, 6000)
}
})
@ -92,11 +119,25 @@ function createBot (options = {}) {
bot.emit('packet.' + meta.name, data);
})
})
bot._client = options.client ?? mc.createClient(options)
for (const plugin of plugins) plugin(bot, options) // Load plugins before emitting set_client so that plugins can listen for the event
async function _initClient () {
const usernameScheme = typeof options.usernameScheme === 'function' ? options.usernameScheme : usernameSchemes[options.usernameScheme]
const username = await usernameScheme?.(bot, options)
if (username) options.username = username
bot.emit('set_client', bot._client)
bot._client = mc.createClient(options)
bot.emit('set_client', bot._client)
}
bot._initClient = _initClient
for (const plugin of plugins) plugin(bot, options)
if (options.client) {
bot._client = options.client
bot.emit('set_client', bot._client)
} else {
bot._initClient()
}
function loadPlugin (plugin) {
try {

View file

@ -14,12 +14,12 @@ async function main () {
const configPath = process.argv[2] ?? 'config.json5'
if (!await fileExists(configPath)) {
await fs.copyFile(path.join(__dirname, 'default.json5'), configPath)
console.info('No config file was found, so a default one was created')
globalThis.console.info('No config file was found, so a default one was created')
}
const config = json5.parse(await fs.readFile(configPath, 'utf-8'))
// logging
const logdir = config.paths.logs ?? 'logs'
const logdir = config.paths?.logs ?? 'logs'
if (!await fileExists(logdir)) await fs.mkdir(logdir)
const rl = readline.createInterface({

14
package-lock.json generated
View file

@ -4,6 +4,7 @@
"requires": true,
"packages": {
"": {
"name": "chipmunkbot3",
"dependencies": {
"@mozilla/readability": "^0.4.1",
"@skeldjs/client": "^2.15.17",
@ -2413,9 +2414,10 @@
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
},
"node_modules/matrix-js-sdk": {
"version": "31.5.0",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.5.0.tgz",
"integrity": "sha512-d8Y/Vt6PdX8leSOQ06yoArJ1xMwCzxSb1H2GzW9mtOgXnHpeYvrAuPrYr32k5hfdUAJp0xPibSqDP+/+2kCnpg==",
"version": "31.6.1",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.6.1.tgz",
"integrity": "sha512-XDSafXV2mrOQvkyZ8i1eDwqmicjLVTkdA2iAw1QsfAZtERz4CAlp1QJULcN1X2WhBx7pMnsN8M/k98LruUqMMQ==",
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^4.6.0",
@ -5694,9 +5696,9 @@
"integrity": "sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA=="
},
"matrix-js-sdk": {
"version": "31.5.0",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.5.0.tgz",
"integrity": "sha512-d8Y/Vt6PdX8leSOQ06yoArJ1xMwCzxSb1H2GzW9mtOgXnHpeYvrAuPrYr32k5hfdUAJp0xPibSqDP+/+2kCnpg==",
"version": "31.6.1",
"resolved": "https://registry.npmjs.org/matrix-js-sdk/-/matrix-js-sdk-31.6.1.tgz",
"integrity": "sha512-XDSafXV2mrOQvkyZ8i1eDwqmicjLVTkdA2iAw1QsfAZtERz4CAlp1QJULcN1X2WhBx7pMnsN8M/k98LruUqMMQ==",
"requires": {
"@babel/runtime": "^7.12.5",
"@matrix-org/matrix-sdk-crypto-wasm": "^4.6.0",

View file

@ -3,7 +3,7 @@ function inject (bot) {
bot.kbwl.enabled = false
bot.kbwl.players = ['_ChipMC_']
bot._client.on('player_info', (packet) => {
bot.on('packet.player_info', (packet) => {
if (bot.kbwl.enabled && packet.action === 0) {
packet.data.forEach((player) => {
if (player.UUID !== bot.uuid && !bot.kbwl.players.includes(player.name)) { bot.exploits.titleKick(`@a[name="${player.name.replace(/"/, '\\"')}"]`) }

View file

@ -11,18 +11,9 @@ async function inject (bot) {
const persistentDir = bot.paths.persistent
const playerDataDir = path.join(persistentDir, 'playerdata')
if (!await fileExists(playerDataDir)) await fs.mkdir(playerDataDir)
bot.playerData = playerData
bot.loadPlayerData = loadPlayerData
if (bot.loggedIn && bot.players) {
for (const player of bot.players) {
// If we logged in while the async file operations were happening (highly unlikely but possible), load the player data for the players on the server
handlePlayerAdded(player)
}
}
bot.on('player_added', handlePlayerAdded)
async function handlePlayerAdded (player) {
@ -49,7 +40,7 @@ async function inject (bot) {
uuid = getOfflineUUID(uuid)
}
const data = new PlayerData(path.join(playerDataDir, uuid + '.dat'))
const data = new PlayerData(path.join(playerDataDir, uuid.substring(0, 2) + '/' + uuid.substring(2) + '.dat'))
await data.load()
return data
}

View file

@ -1,4 +1,5 @@
const fs = require('fs/promises')
const path = require('path')
const { createWriteStream } = require('fs')
const zlib = require('zlib')
const stream = require('stream/promises')
@ -38,6 +39,8 @@ class PersistentData {
}
async save () {
await fs.mkdir(path.dirname(this.filepath), { recursive: true })
const data = this.unparse(this.data)
if (await fileExists(this.filepath)) await fs.copyFile(this.filepath, this.filepath + '_old') // Back up our data before a save