player refactor & stuff

This commit is contained in:
Chipmunk 2024-02-16 22:59:09 -05:00
parent 9c23572db3
commit 9de7b08cee
13 changed files with 179 additions and 59767 deletions

18
bot.js
View file

@ -3,22 +3,6 @@ const { EventEmitter } = require('events')
const fs = require('fs')
const path = require('path')
function createBots (servers = ['localhost'], options = {}) {
const bots = []
servers.forEach((server) => {
const a = server.split(':')
options.host = a[0]
options.port = parseInt(a[1])
options.brand = a[2]
const bot = createBot({ ...options })
bot.getBots = () => bots
bot.on('error', console.error) // "fix"
bots.push(bot)
})
return bots
}
function createBot (options = {}) {
// defaults
options.username ??= 'Bot'
@ -138,4 +122,4 @@ function createBot (options = {}) {
return bot
}
module.exports = { createBot, createBots }
module.exports = createBot

View file

@ -18,7 +18,7 @@ function execute (bot, cmd, player, args, handler) {
case 'send':
u = args.shift()
message = args.join(' ')
bot.sendMail(player.name, bot.players[u].name ?? u, message)
bot.sendMail(player.name, u, message)
bot.core.run(`minecraft:tellraw @a[nbt=${SNBT.stringify(nbt.comp({ UUID: toNBTUUID(player.UUID) }))}] ${JSON.stringify([
{ text: 'Sent ', color: bot.colors.primary },
{ text: message, color: bot.colors.secondary },

16
default.json5 Normal file
View file

@ -0,0 +1,16 @@
{
bots: [
{
host: 'localhost',
port: 25565,
brand: 'kaboom', // TODO: Rename this
username: ' ',
prefix: "'",
colors: { primary: 'green', secondary: 'dark_green', error: 'red' },
version: '1.20.4',
randomizeUsername: true,
autoReconnect: true
}
]
}

View file

@ -1,12 +1,12 @@
const readline = require('readline')
const { createBots } = require('./bot.js')
const createBot = require('./bot.js')
const fs = require('fs')
const path = require('path')
const moment = require('moment')
const json5 = require('json5')
if (!fs.existsSync('config.json5')) {
fs.copyFileSync(path.join(__dirname, 'default.json5'), 'config.json')
fs.copyFileSync(path.join(__dirname, 'default.json5'), 'config.json5')
console.info('No config file was found, so a default one was created.')
}
const config = json5.parse(fs.readFileSync('config.json5', 'utf-8'))
@ -14,7 +14,7 @@ const config = json5.parse(fs.readFileSync('config.json5', 'utf-8'))
const logdir = 'logs'
if (!fs.existsSync(logdir)) fs.mkdirSync(logdir)
let logfile = path.join(logDir, moment().format('YYYY-MM-DD'))
let logfile = path.join(logdir, moment().format('YYYY-MM-DD'))
if (fs.existsSync(logfile)) {
const pathWithSeparator = logfile + '-'
let i = 0
@ -22,7 +22,7 @@ if (fs.existsSync(logfile)) {
logfile = pathWithSeparator + (i++)
}
}
filepath += '.log'
logfile += '.log'
fs.writeFileSync(logfile, '')
const rl = readline.createInterface({
@ -31,18 +31,12 @@ const rl = readline.createInterface({
prefix: '> '
})
const bots = createBots(servers, {
username: ' ',
prefix: "'",
colors: { primary: 'green', secondary: 'dark_green', error: 'red' },
version: '1.20.4',
randomizeUsername: true,
autoReconnect: true
// 'online-mode': { enabled: false, username: 'removed lol', password: null }
})
for (const options of config.bots) {
const bot = createBot(options)
bots.forEach((bot) => {
bot.console.filepath = filepath
bot.on('error', console.error)
bot.console.filepath = logfile
bot.console.setRl(rl)
bot.commands.loadFromDir('commands')
})
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -53,23 +53,23 @@ function bot (bot) {
const msg = message.raw
if (msg.match(/<.*§r> §r.*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players[packet.sender]
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('§r> §r')[1]
bot.emit('message', player, message)
} else if (msg.match(/<.*> .*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players[packet.sender]
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('> ')[1]
bot.emit('message', player, message)
} else if (msg.match(/.* .*§r:§r §.*/g)) {
if (packet.sender === '00000000-0000-0000-0000-000000000000') return
const player = bot.players[packet.sender]
const player = bot.players.find(player => player.uuid === packet.sender)
const message = msg.split('§r:§r ')[1].substring(2)
bot.emit('message', player, message)
} else if (msg.match(/§.*§b: \/.*/g)) {
let username = msg.split('§b: ')[0]
if (username.startsWith('§b')) { username = username.slice(2) }
const player = bot.players[username]
const player = bot.players.find(player => player.username === username)
if (player == null) return
const command = msg.split('§b: ')[1]
bot.emit('cspy', player, command)

View file

@ -22,7 +22,7 @@ function inject (bot) {
function sendFeedback (message) {
bot.core.run('minecraft:tellraw @a ' + JSON.stringify(message))
}
bot.commands.execute(message.substring(bot.prefix.length), new CommandSource({ bot, sendFeedback }))
bot.commands.execute(message.substring(bot.prefix.length), new CommandSource({ bot, player, sendFeedback }))
})
function add (command) {
@ -34,9 +34,10 @@ function inject (bot) {
if (isValid(command)) {
bot.console.warn(`Command '${command.aliases[0]}' is using the legacy command system!`)
const _execute = args => {
const _execute = (c, args) => {
try {
command.execute(bot, command.aliases[0], {}, args)
const player = c.source.player
command.execute(bot, command.aliases[0], { UUID: player?.uuid, name: player?.username }, args)
} catch (error) {
bot.core.run('minecraft:tellraw @a ' + JSON.stringify({ text: error.toString(), color: 'red' }))
}
@ -44,10 +45,10 @@ function inject (bot) {
const node = bot.commands.dispatcher.register(
literal(command.aliases[0])
.executes(context => { _execute([]); return 0 })
.executes(c => { _execute(c, []); return 0 })
.then(
argument('args', greedyString())
.executes(context => { _execute(context.getArgument('args').split(' ')); return 0 })
.executes(c => { _execute(c, c.getArgument('args').split(' ')); return 0 })
)
)
for (let i = 1; i < command.aliases.length; i++) {

View file

@ -13,7 +13,7 @@ try {
// save it every 5 minutes
setInterval(() => {
fs.writeFileSync(filepath, JSON.stringify(mail, null, 2))
fs.writeFileSync(filepath, JSON.stringify(mail))
}, 5 * 6000)
// make the bot uuid ban blacklisted players

View file

@ -1,5 +1,89 @@
const nbt = require('prismarine-nbt')
const gamemodes = ['survival', 'creative', 'adventure', 'spectator']
function inject (bot, client) {
bot
bot.players = []
client.on('player_info', packet => {
for (const player of packet.data) {
if (packet.action & 1) addPlayer(player)
if (packet.action & 2) initializeChat(player)
if (packet.action & 4) updateGamemode(player)
if (packet.action & 8) updateListed(player)
if (packet.action & 16) updateLatency(player)
if (packet.action & 32) updateDisplayName(player)
}
})
function addPlayer (player) {
bot.players.filter(_player => _player.uuid !== player.uuid) // Remove duplicates
const target = {
uuid: player.uuid,
username: player.player.name,
properties: player.player.properties
}
bot.players.push(target)
bot.emit('player_added', target)
}
function initializeChat (player) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.chatSession = player.chatSession
}
function updateGamemode (player) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.gamemode = gamemodes[player.gamemode]
}
function updateListed (player) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.listed = player.listed
}
function updateLatency (player) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.latency = player.latency
}
function updateDisplayName (player) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
target.displayName = nbt.simplify(player.displayName)
}
async function removePlayer (uuid) {
const target = bot.players.find(_player => _player.uuid === player.uuid)
if (target == null) return
// Check that the player actually left
const completions = await bot.tabComplete('scoreboard players add ')
for (const match of completions.matches) {
if (match.tooltip) return
if (match.match === target.username) {
target.listed = false
return
}
}
bot.players = bot.players.filter(player => player.uuid !== uuid)
bot.emit('player_removed', target)
}
client.on('end', () => (bot.players = []))
}
module.exports.client = inject

View file

@ -13,37 +13,31 @@ try {
// save it every 5 minutes
setInterval(() => {
fs.writeFileSync(filepath, JSON.stringify(seen, null, 2))
fs.writeFileSync(filepath, JSON.stringify(seen))
}, 5 * 6000)
// expose the data to the bot
function bot (bot) {
bot.seen = seen
}
// add players to it
function client (bot, client) {
client.on('player_info', (packet) => {
packet.data.forEach((player) => {
if (player.UUID === client.uuid) return
if (packet.action !== 0) {
const name = bot.players[player.UUID]?.name
if (seen[name] != null) seen[name].last = new Date()
return
}
bot.on('player_added', player => {
if (player.uuid === bot._client.uuid) return
seen[player.name] ??= {}
if (seen[player.name].first == null) {
seen[player.name].first = new Date()
bot.core.run('minecraft:tellraw @a ' + JSON.stringify([
{ text: 'Welcome ', color: bot.colors.primary },
{ text: player.name, color: bot.colors.secondary },
' to the server!'
]))
}
seen[player.name].last = new Date()
})
seen[player.username] ??= {}
if (seen[player.username].first == null) {
seen[player.username].first = new Date()
bot.core.run('minecraft:tellraw @a ' + JSON.stringify([
{ text: 'Welcome ', color: bot.colors.primary },
{ text: player.username, color: bot.colors.secondary },
' to the server!'
]))
}
seen[player.username].last = new Date()
})
bot.on('player_removed', player => {
if (seen[player.username] != null) seen[player.username].last = new Date()
})
}
module.exports = { bot, client }
module.exports = { bot }

24
plugins/tab_complete.js Normal file
View file

@ -0,0 +1,24 @@
function inject (bot, client) {
const transactions = {}
let transactionId = 0
function tabComplete (text) {
return new Promise(resolve => {
transactions[transactionId] = resolve
client.write('tab_complete', { transactionId, text })
transactionId = (transactionId + 1) % 256
})
}
client.on('tab_complete', packet => {
if (!transactions[packet.transactionId]) return
transactions[packet.transactionId](packet)
delete transactions[packet.transactionId]
})
bot.tabComplete = tabComplete
}
module.exports.client = inject

View file

@ -1,6 +1,11 @@
const { exceptions: { SimpleCommandExceptionType }, LiteralMessage } = require('brigadier-commands')
const COMMAND_MUST_BE_EXECUTED_BY_A_PLAYER_EXCEPTION = new SimpleCommandExceptionType(new LiteralMessage('Command must be executed by a player')) // TODO: Translations
class CommandSource {
constructor ({ bot, permissionLevel = 0, sendFeedback = () => {} } = {}) {
constructor ({ bot, player = null, permissionLevel = 0, sendFeedback = () => {} } = {}) {
this.bot = bot
this.player = player
this.permissionLevel = permissionLevel
this.sendFeedback = sendFeedback
}
@ -8,6 +13,11 @@ class CommandSource {
sendError (error) {
this.sendFeedback([{ text: '', color: 'red' }, error], false)
}
getPlayerOrThrow () {
if (this.player == null) throw COMMAND_MUST_BE_EXECUTED_BY_A_PLAYER_EXCEPTION.create()
return this.player
}
}
module.exports = CommandSource