dougbot/bridge/lib/mineflayer/lib/loader.js
roberts 8f616598fd Fix chat, brain stability, MariaDB reconnect, suppress warnings
- 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>
2026-03-30 12:33:17 -05:00

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
}