Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tab complete & /give, /effect improvements #635

Merged
merged 9 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"node": ">=8"
},
"dependencies": {
"change-case": "^4.1.2",
"colors": "1.4.0",
"diamond-square": "^1.2.0",
"emit-then": "^2.0.0",
Expand Down
9 changes: 6 additions & 3 deletions src/lib/plugins/blocks.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { skipMcPrefix } = require('../utils')

const Vec3 = require('vec3').Vec3

module.exports.player = function (player, serv) {
Expand Down Expand Up @@ -58,9 +60,9 @@ module.exports.server = function (serv, { version }) {
info: 'set a block at a position',
usage: '/setblock <x> <y> <z> <id> [data]',
op: true,
tab: ['blockX', 'blockY', 'blockZ', 'number'],
tab: ['blockX', 'blockY', 'blockZ', 'block', 'number'],
parse (str) {
const results = str.match(/^(~|~?-?[0-9]+) (~|~?-?[0-9]+) (~|~?-?[0-9]+) ([0-9]{1,3})(?: ([0-9]{1,3}))?/)
const results = str.match(/^(~|~?-?[0-9]+) (~|~?-?[0-9]+) (~|~?-?[0-9]+) ([\w_:0-9]+)(?: ([0-9]{1,3}))?/)
if (!results) return false
return results
},
Expand All @@ -69,7 +71,8 @@ module.exports.server = function (serv, { version }) {
if (ctx.player) res = res.map((val, i) => serv.posFromString(val, ctx.player.position[['x', 'y', 'z'][i]]))
else res = res.map((val, i) => serv.posFromString(val, new Vec3(0, 128, 0)[['x', 'y', 'z'][i]]))

const id = parseInt(params[4], 10)
const blockParam = params[4]
const id = isNaN(+blockParam) ? mcData.blocksByName[skipMcPrefix(blockParam)]?.id : +blockParam
const data = parseInt(params[5] || 0, 10)
const stateId = serv.supportFeature('theFlattening') ? (blocks[id].minStateId + data) : (id << 4 | data)

Expand Down
4 changes: 2 additions & 2 deletions src/lib/plugins/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ module.exports.server = function (serv, { version }) {

serv.commands.add({
base: 'selector',
info: 'Get array from selector',
info: 'Get entities id from selector like @a',
usage: '/selector <selector>',
op: true,
parse (str) {
Expand Down Expand Up @@ -294,7 +294,7 @@ module.exports.server = function (serv, { version }) {
if (!player && str[0] !== '@') return []
else if (player) return allowUser ? [player] : []
const match = str.match(/^@([arspe])(?:\[([^\]]+)\])?$/)
if (match[1] === 'r' && !pos) throw new UserError('Can\'t found nearest players')
if (match[1] === 'r' && !pos) throw new UserError('Can\'t find nearest players')
if (match === null) throw new UserError('Invalid selector format')
const typeConversion = {
a: 'all',
Expand Down
18 changes: 15 additions & 3 deletions src/lib/plugins/effects.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const { pascalCase } = require('change-case')
const UserError = require('../user_error')
const { skipMcPrefix } = require('../utils')

module.exports.entity = function (entity, serv) {
entity.effects = {}
for (let i = 1; i <= 23; i++) { // 23 in 1.8, 27 in 1.9
Expand Down Expand Up @@ -49,14 +53,15 @@ module.exports.entity = function (entity, serv) {
}
}

module.exports.server = function (serv) {
module.exports.server = function (serv, options) {
serv.commands.add({
base: 'effect',
info: 'Give player an effect',
usage: '/effect <player> <effect> [seconds] [amplifier] [hideParticles]',
tab: ['player', 'effect', 'number', 'number', 'boolean'],
onlyPlayer: true,
parse (str) {
return str.match(/(.+?) (\d+)(?: (\d+))?(?: (\d+))?(?: (true|false))?|.*? clear/) || false
return str.match(/(.+?) ([\d\w_]+)(?: (\d+|))?(?: (\d+))?(?: (true|false))?|.*? clear/) || false
},
action (params, ctx) {
const targets = ctx.player ? ctx.player.selectorString(params[1]) : serv.selectorString(params[1])
Expand All @@ -66,7 +71,14 @@ module.exports.server = function (serv) {
}))
} else {
targets.forEach(e => {
const effId = parseInt(params[2])
let effId = parseInt(params[2])
if (isNaN(effId)) {
const mcData = require('minecraft-data')(options.version)
const effectNamePascal = pascalCase(skipMcPrefix(params[2]))
const effect = mcData.effectsByName[effectNamePascal]
if (!effect) throw new UserError(`Unknown effect ${params[2]}}`)
effId = effect.id
}
if (e.effects[effId]) {
e.removeEffect(effId)
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/plugins/placeBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ module.exports.player = function (player, serv, { version }) {
const heldItem = player.inventory.slots[36 + player.heldItemSlot]
if (!heldItem || direction === -1 || heldItem.type === -1) return

const directionVector = block.name === 'grass' ? new Vec3(0, 0, 0) : directionToVector[direction]
const directionVector = block.boundingBox === 'empty' ? new Vec3(0, 0, 0) : directionToVector[direction]
const placedPosition = referencePosition.plus(directionVector)
if (placedPosition.equals(player.position.floored())) return
const dx = player.position.x - (placedPosition.x + 0.5)
Expand Down
39 changes: 21 additions & 18 deletions src/lib/plugins/players.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { skipMcPrefix } = require('../utils')

const UserError = require('flying-squid').UserError

module.exports.server = function (serv, { version }) {
Expand Down Expand Up @@ -101,7 +103,7 @@ module.exports.server = function (serv, { version }) {
base: 'give',
info: 'Gives an item to a player.',
usage: '/give <player> <item> [count]',
tab: ['player', 'number', 'number'],
tab: ['player', 'item', 'number'],
op: true,
parse (args, ctx) {
args = args.split(' ')
Expand All @@ -111,44 +113,45 @@ module.exports.server = function (serv, { version }) {
if (args[2] && !args[2].match(/\d/)) throw new UserError('Count must be numerical')
return {
players,
item: args[1],
item: skipMcPrefix(args[1]),
count: args[2] ? args[2] : 1
}
},
action ({ players, item, count }) {
const newItem = new Item(item, count)
const itemData = isNaN(+item) ? mcData.itemsByName[skipMcPrefix(item)] : mcData.items[+item]

players.forEach(player => {
player.inventory.slots.forEach((e, i) => {
if (!e) return
if (e.type === parseInt(newItem.type)) {
e.count += parseInt(count)
player.inventory.updateSlot(e.slot, e)
return true
}
if (!itemData) throw new UserError(`Unknown item '${item}'`)
const newItem = new Item(itemData.id, count)

if (player.inventory.slots.length === i) {
player.inventory.updateSlot(player.inventory.firstEmptyInventorySlot(), newItem)
for (const player of players) {
let slotToUpdateFound = false
for (const slot of player.inventory.slots) {
if (!slot) continue
if (slot.type === parseInt(newItem.type)) {
slot.count += parseInt(count)
player.inventory.updateSlot(slot.slot, slot)
slotToUpdateFound = true
break
}
})
}

if (player.inventory.items().length === 0) {
if (!slotToUpdateFound) {
player.inventory.updateSlot(player.inventory.firstEmptyInventorySlot(), newItem)
}
})
}
}
})

serv.commands.add({
base: 'enchant',
info: 'Enchants items holded by targets with the specified enchantment and level',
info: 'Enchants items held by targets with the specified enchantment and level',
usage: '/enchant <targets> <enchantment> [level]',
tab: ['selector', 'item_enchantment', 'number'],
op: true,
parse (args, ctx) {
args = args.split(' ')
if (args[0] === '') return false
const enchantment = mcData.enchantmentsByName[args[1]]
const enchantment = mcData.enchantmentsByName[skipMcPrefix(args[1])]
if (!enchantment) throw new UserError('No such enchantment')
if (args[2] && (parseInt(args[2]) > enchantment.maxLevel || parseInt(args[2]) < 1)) throw new UserError(`Level ${args[2]} is not supported by that enchantment`)
const players = serv.getPlayers(args[0], ctx.player)
Expand Down
1 change: 1 addition & 0 deletions src/lib/plugins/pvp.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports.server = function (serv) {
base: 'kill',
info: 'Kill entities',
usage: '/kill <selector>|<player>',
tab: ['player'],
parse (str) {
return str || false
},
Expand Down
6 changes: 4 additions & 2 deletions src/lib/plugins/sound.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const { skipMcPrefix } = require('../utils')

const Vec3 = require('vec3').Vec3

module.exports.server = function (serv) {
Expand Down Expand Up @@ -48,7 +50,7 @@ module.exports.server = function (serv) {
const results = str.match(/([^ ]+)(?: ([^ ]+))?(?: ([^ ]+))?/)
if (!results) return false
return {
sound_name: results[1],
sound_name: skipMcPrefix(results[1]),
volume: results[2] ? parseFloat(results[2]) : 1.0,
pitch: results[3] ? parseFloat(results[3]) : 1.0
}
Expand All @@ -69,7 +71,7 @@ module.exports.server = function (serv) {
const results = str.match(/([^ ]+)(?: ([^ ]+))?(?: ([^ ]+))?/)
if (!results) return false
return {
sound_name: results[1],
sound_name: skipMcPrefix(results[1]),
volume: results[2] ? parseFloat(results[2]) : 1.0,
pitch: results[3] ? parseFloat(results[3]) : 1.0
}
Expand Down
7 changes: 5 additions & 2 deletions src/lib/plugins/spawn.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const requireIndex = require('../requireindex')
const plugins = requireIndex(path.join(__dirname, '..', 'plugins'))
const UserError = require('flying-squid').UserError
const UUID = require('uuid-1345')
const { skipMcPrefix } = require('../utils')
const Vec3 = require('vec3').Vec3

module.exports.server = function (serv, options) {
Expand Down Expand Up @@ -112,10 +113,11 @@ module.exports.server = function (serv, options) {
info: 'Summon an entity',
usage: '/summon <entity_name>',
onlyPlayer: true,
tab: ['entity'],
op: true,
action (name, ctx) {
if (Object.keys(serv.entities).length > options['max-entities']) { throw new UserError('Too many mobs !') }
const entity = entitiesByName[name]
const entity = entitiesByName[skipMcPrefix(name)]
if (!entity) {
return 'No entity named ' + name
}
Expand All @@ -135,6 +137,7 @@ module.exports.server = function (serv, options) {
base: 'summonMany',
info: 'Summon many entities',
usage: '/summonMany <number> <entity_name>',
tab: ['number', 'entity'],
onlyPlayer: true,
op: true,
parse (str) {
Expand All @@ -144,7 +147,7 @@ module.exports.server = function (serv, options) {
},
action ({ number, name }, ctx) {
if (Object.keys(serv.entities).length > options['max-entities'] - number) { throw new UserError('Too many mobs !') }
const entity = entitiesByName[name]
const entity = entitiesByName[skipMcPrefix(name)]
if (!entity) {
return 'No entity named ' + name
}
Expand Down
60 changes: 44 additions & 16 deletions src/lib/plugins/tabComplete.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
module.exports.player = function (player, serv) {
const { snakeCase } = require('change-case')

module.exports.player = function (player, serv, options) {
const sendTabComplete = (matches, existingContent) => {
player._client.write('tab_complete', {
matches: matches.filter((match) => match.startsWith(existingContent))
})
}

player._client.on('tab_complete', function (data) {
// console.log(data)
const textSplit = data.text.split(' ')
if (textSplit[0].startsWith('/')) {
const cmds = []
for (const cmd in serv.commands.uniqueHash) {
const cmdFull = serv.commands.uniqueHash[cmd]
if (!player.op && cmdFull.params.op) continue
cmds.push(`/${cmd}`)
const cmdSlash = `/${cmd}`
if (!cmdSlash.startsWith(data.text) || (!player.op && cmdFull.params.op)) continue
cmds.push(cmdSlash)
}

if (serv.commands.uniqueHash[textSplit[0].slice(1)]) {
if (data.lookedAtBlock) serv.tabComplete.use(serv.commands.tab(textSplit[0].slice(1), textSplit.length - 2), data.lookedAtBlock)
else serv.tabComplete.use(serv.commands.tab(textSplit[0].slice(1), textSplit.length - 2))
serv.tabComplete.use(
serv.commands.tab(textSplit[0].slice(1), textSplit.length - 2),
data.lookedAtBlock || data.block,
textSplit[textSplit.length - 1]
)
} else {
player._client.write('tab_complete', {
matches: cmds
})
sendTabComplete(cmds, textSplit[textSplit.length - 1])
}
} else {
serv.tabComplete.use('player')
serv.tabComplete.use('player', null, textSplit[textSplit.length - 1])
}
})

serv.tabComplete = {
types: [],

use: function (id, otherData = null) {
if (id === undefined) return
player._client.write('tab_complete', {
matches: this.types[id](otherData) || this.types.player()
})
use: function (id, otherData = null, existingContent = '') {
if (id === undefined || !this.types[id]) return
const matches = this.types[id](otherData) || this.types.player()
sendTabComplete(matches, existingContent)
},
add: function (id, cb) {
this.types[id] = cb
Expand All @@ -43,6 +51,26 @@ module.exports.player = function (player, serv) {
return playerNames
})

serv.tabComplete.add('item', () => {
const mcData = require('minecraft-data')(options.version)
return mcData.itemsArray.map(item => item.name)
})

serv.tabComplete.add('block', () => {
const mcData = require('minecraft-data')(options.version)
return mcData.blocksArray.map(item => item.name)
})

serv.tabComplete.add('entity', () => {
const mcData = require('minecraft-data')(options.version)
return mcData.entitiesArray.map(item => item.name)
})

serv.tabComplete.add('effect', () => {
const mcData = require('minecraft-data')(options.version)
return mcData.effectsArray.map(item => snakeCase(item.name))
})

serv.tabComplete.add('selector', () => {
const playerNames = []
const selectors = ['@p', '@a', '@e', '@r']
Expand All @@ -52,7 +80,7 @@ module.exports.player = function (player, serv) {
})

serv.tabComplete.add('number', () => {
return ['1', '2', '3']
return ['1']
})

serv.tabComplete.add('command', () => {
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports.skipMcPrefix = (name) => typeof name === 'string' ? name.replace(/^minecraft:/, '') : name
15 changes: 5 additions & 10 deletions test/mineflayer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ squid.supportedVersions.forEach((supportedVersion, i) => {
const Item = require('prismarine-item')(supportedVersion)

describe('server with mineflayer connection ' + version.minecraftVersion, () => {
/** @type {import('mineflayer').Bot} */
let bot
/** @type {import('mineflayer').Bot} */
let bot2
let serv
let entityName
Expand Down Expand Up @@ -287,16 +289,9 @@ squid.supportedVersions.forEach((supportedVersion, i) => {
await once(bot2.inventory, 'updateSlot')
expect(bot2.inventory.slots[36].type).toEqual(1)
})
it.skip('can use tabComplete', () => { // TODO to fix
return new Promise((resolve, reject) => {
bot.tabComplete('/give', (err, data) => {
if (err) {
return reject(err)
}
expect(data[0]).toEqual('bot')
return resolve()
})
})
it.skip('can use tabComplete', async () => {
const data = await bot.tabComplete('/give ')
expect(data).toEqual(['bot', 'bot2', '@p', '@a', '@e', '@r'])
})

function waitMessagePromise (message) {
Expand Down
Loading