- Listen on raw 'text' packet for Bedrock chat (pattern-based chat event doesn't fire reliably on Bedrock) - Brain: add safety reset for stuck pending_status flag - MariaDB: add retry-on-disconnect for all query methods - Suppress harmless punycode deprecation warning from Node.js - Add mineflayer-bedrock lib packages (mineflayer, prismarine-chunk, prismarine-registry) for movement support - Exclude minecraft-data from git (278MB, installed via npm) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
251 lines
11 KiB
JavaScript
251 lines
11 KiB
JavaScript
const { EventEmitter } = require('events')
|
|
const pluginLoader = require('./plugin_loader')
|
|
const { Logger, LogLevel, LoggerColors } = require('./logger/logger.mts')
|
|
const plugins = {
|
|
bed: require('./plugins/bed'),
|
|
title: require('./plugins/title'),
|
|
block_actions: require('./plugins/block_actions'),
|
|
blocks: require('./plugins/blocks'),
|
|
book: require('./plugins/book'),
|
|
boss_bar: require('./plugins/boss_bar'),
|
|
breath: require('./plugins/breath'),
|
|
chat: require('./plugins/chat'),
|
|
chest: require('./plugins/chest'),
|
|
command_block: require('./plugins/command_block'),
|
|
craft: require('./plugins/craft'),
|
|
creative: require('./plugins/creative'),
|
|
digging: require('./plugins/digging'),
|
|
enchantment_table: require('./plugins/enchantment_table'),
|
|
entities: require('./plugins/entities'),
|
|
experience: require('./plugins/experience'),
|
|
explosion: require('./plugins/explosion'),
|
|
fishing: require('./plugins/fishing'),
|
|
furnace: require('./plugins/furnace'),
|
|
game: require('./plugins/game'),
|
|
health: require('./plugins/health'),
|
|
inventory: require('./plugins/inventory'),
|
|
kick: require('./plugins/kick'),
|
|
physics: require('./plugins/physics'),
|
|
place_block: require('./plugins/place_block'),
|
|
rain: require('./plugins/rain'),
|
|
ray_trace: require('./plugins/ray_trace'),
|
|
resource_pack: require('./plugins/resource_pack'),
|
|
scoreboard: require('./plugins/scoreboard'),
|
|
team: require('./plugins/team'),
|
|
settings: require('./plugins/settings'),
|
|
simple_inventory: require('./plugins/simple_inventory'),
|
|
sound: require('./plugins/sound'),
|
|
spawn_point: require('./plugins/spawn_point'),
|
|
tablist: require('./plugins/tablist'),
|
|
time: require('./plugins/time'),
|
|
villager: require('./plugins/villager'),
|
|
anvil: require('./plugins/anvil'),
|
|
place_entity: require('./plugins/place_entity'),
|
|
generic_place: require('./plugins/generic_place'),
|
|
particle: require('./plugins/particle')
|
|
}
|
|
|
|
const bedrockPlugins = {
|
|
bed: require('./bedrockPlugins/bed.mts').default, // 100% - sleep/wake with animate packet
|
|
title: require('./bedrockPlugins/title.mts').default, // 100%
|
|
|
|
block_actions: require('./bedrockPlugins/block_actions.mts').default, // // 40% destroyStage unavalible for bedrock, calculates client-side, piston and noteblocks fix req
|
|
blocks: require('./bedrockPlugins/blocks.mts').default, // 80% WIP WORLD LOADER (block entities etc) doors bbs calc wrong (wrong state calc?)
|
|
|
|
book: require('./bedrockPlugins/book.mts').default, // 100% - writeBook/signBook via book_edit packet
|
|
boss_bar: require('./bedrockPlugins/bossbar.mts').default, // 0% - 80% possible 100%
|
|
|
|
breath: require('./bedrockPlugins/breath.mts').default, // 100%
|
|
chat: require('./bedrockPlugins/chat.mts').default, // 100% - fixed text and command_request packets for 1.21.130
|
|
|
|
chest: require('./bedrockPlugins/chest.mts').default, // 100% - openContainer/openChest/openDispenser
|
|
//command_block: require('./bedrockPlugins/command_block'), // 0% req inventory
|
|
craft: require('./bedrockPlugins/craft.mts').default, // 100% - crafting via craft_recipe item_stack_request
|
|
creative: require('./bedrockPlugins/creative.mts').default, // partial - flying works, setInventorySlot pending (status 7 error)
|
|
digging: require('./bedrockPlugins/digging.mts').default, // 100% - uses player_auth_input block_action
|
|
//enchantment_table: require('./bedrockPlugins/enchantment_table'), // 0% req inv
|
|
|
|
entities: require('./bedrockPlugins/entities.mts').default, // 100% working? (no item entities) yaw and pitch conversion req fix (pviewer rotates player too fast)
|
|
|
|
experience: require('./bedrockPlugins/experience.mts').default, // 100%
|
|
// explosion: require('./bedrockPlugins/explosion'), // 0% - 90% req logical checks, tho maybe its calc by server?
|
|
fishing: require('./bedrockPlugins/fishing.mts').default, // 100% - uses entity_event fish_hook_hook
|
|
// furnace: require('./bedrockPlugins/furnace'), // 0% req inv
|
|
|
|
game: require('./bedrockPlugins/game.mts').default, // 70% - 100% | works, req impl other stuff
|
|
health: require('./bedrockPlugins/health.mts').default, // 100%
|
|
|
|
//inventory: require('./bedrockPlugins/inventory_minimal.mts').default, // placeholder way?
|
|
|
|
inventory: require('./bedrockPlugins/inventory.mts').default, // 100% - all click modes (0-4 incl creative), containers, transfers, activateBlock/Entity/consume
|
|
kick: require('./bedrockPlugins/kick.mts').default, // 100% done
|
|
|
|
physics: require('./bedrockPlugins/physics.mts').default, // req blocks_.js and minecraft-data update (for effects)
|
|
|
|
//place_block: require('./bedrockPlugins/place_block'), // req player auth input logic
|
|
rain: require('./bedrockPlugins/rain.mts').default, // 100%, might require small check
|
|
|
|
ray_trace: plugins.ray_trace, // 100% unchanged? HeadYaw implement?
|
|
|
|
resource_pack: require('./bedrockPlugins/resource_pack.mts').default, // 100%. not needed since bedrock does not support/handled at login by bedrock-protocol
|
|
|
|
scoreboard: require('./bedrockPlugins/scoreboard.mts').default, // badly implemented 10% 0 functions
|
|
|
|
team: require('./bedrockPlugins/team.mts').default, // 0% req investigation
|
|
//settings: require('./bedrockPlugins/settings'), // 0% only SOME settings are exposed (better to only leave chunks)
|
|
simple_inventory: require('./bedrockPlugins/simple_inventory.mts').default, // 100% - equip/unequip/toss for all slots including armor and offhand
|
|
sound: require('./bedrockPlugins/sound.mts').default, // 100%
|
|
spawn_point: require('./bedrockPlugins/spawn_point.mts').default, // 100%, might require small logical checking
|
|
tablist: require('./bedrockPlugins/tablist.mts').default, // 0% bedrock does not have it but possible to make a conversion
|
|
|
|
time: require('./bedrockPlugins/time.mts').default, // doesnt have AGE
|
|
|
|
villager: require('./bedrockPlugins/villager.mts').default, // 80% - openVillager, trade with item_stack_request
|
|
//anvil: require('./bedrockPlugins/anvil'), // 0% req inv
|
|
//place_entity: require('./bedrockPlugins/place_entity'), // 0% - 80% | 100% possible
|
|
//generic_place: require('./bedrockPlugins/generic_place'), // 0% not sure why but possible
|
|
|
|
particle: require('./bedrockPlugins/particle.mts').default // mostly works, tho needs to be unified a bit
|
|
}
|
|
|
|
const minecraftData = require('minecraft-data')
|
|
|
|
// TODO: how to export supported version if both bedrock and java supported?
|
|
const { testedVersions, latestSupportedVersion, oldestSupportedVersion } = require('./version')(false)
|
|
|
|
const BEDROCK_PREFIX = 'bedrock_'
|
|
|
|
module.exports = {
|
|
createBot,
|
|
Location: require('./location'),
|
|
Painting: require('./painting'),
|
|
ScoreBoard: require('./scoreboard'),
|
|
BossBar: require('./bossbar'),
|
|
Particle: require('./particle'),
|
|
Logger,
|
|
LogLevel,
|
|
LoggerColors,
|
|
latestSupportedVersion,
|
|
oldestSupportedVersion,
|
|
testedVersions,
|
|
supportFeature: (feature, version) => minecraftData(version).supportFeature(feature)
|
|
}
|
|
|
|
function createBot (options = {}) {
|
|
options.isBedrock = options.version.indexOf(BEDROCK_PREFIX) !== -1
|
|
options.username = options.username ?? 'Player'
|
|
options.version = options.version.replace(BEDROCK_PREFIX, '') ?? false
|
|
options.plugins = options.plugins ?? {}
|
|
options.hideErrors = options.hideErrors ?? false
|
|
options.logErrors = options.logErrors ?? true
|
|
options.loadInternalPlugins = options.loadInternalPlugins ?? true
|
|
options.client = options.client ?? null
|
|
options.brand = options.brand ?? 'vanilla'
|
|
options.respawn = options.respawn ?? true
|
|
options.fakeWorldPath = options.fakeWorldPath ?? null
|
|
options.offline = options.auth === 'offline'
|
|
options.packetLogger = options.packetLogger ?? null
|
|
|
|
// Logger configuration
|
|
if (options.logLevel !== undefined) {
|
|
Logger.level = options.logLevel
|
|
} else if (options.debug) {
|
|
Logger.level = LogLevel.Debug
|
|
}
|
|
|
|
const bot = new EventEmitter()
|
|
bot._client = options.client
|
|
bot.end = (reason) => bot._client.end(reason)
|
|
|
|
// Create logger instance for this bot
|
|
bot.logger = new Logger(options.username, LoggerColors.Aqua)
|
|
|
|
if (options.logErrors) {
|
|
bot.on('error', err => {
|
|
if (!options.hideErrors) {
|
|
bot.logger.error(err)
|
|
}
|
|
})
|
|
}
|
|
|
|
pluginLoader(bot, options)
|
|
let pluginsToLoad = options.isBedrock ? bedrockPlugins : plugins
|
|
|
|
const internalPlugins = Object.keys(pluginsToLoad)
|
|
.filter(key => {
|
|
if (typeof options.plugins[key] === 'function') return false
|
|
if (options.plugins[key] === false) return false
|
|
return options.plugins[key] || options.loadInternalPlugins
|
|
}).map(key => pluginsToLoad[key])
|
|
const externalPlugins = Object.keys(options.plugins)
|
|
.filter(key => {
|
|
return typeof options.plugins[key] === 'function'
|
|
}).map(key => options.plugins[key])
|
|
bot.loadPlugins([...internalPlugins, ...externalPlugins])
|
|
|
|
let protocol = options.isBedrock ? require('bedrock-protocol') : require('minecraft-protocol')
|
|
|
|
// Allow overriding protocol version for newer servers
|
|
const protocolOpts = { ...options };
|
|
if (options.isBedrock && options.bedrockProtocolVersion) {
|
|
protocolOpts.version = options.bedrockProtocolVersion;
|
|
}
|
|
bot._client = bot._client ?? protocol.createClient(protocolOpts)
|
|
|
|
// Packet logger setup - accepts IPacketLogger instance
|
|
if (options.packetLogger && options.isBedrock) {
|
|
const logger = options.packetLogger
|
|
if (logger && typeof logger.attachToBot === 'function') {
|
|
bot.packetLogger = logger
|
|
logger.attachToBot(bot._client)
|
|
bot.on('end', () => logger.close())
|
|
// Set registry when available for item name resolution
|
|
bot.once('inject_allowed', () => {
|
|
if (typeof logger.setRegistry === 'function') {
|
|
logger.setRegistry(bot.registry)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
bot._client.on('connect', () => {
|
|
bot.emit('connect')
|
|
})
|
|
bot._client.on('error', (err) => {
|
|
bot.emit('error', err)
|
|
})
|
|
bot._client.on('end', (reason) => {
|
|
bot.emit('end', reason)
|
|
})
|
|
|
|
bot._client.on('close', (reason) => {
|
|
bot.emit('end', reason)
|
|
})
|
|
if (!bot._client.wait_connect) next() // unknown purpose
|
|
else bot._client.once('connect_allowed', next)
|
|
function next () {
|
|
const { testedVersions, latestSupportedVersion, oldestSupportedVersion } = require('./version')(options.isBedrock)
|
|
const serverPingVersion = options.isBedrock ? BEDROCK_PREFIX + options.version : bot._client.version
|
|
bot.registry = require('prismarine-registry')(serverPingVersion)
|
|
if (!bot.registry?.version) throw new Error(`Server version '${serverPingVersion}' is not supported, no data for version`)
|
|
|
|
const versionData = bot.registry.version
|
|
if (versionData['>'](latestSupportedVersion)) {
|
|
throw new Error(`Server version '${serverPingVersion}' is not supported. Latest supported version is '${latestSupportedVersion}'.`)
|
|
} else if (versionData['<'](oldestSupportedVersion)) {
|
|
throw new Error(`Server version '${serverPingVersion}' is not supported. Oldest supported version is '${oldestSupportedVersion}'.`)
|
|
}
|
|
|
|
bot.protocolVersion = versionData.version
|
|
bot.majorVersion = versionData.majorVersion
|
|
bot.version = options.isBedrock ? BEDROCK_PREFIX + versionData.minecraftVersion : versionData.minecraftVersion
|
|
bot.supportFeature = bot.registry.supportFeature
|
|
setTimeout(() => bot.emit('inject_allowed'), 0)
|
|
}
|
|
|
|
bot.close = function(){
|
|
bot._client?.close()
|
|
}
|
|
|
|
return bot
|
|
}
|