chipmunkbot3/plugins/proxy.js

123 lines
3.5 KiB
JavaScript

const mc = require('minecraft-protocol')
const LOGIN_PACKET_NAMES = [
'login',
'difficulty',
'abilities',
'held_item_slot',
'declare_recipes',
'entity_status',
'declare_commands',
'unlock_recipes',
'position',
// 'server_data',
// 'player_info',
'initialize_world_border',
'update_time',
'spawn_position',
'game_state_change',
'set_ticking_state',
'step_tick',
'update_view_position'
]
function createLoginPacketMap () {
return new Map(LOGIN_PACKET_NAMES.map(name => [name, null]))
}
function inject (bot, options) {
if (!options.proxy?.enabled) return
options.proxy.version ??= bot._client.version
options.proxy.enforceSecureProfile ??= false
bot.proxy = {
server: mc.createServer(options.proxy),
options: options.proxy,
client: null,
loginPackets: createLoginPacketMap(),
_players: {}
}
bot.on('end', () => {
bot.proxy.loginPackets = createLoginPacketMap()
bot.proxy._players = {}
})
bot.on('packet.registry_data', packet => {
bot.proxy.options.registryCodec = packet.codec // * nmp gets the registry codec from options
})
bot.on('packet', (data, meta) => {
if (bot.proxy.client && bot.proxy.client.state === mc.states.PLAY && meta.state === mc.states.PLAY) {
// Forward packets
bot.proxy.client.write(meta.name, data)
}
// Store login-related packets
if (!bot.proxy.loginPackets.has(meta.name)) return
if (meta.name === 'entity_status' && (data.entityStatus < 24 || data.entityStatus > 28)) return // Only forward permission level entity statuses on login
if (meta.name === 'game_state_change' && data.reason !== 13) return // Only forward "Start waiting for level chunks"
bot.proxy.loginPackets.set(meta.name, data)
})
bot.proxy.server.on('playerJoin', client => {
if (bot.proxy.client) bot.proxy.client.end('Logged in from another client!')
bot.proxy.client = client
client.on('packet', (data, meta) => {
if (bot._client.state !== mc.states.PLAY || meta.state !== mc.states.PLAY) return
bot._client.write(meta.name, data)
})
client.on('position', packet => {
bot.position.x = packet.x
bot.position.y = packet.y
bot.position.z = packet.z
})
client.on('look', packet => {
bot.position.yaw = packet.yaw
bot.position.pitch = packet.pitch
})
client.on('position_look', packet => {
bot.position.x = packet.x
bot.position.y = packet.y
bot.position.z = packet.z
bot.position.yaw = packet.yaw
bot.position.pitch = packet.pitch
})
client.on('vehicle_move', packet => {
bot.position.x = packet.x
bot.position.y = packet.y
bot.position.z = packet.z
bot.position.yaw = packet.yaw
bot.position.pitch = packet.pitch
})
for (const [name, data] of bot.proxy.loginPackets) {
if (!data) continue
client.write(name, data)
}
// send player info
for (const player of Object.values(bot.proxy._players)) {console.log('writing data for player %s (%s)', player.player?.username, player.uuid)
client.write('player_info', { action: player.flags, data: [{ ...player, flags: undefined }] })
}
})
bot.on('packet.player_info', packet => {
for (const player of packet.data) {
const old = bot.proxy._players[player.uuid] ?? { flags: 0 }
const filteredPlayer = Object.fromEntries(Object.entries(player).filter(([k, v]) => v != null))
bot.proxy._players[player.uuid] = { ...old, ...filteredPlayer, flags: old.flags | packet.action }
}
})
}
module.exports = inject